nalika / rpms / grub2

Forked from rpms/grub2 2 years ago
Clone
3efed6
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
3efed6
From: Vladimir Serbinenko <phcoder@gmail.com>
3efed6
Date: Sun, 5 Feb 2017 14:25:47 +0100
3efed6
Subject: [PATCH] verifiers: Framework core
3efed6
3efed6
Verifiers framework provides core file verification functionality which
3efed6
can be used by various security mechanisms, e.g., UEFI secure boot, TPM,
3efed6
PGP signature verification, etc.
3efed6
3efed6
The patch contains PGP code changes and probably they should be extracted
3efed6
to separate patch for the sake of clarity.
3efed6
3efed6
Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
3efed6
Signed-off-by: Daniel Kiper <daniel.kiper@oracle.com>
3efed6
Reviewed-by: Ross Philipson <ross.philipson@oracle.com>
3efed6
(cherry picked from commit 75a919e334f8514b6adbc024743cfcd9b88fa394)
3efed6
Signed-off-by: Daniel Axtens <dja@axtens.net>
3efed6
---
3efed6
 grub-core/Makefile.core.def    |   5 +
3efed6
 grub-core/commands/verifiers.c | 197 ++++++++++++++
3efed6
 grub-core/commands/verify.c    | 566 ++++++++++++++++++++---------------------
3efed6
 include/grub/file.h            |   2 +-
3efed6
 include/grub/list.h            |   1 +
3efed6
 include/grub/verify.h          |  65 +++++
3efed6
 6 files changed, 539 insertions(+), 297 deletions(-)
3efed6
 create mode 100644 grub-core/commands/verifiers.c
3efed6
 create mode 100644 include/grub/verify.h
3efed6
3efed6
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
b71686
index c8a50b4fc..29c3bf6cd 100644
3efed6
--- a/grub-core/Makefile.core.def
3efed6
+++ b/grub-core/Makefile.core.def
3efed6
@@ -921,6 +921,11 @@ module = {
3efed6
   cppflags = '-I$(srcdir)/lib/posix_wrap';
3efed6
 };
3efed6
 
3efed6
+module = {
3efed6
+  name = verifiers;
3efed6
+  common = commands/verifiers.c;
3efed6
+};
3efed6
+
3efed6
 module = {
3efed6
   name = hdparm;
3efed6
   common = commands/hdparm.c;
3efed6
diff --git a/grub-core/commands/verifiers.c b/grub-core/commands/verifiers.c
3efed6
new file mode 100644
b71686
index 000000000..fde88318d
3efed6
--- /dev/null
3efed6
+++ b/grub-core/commands/verifiers.c
3efed6
@@ -0,0 +1,197 @@
3efed6
+/*
3efed6
+ *  GRUB  --  GRand Unified Bootloader
3efed6
+ *  Copyright (C) 2017  Free Software Foundation, Inc.
3efed6
+ *
3efed6
+ *  GRUB is free software: you can redistribute it and/or modify
3efed6
+ *  it under the terms of the GNU General Public License as published by
3efed6
+ *  the Free Software Foundation, either version 3 of the License, or
3efed6
+ *  (at your option) any later version.
3efed6
+ *
3efed6
+ *  GRUB is distributed in the hope that it will be useful,
3efed6
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
3efed6
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
3efed6
+ *  GNU General Public License for more details.
3efed6
+ *
3efed6
+ *  You should have received a copy of the GNU General Public License
3efed6
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
3efed6
+ *
3efed6
+ *  Verifiers helper.
3efed6
+ */
3efed6
+
3efed6
+#include <grub/file.h>
3efed6
+#include <grub/verify.h>
3efed6
+#include <grub/dl.h>
3efed6
+
3efed6
+GRUB_MOD_LICENSE ("GPLv3+");
3efed6
+
3efed6
+struct grub_file_verifier *grub_file_verifiers;
3efed6
+
3efed6
+struct grub_verified
3efed6
+{
3efed6
+  grub_file_t file;
3efed6
+  void *buf;
3efed6
+};
3efed6
+typedef struct grub_verified *grub_verified_t;
3efed6
+
3efed6
+static void
3efed6
+verified_free (grub_verified_t verified)
3efed6
+{
3efed6
+  if (verified)
3efed6
+    {
3efed6
+      grub_free (verified->buf);
3efed6
+      grub_free (verified);
3efed6
+    }
3efed6
+}
3efed6
+
3efed6
+static grub_ssize_t
3efed6
+verified_read (struct grub_file *file, char *buf, grub_size_t len)
3efed6
+{
3efed6
+  grub_verified_t verified = file->data;
3efed6
+
3efed6
+  grub_memcpy (buf, (char *) verified->buf + file->offset, len);
3efed6
+  return len;
3efed6
+}
3efed6
+
3efed6
+static grub_err_t
3efed6
+verified_close (struct grub_file *file)
3efed6
+{
3efed6
+  grub_verified_t verified = file->data;
3efed6
+
3efed6
+  grub_file_close (verified->file);
3efed6
+  verified_free (verified);
3efed6
+  file->data = 0;
3efed6
+
3efed6
+  /* Device and name are freed by parent. */
3efed6
+  file->device = 0;
3efed6
+  file->name = 0;
3efed6
+
3efed6
+  return grub_errno;
3efed6
+}
3efed6
+
3efed6
+struct grub_fs verified_fs =
3efed6
+{
3efed6
+  .name = "verified_read",
3efed6
+  .read = verified_read,
3efed6
+  .close = verified_close
3efed6
+};
3efed6
+
3efed6
+static grub_file_t
3efed6
+grub_verifiers_open (grub_file_t io, enum grub_file_type type)
3efed6
+{
3efed6
+  grub_verified_t verified = NULL;
3efed6
+  struct grub_file_verifier *ver;
3efed6
+  void *context;
3efed6
+  grub_file_t ret = 0;
3efed6
+  grub_err_t err;
3efed6
+
3efed6
+  grub_dprintf ("verify", "file: %s type: %d\n", io->name, type);
3efed6
+
3efed6
+  if ((type & GRUB_FILE_TYPE_MASK) == GRUB_FILE_TYPE_SIGNATURE
3efed6
+      || (type & GRUB_FILE_TYPE_MASK) == GRUB_FILE_TYPE_VERIFY_SIGNATURE
3efed6
+      || (type & GRUB_FILE_TYPE_SKIP_SIGNATURE))
3efed6
+    return io;
3efed6
+
3efed6
+  if (io->device->disk &&
3efed6
+      (io->device->disk->dev->id == GRUB_DISK_DEVICE_MEMDISK_ID
3efed6
+       || io->device->disk->dev->id == GRUB_DISK_DEVICE_PROCFS_ID))
3efed6
+    return io;
3efed6
+
3efed6
+  FOR_LIST_ELEMENTS(ver, grub_file_verifiers)
3efed6
+    {
3efed6
+      enum grub_verify_flags flags = 0;
3efed6
+      err = ver->init (io, type, &context, &flags);
3efed6
+      if (err)
3efed6
+	goto fail_noclose;
3efed6
+      if (!(flags & GRUB_VERIFY_FLAGS_SKIP_VERIFICATION))
3efed6
+	break;
3efed6
+    }
3efed6
+
3efed6
+  if (!ver)
3efed6
+    /* No verifiers wanted to verify. Just return underlying file. */
3efed6
+    return io;
3efed6
+
3efed6
+  ret = grub_malloc (sizeof (*ret));
3efed6
+  if (!ret)
3efed6
+    {
3efed6
+      goto fail;
3efed6
+    }
3efed6
+  *ret = *io;
3efed6
+
3efed6
+  ret->fs = &verified_fs;
3efed6
+  ret->not_easily_seekable = 0;
3efed6
+  if (ret->size >> (sizeof (grub_size_t) * GRUB_CHAR_BIT - 1))
3efed6
+    {
3efed6
+      grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
3efed6
+		  N_("big file signature isn't implemented yet"));
3efed6
+      goto fail;
3efed6
+    }
3efed6
+  verified = grub_malloc (sizeof (*verified));
3efed6
+  if (!verified)
3efed6
+    {
3efed6
+      goto fail;
3efed6
+    }
3efed6
+  verified->buf = grub_malloc (ret->size);
3efed6
+  if (!verified->buf)
3efed6
+    {
3efed6
+      goto fail;
3efed6
+    }
3efed6
+  if (grub_file_read (io, verified->buf, ret->size) != (grub_ssize_t) ret->size)
3efed6
+    {
3efed6
+      if (!grub_errno)
3efed6
+	grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
3efed6
+		    io->name);
3efed6
+      goto fail;
3efed6
+    }
3efed6
+
3efed6
+  err = ver->write (context, verified->buf, ret->size);
3efed6
+  if (err)
3efed6
+    goto fail;
3efed6
+
3efed6
+  err = ver->fini ? ver->fini (context) : GRUB_ERR_NONE;
3efed6
+  if (err)
3efed6
+    goto fail;
3efed6
+
3efed6
+  if (ver->close)
3efed6
+    ver->close (context);
3efed6
+
3efed6
+  FOR_LIST_ELEMENTS_NEXT(ver, grub_file_verifiers)
3efed6
+    {
3efed6
+      enum grub_verify_flags flags = 0;
3efed6
+      err = ver->init (io, type, &context, &flags);
3efed6
+      if (err)
3efed6
+	goto fail_noclose;
3efed6
+      if (flags & GRUB_VERIFY_FLAGS_SKIP_VERIFICATION)
3efed6
+	continue;
3efed6
+      err = ver->write (context, verified->buf, ret->size);
3efed6
+      if (err)
3efed6
+	goto fail;
3efed6
+
3efed6
+      err = ver->fini ? ver->fini (context) : GRUB_ERR_NONE;
3efed6
+      if (err)
3efed6
+	goto fail;
3efed6
+
3efed6
+      if (ver->close)
3efed6
+	ver->close (context);
3efed6
+    }
3efed6
+
3efed6
+  verified->file = io;
3efed6
+  ret->data = verified;
3efed6
+  return ret;
3efed6
+
3efed6
+ fail:
3efed6
+  ver->close (context);
3efed6
+ fail_noclose:
3efed6
+  verified_free (verified);
3efed6
+  grub_free (ret);
3efed6
+  return NULL;
3efed6
+}
3efed6
+
3efed6
+GRUB_MOD_INIT(verifiers)
3efed6
+{
3efed6
+  grub_file_filter_register (GRUB_FILE_FILTER_VERIFY, grub_verifiers_open);
3efed6
+}
3efed6
+
3efed6
+GRUB_MOD_FINI(verifiers)
3efed6
+{
3efed6
+  grub_file_filter_unregister (GRUB_FILE_FILTER_VERIFY);
3efed6
+}
3efed6
diff --git a/grub-core/commands/verify.c b/grub-core/commands/verify.c
b71686
index f0dfeceeb..29e74a640 100644
3efed6
--- a/grub-core/commands/verify.c
3efed6
+++ b/grub-core/commands/verify.c
3efed6
@@ -30,16 +30,10 @@
3efed6
 #include <grub/env.h>
3efed6
 #include <grub/kernel.h>
3efed6
 #include <grub/extcmd.h>
3efed6
+#include <grub/verify.h>
3efed6
 
3efed6
 GRUB_MOD_LICENSE ("GPLv3+");
3efed6
 
3efed6
-struct grub_verified
3efed6
-{
3efed6
-  grub_file_t file;
3efed6
-  void *buf;
3efed6
-};
3efed6
-typedef struct grub_verified *grub_verified_t;
3efed6
-
3efed6
 enum
3efed6
   {
3efed6
     OPTION_SKIP_SIG = 0
3efed6
@@ -445,23 +439,27 @@ rsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval,
3efed6
   return ret;
3efed6
 }
3efed6
 
3efed6
-static grub_err_t
3efed6
-grub_verify_signature_real (char *buf, grub_size_t size,
3efed6
-			    grub_file_t f, grub_file_t sig,
3efed6
-			    struct grub_public_key *pkey)
3efed6
+struct grub_pubkey_context
3efed6
 {
3efed6
-  grub_size_t len;
3efed6
+  grub_file_t sig;
3efed6
+  struct signature_v4_header v4;
3efed6
   grub_uint8_t v;
3efed6
+  const gcry_md_spec_t *hash;
3efed6
+  void *hash_context;
3efed6
+};
3efed6
+
3efed6
+static grub_err_t
3efed6
+grub_verify_signature_init (struct grub_pubkey_context *ctxt, grub_file_t sig)
3efed6
+{
3efed6
+  grub_size_t len;
3efed6
   grub_uint8_t h;
3efed6
   grub_uint8_t t;
3efed6
   grub_uint8_t pk;
3efed6
-  const gcry_md_spec_t *hash;
3efed6
-  struct signature_v4_header v4;
3efed6
   grub_err_t err;
3efed6
-  grub_size_t i;
3efed6
-  gcry_mpi_t mpis[10];
3efed6
   grub_uint8_t type = 0;
3efed6
 
3efed6
+  grub_memset (ctxt, 0, sizeof (*ctxt));
3efed6
+
3efed6
   err = read_packet_header (sig, &type, &len;;
3efed6
   if (err)
3efed6
     return err;
3efed6
@@ -469,18 +467,18 @@ grub_verify_signature_real (char *buf, grub_size_t size,
3efed6
   if (type != 0x2)
3efed6
     return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature"));
3efed6
 
3efed6
-  if (grub_file_read (sig, &v, sizeof (v)) != sizeof (v))
3efed6
+  if (grub_file_read (sig, &ctxt->v, sizeof (ctxt->v)) != sizeof (ctxt->v))
3efed6
     return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature"));
3efed6
 
3efed6
-  if (v != 4)
3efed6
+  if (ctxt->v != 4)
3efed6
     return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature"));
3efed6
 
3efed6
-  if (grub_file_read (sig, &v4, sizeof (v4)) != sizeof (v4))
3efed6
+  if (grub_file_read (sig, &ctxt->v4, sizeof (ctxt->v4)) != sizeof (ctxt->v4))
3efed6
     return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature"));
3efed6
 
3efed6
-  h = v4.hash;
3efed6
-  t = v4.type;
3efed6
-  pk = v4.pkeyalgo;
3efed6
+  h = ctxt->v4.hash;
3efed6
+  t = ctxt->v4.type;
3efed6
+  pk = ctxt->v4.pkeyalgo;
3efed6
   
3efed6
   if (t != 0)
3efed6
     return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature"));
3efed6
@@ -491,183 +489,233 @@ grub_verify_signature_real (char *buf, grub_size_t size,
3efed6
   if (pk >= ARRAY_SIZE (pkalgos) || pkalgos[pk].name == NULL)
3efed6
     return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature"));
3efed6
 
3efed6
-  hash = grub_crypto_lookup_md_by_name (hashes[h]);
3efed6
-  if (!hash)
3efed6
+  ctxt->hash = grub_crypto_lookup_md_by_name (hashes[h]);
3efed6
+  if (!ctxt->hash)
3efed6
     return grub_error (GRUB_ERR_BAD_SIGNATURE, "hash `%s' not loaded", hashes[h]);
3efed6
 
3efed6
   grub_dprintf ("crypt", "alive\n");
3efed6
 
3efed6
-  {
3efed6
-    void *context = NULL;
3efed6
-    unsigned char *hval;
3efed6
-    grub_ssize_t rem = grub_be_to_cpu16 (v4.hashed_sub);
3efed6
-    grub_uint32_t headlen = grub_cpu_to_be32 (rem + 6);
3efed6
-    grub_uint8_t s;
3efed6
-    grub_uint16_t unhashed_sub;
3efed6
-    grub_ssize_t r;
3efed6
-    grub_uint8_t hash_start[2];
3efed6
-    gcry_mpi_t hmpi;
3efed6
-    grub_uint64_t keyid = 0;
3efed6
-    struct grub_public_subkey *sk;
3efed6
-    grub_uint8_t *readbuf = NULL;
3efed6
+  ctxt->sig = sig;
3efed6
 
3efed6
-    context = grub_zalloc (hash->contextsize);
3efed6
-    readbuf = grub_zalloc (READBUF_SIZE);
3efed6
-    if (!context || !readbuf)
3efed6
-      goto fail;
3efed6
-
3efed6
-    hash->init (context);
3efed6
-    if (buf)
3efed6
-      hash->write (context, buf, size);
3efed6
-    else 
3efed6
-      while (1)
3efed6
-	{
3efed6
-	  r = grub_file_read (f, readbuf, READBUF_SIZE);
3efed6
-	  if (r < 0)
3efed6
-	    goto fail;
3efed6
-	  if (r == 0)
3efed6
-	    break;
3efed6
-	  hash->write (context, readbuf, r);
3efed6
-	}
3efed6
-
3efed6
-    hash->write (context, &v, sizeof (v));
3efed6
-    hash->write (context, &v4, sizeof (v4));
3efed6
-    while (rem)
3efed6
-      {
3efed6
-	r = grub_file_read (sig, readbuf,
3efed6
-			    rem < READBUF_SIZE ? rem : READBUF_SIZE);
3efed6
-	if (r < 0)
3efed6
-	  goto fail;
3efed6
-	if (r == 0)
3efed6
-	  break;
3efed6
-	hash->write (context, readbuf, r);
3efed6
-	rem -= r;
3efed6
-      }
3efed6
-    hash->write (context, &v, sizeof (v));
3efed6
-    s = 0xff;
3efed6
-    hash->write (context, &s, sizeof (s));
3efed6
-    hash->write (context, &headlen, sizeof (headlen));
3efed6
-    r = grub_file_read (sig, &unhashed_sub, sizeof (unhashed_sub));
3efed6
-    if (r != sizeof (unhashed_sub))
3efed6
-      goto fail;
3efed6
-    {
3efed6
-      grub_uint8_t *ptr;
3efed6
-      grub_uint32_t l;
3efed6
-      rem = grub_be_to_cpu16 (unhashed_sub);
3efed6
-      if (rem > READBUF_SIZE)
3efed6
-	goto fail;
3efed6
-      r = grub_file_read (sig, readbuf, rem);
3efed6
-      if (r != rem)
3efed6
-	goto fail;
3efed6
-      for (ptr = readbuf; ptr < readbuf + rem; ptr += l)
3efed6
-	{
3efed6
-	  if (*ptr < 192)
3efed6
-	    l = *ptr++;
3efed6
-	  else if (*ptr < 255)
3efed6
-	    {
3efed6
-	      if (ptr + 1 >= readbuf + rem)
3efed6
-		break;
3efed6
-	      l = (((ptr[0] & ~192) << GRUB_CHAR_BIT) | ptr[1]) + 192;
3efed6
-	      ptr += 2;
3efed6
-	    }
3efed6
-	  else
3efed6
-	    {
3efed6
-	      if (ptr + 5 >= readbuf + rem)
3efed6
-		break;
3efed6
-	      l = grub_be_to_cpu32 (grub_get_unaligned32 (ptr + 1));
3efed6
-	      ptr += 5;
3efed6
-	    }
3efed6
-	  if (*ptr == 0x10 && l >= 8)
3efed6
-	    keyid = grub_get_unaligned64 (ptr + 1);
3efed6
-	}
3efed6
-    }
3efed6
-
3efed6
-    hash->final (context);
3efed6
-
3efed6
-    grub_dprintf ("crypt", "alive\n");
3efed6
-
3efed6
-    hval = hash->read (context);
3efed6
-
3efed6
-    if (grub_file_read (sig, hash_start, sizeof (hash_start)) != sizeof (hash_start))
3efed6
-      goto fail;
3efed6
-    if (grub_memcmp (hval, hash_start, sizeof (hash_start)) != 0)
3efed6
-      goto fail;
3efed6
-
3efed6
-    grub_dprintf ("crypt", "@ %x\n", (int)grub_file_tell (sig));
3efed6
-
3efed6
-    for (i = 0; i < pkalgos[pk].nmpisig; i++)
3efed6
-      {
3efed6
-	grub_uint16_t l;
3efed6
-	grub_size_t lb;
3efed6
-	grub_dprintf ("crypt", "alive\n");
3efed6
-	if (grub_file_read (sig, &l, sizeof (l)) != sizeof (l))
3efed6
-	  goto fail;
3efed6
-	grub_dprintf ("crypt", "alive\n");
3efed6
-	lb = (grub_be_to_cpu16 (l) + 7) / 8;
3efed6
-	grub_dprintf ("crypt", "l = 0x%04x\n", grub_be_to_cpu16 (l));
3efed6
-	if (lb > READBUF_SIZE - sizeof (grub_uint16_t))
3efed6
-	  goto fail;
3efed6
-	grub_dprintf ("crypt", "alive\n");
3efed6
-	if (grub_file_read (sig, readbuf + sizeof (grub_uint16_t), lb) != (grub_ssize_t) lb)
3efed6
-	  goto fail;
3efed6
-	grub_dprintf ("crypt", "alive\n");
3efed6
-	grub_memcpy (readbuf, &l, sizeof (l));
3efed6
-	grub_dprintf ("crypt", "alive\n");
3efed6
-
3efed6
-	if (gcry_mpi_scan (&mpis[i], GCRYMPI_FMT_PGP,
3efed6
-			   readbuf, lb + sizeof (grub_uint16_t), 0))
3efed6
-	  goto fail;
3efed6
-	grub_dprintf ("crypt", "alive\n");
3efed6
-      }
3efed6
-
3efed6
-    if (pkey)
3efed6
-      sk = grub_crypto_pk_locate_subkey (keyid, pkey);
3efed6
-    else
3efed6
-      sk = grub_crypto_pk_locate_subkey_in_trustdb (keyid);
3efed6
-    if (!sk)
3efed6
-      {
3efed6
-	/* TRANSLATORS: %08x is 32-bit key id.  */
3efed6
-	grub_error (GRUB_ERR_BAD_SIGNATURE, N_("public key %08x not found"),
3efed6
-		    keyid);
3efed6
-	goto fail;
3efed6
-      }
3efed6
-
3efed6
-    if (pkalgos[pk].pad (&hmpi, hval, hash, sk))
3efed6
-      goto fail;
3efed6
-    if (!*pkalgos[pk].algo)
3efed6
-      {
3efed6
-	grub_dl_load (pkalgos[pk].module);
3efed6
-	grub_errno = GRUB_ERR_NONE;
3efed6
-      }
3efed6
-
3efed6
-    if (!*pkalgos[pk].algo)
3efed6
-      {
3efed6
-	grub_error (GRUB_ERR_BAD_SIGNATURE, N_("module `%s' isn't loaded"),
3efed6
-		    pkalgos[pk].module);
3efed6
-	goto fail;
3efed6
-      }
3efed6
-    if ((*pkalgos[pk].algo)->verify (0, hmpi, mpis, sk->mpis, 0, 0))
3efed6
-      goto fail;
3efed6
-
3efed6
-    grub_free (context);
3efed6
-    grub_free (readbuf);
3efed6
-
3efed6
-    return GRUB_ERR_NONE;
3efed6
-
3efed6
-  fail:
3efed6
-    grub_free (context);
3efed6
-    grub_free (readbuf);
3efed6
-    if (!grub_errno)
3efed6
-      return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature"));
3efed6
+  ctxt->hash_context = grub_zalloc (ctxt->hash->contextsize);
3efed6
+  if (!ctxt->hash_context)
3efed6
     return grub_errno;
3efed6
+
3efed6
+  ctxt->hash->init (ctxt->hash_context);
3efed6
+
3efed6
+  return GRUB_ERR_NONE;
3efed6
+}
3efed6
+
3efed6
+static grub_err_t
3efed6
+grub_pubkey_write (void *ctxt_, void *buf, grub_size_t size)
3efed6
+{
3efed6
+  struct grub_pubkey_context *ctxt = ctxt_;
3efed6
+  ctxt->hash->write (ctxt->hash_context, buf, size);
3efed6
+  return GRUB_ERR_NONE;
3efed6
+}
3efed6
+
3efed6
+static grub_err_t
3efed6
+grub_verify_signature_real (struct grub_pubkey_context *ctxt,
3efed6
+			    struct grub_public_key *pkey)
3efed6
+{
3efed6
+  gcry_mpi_t mpis[10];
3efed6
+  grub_uint8_t pk = ctxt->v4.pkeyalgo;
3efed6
+  grub_size_t i;
3efed6
+  grub_uint8_t *readbuf = NULL;
3efed6
+  unsigned char *hval;
3efed6
+  grub_ssize_t rem = grub_be_to_cpu16 (ctxt->v4.hashed_sub);
3efed6
+  grub_uint32_t headlen = grub_cpu_to_be32 (rem + 6);
3efed6
+  grub_uint8_t s;
3efed6
+  grub_uint16_t unhashed_sub;
3efed6
+  grub_ssize_t r;
3efed6
+  grub_uint8_t hash_start[2];
3efed6
+  gcry_mpi_t hmpi;
3efed6
+  grub_uint64_t keyid = 0;
3efed6
+  struct grub_public_subkey *sk;
3efed6
+
3efed6
+  readbuf = grub_malloc (READBUF_SIZE);
3efed6
+  if (!readbuf)
3efed6
+    goto fail;
3efed6
+
3efed6
+  ctxt->hash->write (ctxt->hash_context, &ctxt->v, sizeof (ctxt->v));
3efed6
+  ctxt->hash->write (ctxt->hash_context, &ctxt->v4, sizeof (ctxt->v4));
3efed6
+  while (rem)
3efed6
+    {
3efed6
+      r = grub_file_read (ctxt->sig, readbuf,
3efed6
+			  rem < READBUF_SIZE ? rem : READBUF_SIZE);
3efed6
+      if (r < 0)
3efed6
+	goto fail;
3efed6
+      if (r == 0)
3efed6
+	break;
3efed6
+      ctxt->hash->write (ctxt->hash_context, readbuf, r);
3efed6
+      rem -= r;
3efed6
+    }
3efed6
+  ctxt->hash->write (ctxt->hash_context, &ctxt->v, sizeof (ctxt->v));
3efed6
+  s = 0xff;
3efed6
+  ctxt->hash->write (ctxt->hash_context, &s, sizeof (s));
3efed6
+  ctxt->hash->write (ctxt->hash_context, &headlen, sizeof (headlen));
3efed6
+  r = grub_file_read (ctxt->sig, &unhashed_sub, sizeof (unhashed_sub));
3efed6
+  if (r != sizeof (unhashed_sub))
3efed6
+    goto fail;
3efed6
+  {
3efed6
+    grub_uint8_t *ptr;
3efed6
+    grub_uint32_t l;
3efed6
+    rem = grub_be_to_cpu16 (unhashed_sub);
3efed6
+    if (rem > READBUF_SIZE)
3efed6
+      goto fail;
3efed6
+    r = grub_file_read (ctxt->sig, readbuf, rem);
3efed6
+    if (r != rem)
3efed6
+      goto fail;
3efed6
+    for (ptr = readbuf; ptr < readbuf + rem; ptr += l)
3efed6
+      {
3efed6
+	if (*ptr < 192)
3efed6
+	  l = *ptr++;
3efed6
+	else if (*ptr < 255)
3efed6
+	  {
3efed6
+	    if (ptr + 1 >= readbuf + rem)
3efed6
+	      break;
3efed6
+	    l = (((ptr[0] & ~192) << GRUB_CHAR_BIT) | ptr[1]) + 192;
3efed6
+	    ptr += 2;
3efed6
+	  }
3efed6
+	else
3efed6
+	  {
3efed6
+	    if (ptr + 5 >= readbuf + rem)
3efed6
+	      break;
3efed6
+	    l = grub_be_to_cpu32 (grub_get_unaligned32 (ptr + 1));
3efed6
+	    ptr += 5;
3efed6
+	  }
3efed6
+	if (*ptr == 0x10 && l >= 8)
3efed6
+	  keyid = grub_get_unaligned64 (ptr + 1);
3efed6
+      }
3efed6
   }
3efed6
+
3efed6
+  ctxt->hash->final (ctxt->hash_context);
3efed6
+
3efed6
+  grub_dprintf ("crypt", "alive\n");
3efed6
+
3efed6
+  hval = ctxt->hash->read (ctxt->hash_context);
3efed6
+
3efed6
+  if (grub_file_read (ctxt->sig, hash_start, sizeof (hash_start)) != sizeof (hash_start))
3efed6
+    goto fail;
3efed6
+  if (grub_memcmp (hval, hash_start, sizeof (hash_start)) != 0)
3efed6
+    goto fail;
3efed6
+
3efed6
+  grub_dprintf ("crypt", "@ %x\n", (int)grub_file_tell (ctxt->sig));
3efed6
+
3efed6
+  for (i = 0; i < pkalgos[pk].nmpisig; i++)
3efed6
+    {
3efed6
+      grub_uint16_t l;
3efed6
+      grub_size_t lb;
3efed6
+      grub_dprintf ("crypt", "alive\n");
3efed6
+      if (grub_file_read (ctxt->sig, &l, sizeof (l)) != sizeof (l))
3efed6
+	goto fail;
3efed6
+      grub_dprintf ("crypt", "alive\n");
3efed6
+      lb = (grub_be_to_cpu16 (l) + 7) / 8;
3efed6
+      grub_dprintf ("crypt", "l = 0x%04x\n", grub_be_to_cpu16 (l));
3efed6
+      if (lb > READBUF_SIZE - sizeof (grub_uint16_t))
3efed6
+	goto fail;
3efed6
+      grub_dprintf ("crypt", "alive\n");
3efed6
+      if (grub_file_read (ctxt->sig, readbuf + sizeof (grub_uint16_t), lb) != (grub_ssize_t) lb)
3efed6
+	goto fail;
3efed6
+      grub_dprintf ("crypt", "alive\n");
3efed6
+      grub_memcpy (readbuf, &l, sizeof (l));
3efed6
+      grub_dprintf ("crypt", "alive\n");
3efed6
+
3efed6
+      if (gcry_mpi_scan (&mpis[i], GCRYMPI_FMT_PGP,
3efed6
+			 readbuf, lb + sizeof (grub_uint16_t), 0))
3efed6
+	goto fail;
3efed6
+      grub_dprintf ("crypt", "alive\n");
3efed6
+    }
3efed6
+
3efed6
+  if (pkey)
3efed6
+    sk = grub_crypto_pk_locate_subkey (keyid, pkey);
3efed6
+  else
3efed6
+    sk = grub_crypto_pk_locate_subkey_in_trustdb (keyid);
3efed6
+  if (!sk)
3efed6
+    {
3efed6
+      /* TRANSLATORS: %08x is 32-bit key id.  */
3efed6
+      grub_error (GRUB_ERR_BAD_SIGNATURE, N_("public key %08x not found"),
3efed6
+		  keyid);
3efed6
+      goto fail;
3efed6
+    }
3efed6
+
3efed6
+  if (pkalgos[pk].pad (&hmpi, hval, ctxt->hash, sk))
3efed6
+    goto fail;
3efed6
+  if (!*pkalgos[pk].algo)
3efed6
+    {
3efed6
+      grub_dl_load (pkalgos[pk].module);
3efed6
+      grub_errno = GRUB_ERR_NONE;
3efed6
+    }
3efed6
+
3efed6
+  if (!*pkalgos[pk].algo)
3efed6
+    {
3efed6
+      grub_error (GRUB_ERR_BAD_SIGNATURE, N_("module `%s' isn't loaded"),
3efed6
+		  pkalgos[pk].module);
3efed6
+      goto fail;
3efed6
+    }
3efed6
+  if ((*pkalgos[pk].algo)->verify (0, hmpi, mpis, sk->mpis, 0, 0))
3efed6
+    goto fail;
3efed6
+
3efed6
+  grub_free (readbuf);
3efed6
+
3efed6
+  return GRUB_ERR_NONE;
3efed6
+
3efed6
+ fail:
3efed6
+  grub_free (readbuf);
3efed6
+  if (!grub_errno)
3efed6
+    return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature"));
3efed6
+  return grub_errno;
3efed6
+}
3efed6
+
3efed6
+static void
3efed6
+grub_pubkey_close_real (struct grub_pubkey_context *ctxt)
3efed6
+{
3efed6
+  if (ctxt->sig)
3efed6
+    grub_file_close (ctxt->sig);
3efed6
+  if (ctxt->hash_context)
3efed6
+    grub_free (ctxt->hash_context);
3efed6
+}
3efed6
+
3efed6
+static void
3efed6
+grub_pubkey_close (void *ctxt)
3efed6
+{
3efed6
+  grub_pubkey_close_real (ctxt);
3efed6
+  grub_free (ctxt);
3efed6
 }
3efed6
 
3efed6
 grub_err_t
3efed6
 grub_verify_signature (grub_file_t f, grub_file_t sig,
3efed6
 		       struct grub_public_key *pkey)
3efed6
 {
3efed6
-  return grub_verify_signature_real (0, 0, f, sig, pkey);
3efed6
+  grub_err_t err;
3efed6
+  struct grub_pubkey_context ctxt;
3efed6
+  grub_uint8_t *readbuf = NULL;
3efed6
+
3efed6
+  err = grub_verify_signature_init (&ctxt, sig);
3efed6
+  if (err)
3efed6
+    return err;
3efed6
+
3efed6
+  readbuf = grub_zalloc (READBUF_SIZE);
3efed6
+  if (!readbuf)
3efed6
+    goto fail;
3efed6
+
3efed6
+  while (1)
3efed6
+    {
3efed6
+      grub_ssize_t r;
3efed6
+      r = grub_file_read (f, readbuf, READBUF_SIZE);
3efed6
+      if (r < 0)
3efed6
+	goto fail;
3efed6
+      if (r == 0)
3efed6
+	break;
3efed6
+      err = grub_pubkey_write (&ctxt, readbuf, r);
3efed6
+      if (err)
3efed6
+	return err;
3efed6
+    }
3efed6
+
3efed6
+  grub_verify_signature_real (&ctxt, pkey);
3efed6
+ fail:
3efed6
+  grub_pubkey_close_real (&ctxt);
3efed6
+  return grub_errno;
3efed6
 }
3efed6
 
3efed6
 static grub_err_t
3efed6
@@ -819,134 +867,52 @@ grub_cmd_verify_signature (grub_extcmd_context_t ctxt,
3efed6
 
3efed6
 static int sec = 0;
3efed6
 
3efed6
-static void
3efed6
-verified_free (grub_verified_t verified)
3efed6
-{
3efed6
-  if (verified)
3efed6
-    {
3efed6
-      grub_free (verified->buf);
3efed6
-      grub_free (verified);
3efed6
-    }
3efed6
-}
3efed6
-
3efed6
-static grub_ssize_t
3efed6
-verified_read (struct grub_file *file, char *buf, grub_size_t len)
3efed6
-{
3efed6
-  grub_verified_t verified = file->data;
3efed6
-
3efed6
-  grub_memcpy (buf, (char *) verified->buf + file->offset, len);
3efed6
-  return len;
3efed6
-}
3efed6
-
3efed6
 static grub_err_t
3efed6
-verified_close (struct grub_file *file)
3efed6
-{
3efed6
-  grub_verified_t verified = file->data;
3efed6
-
3efed6
-  grub_file_close (verified->file);
3efed6
-  verified_free (verified);
3efed6
-  file->data = 0;
3efed6
-
3efed6
-  /* device and name are freed by parent */
3efed6
-  file->device = 0;
3efed6
-  file->name = 0;
3efed6
-
3efed6
-  return grub_errno;
3efed6
-}
3efed6
-
3efed6
-struct grub_fs verified_fs =
3efed6
-{
3efed6
-  .name = "verified_read",
3efed6
-  .read = verified_read,
3efed6
-  .close = verified_close
3efed6
-};
3efed6
-
3efed6
-static grub_file_t
3efed6
-grub_pubkey_open (grub_file_t io, enum grub_file_type type)
3efed6
+grub_pubkey_init (grub_file_t io, enum grub_file_type type __attribute__ ((unused)),
3efed6
+		  void **context, enum grub_verify_flags *flags)
3efed6
 {
3efed6
   grub_file_t sig;
3efed6
   char *fsuf, *ptr;
3efed6
   grub_err_t err;
3efed6
-  grub_file_t ret;
3efed6
-  grub_verified_t verified;
3efed6
-
3efed6
-  if ((type & GRUB_FILE_TYPE_MASK) == GRUB_FILE_TYPE_SIGNATURE
3efed6
-      || (type & GRUB_FILE_TYPE_MASK) == GRUB_FILE_TYPE_VERIFY_SIGNATURE
3efed6
-      || (type & GRUB_FILE_TYPE_SKIP_SIGNATURE))
3efed6
-    return io;
3efed6
+  struct grub_pubkey_context *ctxt;
3efed6
 
3efed6
   if (!sec)
3efed6
-    return io;
3efed6
-  if (io->device->disk && 
3efed6
-      (io->device->disk->dev->id == GRUB_DISK_DEVICE_MEMDISK_ID
3efed6
-       || io->device->disk->dev->id == GRUB_DISK_DEVICE_PROCFS_ID))
3efed6
-    return io;
3efed6
+    {
3efed6
+      *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION;
3efed6
+      return GRUB_ERR_NONE;
3efed6
+    }
3efed6
+
3efed6
   fsuf = grub_malloc (grub_strlen (io->name) + sizeof (".sig"));
3efed6
   if (!fsuf)
3efed6
-    return NULL;
3efed6
+    return grub_errno;
3efed6
   ptr = grub_stpcpy (fsuf, io->name);
3efed6
   grub_memcpy (ptr, ".sig", sizeof (".sig"));
3efed6
 
3efed6
   sig = grub_file_open (fsuf, GRUB_FILE_TYPE_SIGNATURE);
3efed6
   grub_free (fsuf);
3efed6
   if (!sig)
3efed6
-    return NULL;
3efed6
+    return grub_errno;
3efed6
 
3efed6
-  ret = grub_malloc (sizeof (*ret));
3efed6
-  if (!ret)
3efed6
+  ctxt = grub_malloc (sizeof (*ctxt));
3efed6
+  if (!ctxt)
3efed6
     {
3efed6
       grub_file_close (sig);
3efed6
-      return NULL;
3efed6
+      return grub_errno;
3efed6
     }
3efed6
-  *ret = *io;
3efed6
-
3efed6
-  ret->fs = &verified_fs;
3efed6
-  ret->not_easily_seekable = 0;
3efed6
-  if (ret->size >> (sizeof (grub_size_t) * GRUB_CHAR_BIT - 1))
3efed6
-    {
3efed6
-      grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
3efed6
-		  "big file signature isn't implemented yet");
3efed6
-      grub_file_close (sig);
3efed6
-      grub_free (ret);
3efed6
-      return NULL;
3efed6
-    }
3efed6
-  verified = grub_malloc (sizeof (*verified));
3efed6
-  if (!verified)
3efed6
-    {
3efed6
-      grub_file_close (sig);
3efed6
-      grub_free (ret);
3efed6
-      return NULL;
3efed6
-    }
3efed6
-  verified->buf = grub_malloc (ret->size);
3efed6
-  if (!verified->buf)
3efed6
-    {
3efed6
-      grub_file_close (sig);
3efed6
-      verified_free (verified);
3efed6
-      grub_free (ret);
3efed6
-      return NULL;
3efed6
-    }
3efed6
-  if (grub_file_read (io, verified->buf, ret->size) != (grub_ssize_t) ret->size)
3efed6
-    {
3efed6
-      if (!grub_errno)
3efed6
-	grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
3efed6
-		    io->name);
3efed6
-      grub_file_close (sig);
3efed6
-      verified_free (verified);
3efed6
-      grub_free (ret);
3efed6
-      return NULL;
3efed6
-    }
3efed6
-
3efed6
-  err = grub_verify_signature_real (verified->buf, ret->size, 0, sig, NULL);
3efed6
-  grub_file_close (sig);
3efed6
+  err = grub_verify_signature_init (ctxt, sig);
3efed6
   if (err)
3efed6
     {
3efed6
-      verified_free (verified);
3efed6
-      grub_free (ret);
3efed6
-      return NULL;
3efed6
+      grub_pubkey_close (ctxt);
3efed6
+      return err;
3efed6
     }
3efed6
-  verified->file = io;
3efed6
-  ret->data = verified;
3efed6
-  return ret;
3efed6
+  *context = ctxt;
3efed6
+  return GRUB_ERR_NONE;
3efed6
+}
3efed6
+
3efed6
+static grub_err_t
3efed6
+grub_pubkey_fini (void *ctxt)
3efed6
+{
3efed6
+  return grub_verify_signature_real (ctxt, NULL);
3efed6
 }
3efed6
 
3efed6
 static char *
3efed6
@@ -970,8 +936,16 @@ struct grub_fs pseudo_fs =
3efed6
   {
3efed6
     .name = "pseudo",
3efed6
     .read = pseudo_read
3efed6
-};
3efed6
+  };
3efed6
 
3efed6
+struct grub_file_verifier grub_pubkey_verifier =
3efed6
+  {
3efed6
+    .name = "pgp",
3efed6
+    .init = grub_pubkey_init,
3efed6
+    .fini = grub_pubkey_fini,
3efed6
+    .write = grub_pubkey_write,
3efed6
+    .close = grub_pubkey_close,
3efed6
+  };
3efed6
 
3efed6
 static grub_extcmd_t cmd, cmd_trust;
3efed6
 static grub_command_t cmd_distrust, cmd_list;
3efed6
@@ -986,8 +960,6 @@ GRUB_MOD_INIT(verify)
3efed6
     sec = 1;
3efed6
   else
3efed6
     sec = 0;
3efed6
-    
3efed6
-  grub_file_filter_register (GRUB_FILE_FILTER_PUBKEY, grub_pubkey_open);
3efed6
 
3efed6
   grub_register_variable_hook ("check_signatures", 0, grub_env_write_sec);
3efed6
   grub_env_export ("check_signatures");
3efed6
@@ -1033,11 +1005,13 @@ GRUB_MOD_INIT(verify)
3efed6
   cmd_distrust = grub_register_command ("distrust", grub_cmd_distrust,
3efed6
 					N_("PUBKEY_ID"),
3efed6
 					N_("Remove PUBKEY_ID from trusted keys."));
3efed6
+
3efed6
+  grub_verifier_register (&grub_pubkey_verifier);
3efed6
 }
3efed6
 
3efed6
 GRUB_MOD_FINI(verify)
3efed6
 {
3efed6
-  grub_file_filter_unregister (GRUB_FILE_FILTER_PUBKEY);
3efed6
+  grub_verifier_unregister (&grub_pubkey_verifier);
3efed6
   grub_unregister_extcmd (cmd);
3efed6
   grub_unregister_extcmd (cmd_trust);
3efed6
   grub_unregister_command (cmd_list);
3efed6
diff --git a/include/grub/file.h b/include/grub/file.h
b71686
index 5b47c5f91..19dda67f6 100644
3efed6
--- a/include/grub/file.h
3efed6
+++ b/include/grub/file.h
3efed6
@@ -171,7 +171,7 @@ extern grub_disk_read_hook_t EXPORT_VAR(grub_file_progress_hook);
3efed6
 /* Filters with lower ID are executed first.  */
3efed6
 typedef enum grub_file_filter_id
3efed6
   {
3efed6
-    GRUB_FILE_FILTER_PUBKEY,
3efed6
+    GRUB_FILE_FILTER_VERIFY,
3efed6
     GRUB_FILE_FILTER_GZIO,
3efed6
     GRUB_FILE_FILTER_XZIO,
3efed6
     GRUB_FILE_FILTER_LZOPIO,
3efed6
diff --git a/include/grub/list.h b/include/grub/list.h
b71686
index d170ff6da..b13acb962 100644
3efed6
--- a/include/grub/list.h
3efed6
+++ b/include/grub/list.h
3efed6
@@ -35,6 +35,7 @@ void EXPORT_FUNC(grub_list_push) (grub_list_t *head, grub_list_t item);
3efed6
 void EXPORT_FUNC(grub_list_remove) (grub_list_t item);
3efed6
 
3efed6
 #define FOR_LIST_ELEMENTS(var, list) for ((var) = (list); (var); (var) = (var)->next)
3efed6
+#define FOR_LIST_ELEMENTS_NEXT(var, list) for ((var) = (var)->next; (var); (var) = (var)->next)
3efed6
 #define FOR_LIST_ELEMENTS_SAFE(var, nxt, list) for ((var) = (list), (nxt) = ((var) ? (var)->next : 0); (var); (var) = (nxt), ((nxt) = (var) ? (var)->next : 0))
3efed6
 
3efed6
 static inline void *
3efed6
diff --git a/include/grub/verify.h b/include/grub/verify.h
3efed6
new file mode 100644
b71686
index 000000000..298120f57
3efed6
--- /dev/null
3efed6
+++ b/include/grub/verify.h
3efed6
@@ -0,0 +1,65 @@
3efed6
+/*
3efed6
+ *  GRUB  --  GRand Unified Bootloader
3efed6
+ *  Copyright (C) 2017  Free Software Foundation, Inc.
3efed6
+ *
3efed6
+ *  GRUB is free software: you can redistribute it and/or modify
3efed6
+ *  it under the terms of the GNU General Public License as published by
3efed6
+ *  the Free Software Foundation, either version 3 of the License, or
3efed6
+ *  (at your option) any later version.
3efed6
+ *
3efed6
+ *  GRUB is distributed in the hope that it will be useful,
3efed6
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
3efed6
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
3efed6
+ *  GNU General Public License for more details.
3efed6
+ *
3efed6
+ *  You should have received a copy of the GNU General Public License
3efed6
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
3efed6
+ */
3efed6
+
3efed6
+#include <grub/file.h>
3efed6
+#include <grub/list.h>
3efed6
+
3efed6
+enum grub_verify_flags
3efed6
+  {
3efed6
+    GRUB_VERIFY_FLAGS_SKIP_VERIFICATION	= 1,
3efed6
+    GRUB_VERIFY_FLAGS_SINGLE_CHUNK	= 2
3efed6
+  };
3efed6
+
3efed6
+struct grub_file_verifier
3efed6
+{
3efed6
+  struct grub_file_verifier *next;
3efed6
+  struct grub_file_verifier **prev;
3efed6
+
3efed6
+  const char *name;
3efed6
+
3efed6
+  /*
3efed6
+   * Check if file needs to be verified and set up context.
3efed6
+   * init/read/fini is structured in the same way as hash interface.
3efed6
+   */
3efed6
+  grub_err_t (*init) (grub_file_t io, enum grub_file_type type,
3efed6
+		      void **context, enum grub_verify_flags *flags);
3efed6
+
3efed6
+  /*
3efed6
+   * Right now we pass the whole file in one call but it may
3efed6
+   * change in the future. If you insist on single buffer you
3efed6
+   * need to set GRUB_VERIFY_FLAGS_SINGLE_CHUNK in verify_flags.
3efed6
+   */
3efed6
+  grub_err_t (*write) (void *context, void *buf, grub_size_t size);
3efed6
+
3efed6
+  grub_err_t (*fini) (void *context);
3efed6
+  void (*close) (void *context);
3efed6
+};
3efed6
+
3efed6
+extern struct grub_file_verifier *grub_file_verifiers;
3efed6
+
3efed6
+static inline void
3efed6
+grub_verifier_register (struct grub_file_verifier *ver)
3efed6
+{
3efed6
+  grub_list_push (GRUB_AS_LIST_P (&grub_file_verifiers), GRUB_AS_LIST (ver));
3efed6
+}
3efed6
+
3efed6
+static inline void
3efed6
+grub_verifier_unregister (struct grub_file_verifier *ver)
3efed6
+{
3efed6
+  grub_list_remove (GRUB_AS_LIST (ver));
3efed6
+}