grub-devel.gnu.org archive mirror
 help / color / mirror / Atom feed
From: Daniel Kiper <daniel.kiper@oracle.com>
To: grub-devel@gnu.org
Cc: dpsmith.dev@gmail.com, eric.snowberg@oracle.com,
	javierm@redhat.com, jonmccune@google.com,
	kanth.ghatraju@oracle.com, keng-yu.lin@hpe.com,
	konrad.wilk@oracle.com, leif.lindholm@linaro.org,
	mjg59@srcf.ucam.org,  phcoder@gmail.com,
	philip.b.tricca@intel.com, ross.philipson@oracle.com
Subject: [PATCH v3 2/8] verifiers: Framework core
Date: Wed,  3 Oct 2018 11:36:49 +0200	[thread overview]
Message-ID: <1538559415-6233-3-git-send-email-daniel.kiper@oracle.com> (raw)
In-Reply-To: <1538559415-6233-1-git-send-email-daniel.kiper@oracle.com>

From: Vladimir Serbinenko <phcoder@gmail.com>

Verifiers framework provides core file verification functionality which
can be used by various security mechanisms, e.g., UEFI secure boot, TPM,
PGP signature verification, etc.

The patch contains PGP code changes and probably they should be extracted
to separate patch for the sake of clarity.

Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
Signed-off-by: Daniel Kiper <daniel.kiper@oracle.com>
---
v3 - suggestions/fixes:
   - minor improvement and cleanups.
---
 grub-core/Makefile.core.def        |    5 +
 grub-core/commands/verify.c        |  565 +++++++++++++++++-------------------
 grub-core/commands/verify_helper.c |  197 +++++++++++++
 include/grub/file.h                |    2 +-
 include/grub/list.h                |    1 +
 include/grub/verify.h              |   65 +++++
 6 files changed, 538 insertions(+), 297 deletions(-)
 create mode 100644 grub-core/commands/verify_helper.c
 create mode 100644 include/grub/verify.h

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 9590e87..dfcc95d 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -895,6 +895,11 @@ module = {
 };
 
 module = {
+  name = verify_helper;
+  common = commands/verify_helper.c;
+};
+
+module = {
   name = hdparm;
   common = commands/hdparm.c;
   enable = pci;
diff --git a/grub-core/commands/verify.c b/grub-core/commands/verify.c
index 3616318..d5d7c0f 100644
--- a/grub-core/commands/verify.c
+++ b/grub-core/commands/verify.c
@@ -30,16 +30,10 @@
 #include <grub/env.h>
 #include <grub/kernel.h>
 #include <grub/extcmd.h>
+#include <grub/verify.h>
 
 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,22 +439,26 @@ rsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval,
   return ret;
 }
 
-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)
+struct grub_pubkey_context
 {
-  grub_size_t len;
+  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_init (struct grub_pubkey_context *ctxt, grub_file_t sig)
+{
+  grub_size_t len;
   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_uint8_t pk;
+
+  grub_memset (ctxt, 0, sizeof (*ctxt));
 
   err = read_packet_header (sig, &type, &len);
   if (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,232 @@ 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;
-
-    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);
-	}
-
-    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;
-    {
-      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 (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);
-	}
-    }
-
-    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))
-      goto fail;
-    if (grub_memcmp (hval, hash_start, sizeof (hash_start)) != 0)
-      goto fail;
-
-    grub_dprintf ("crypt", "@ %x\n", (int)grub_file_tell (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 (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");
-
-	if (gcry_mpi_scan (&mpis[i], GCRYMPI_FMT_PGP,
-			   readbuf, lb + sizeof (grub_uint16_t), 0))
-	  goto fail;
-	grub_dprintf ("crypt", "alive\n");
-      }
-
-    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 (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"));
+  ctxt->hash_context = grub_zalloc (ctxt->hash->contextsize);
+  if (!ctxt->hash_context)
     return grub_errno;
+
+  ctxt->hash->init (ctxt->hash_context);
+
+  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)
+    {
+      r = grub_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 = 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;
+      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");
+    }
+
+  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 (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_free (readbuf);
+
+  return GRUB_ERR_NONE;
+
+ fail:
+  grub_free (readbuf);
+  if (!grub_errno)
+    return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature"));
+  return grub_errno;
+}
+
+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);
+}
+
+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 +866,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;
 
   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)
-    {
-      grub_free (fsuf);
-      return NULL;
-    }
-  *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)
+  struct grub_pubkey_context *ctxt = grub_malloc (sizeof (*ctxt));
+  if (!ctxt)
     {
-      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;
+      return grub_errno;
     }
-
-  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 +935,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 +959,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 +1004,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/grub-core/commands/verify_helper.c b/grub-core/commands/verify_helper.c
new file mode 100644
index 0000000..6e60dbf
--- /dev/null
+++ b/grub-core/commands/verify_helper.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 <http://www.gnu.org/licenses/>.
+ *
+ *  Verifiers helper.
+ */
+
+#include <grub/file.h>
+#include <grub/verify.h>
+#include <grub/dl.h>
+
+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_verify_helper_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(verify_helper)
+{
+  grub_file_filter_register (GRUB_FILE_FILTER_VERIFY, grub_verify_helper_open);
+}
+
+GRUB_MOD_FINI(verify_helper)
+{
+  grub_file_filter_unregister (GRUB_FILE_FILTER_VERIFY);
+}
diff --git a/include/grub/file.h b/include/grub/file.h
index e2795d1..c55901c 100644
--- a/include/grub/file.h
+++ b/include/grub/file.h
@@ -170,7 +170,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 d170ff6..b13acb9 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 0000000..298120f
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/file.h>
+#include <grub/list.h>
+
+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));
+}
-- 
1.7.10.4



  parent reply	other threads:[~2018-10-03  9:37 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-10-03  9:36 [PATCH v3 0/8] verifiers: Framework and EFI shim lock verifier Daniel Kiper
2018-10-03  9:36 ` [PATCH v3 1/8] verifiers: File type for fine-grained signature-verification controlling Daniel Kiper
2018-10-03 17:33   ` Ross Philipson
2018-10-09 13:43     ` Daniel Kiper
2018-10-03  9:36 ` Daniel Kiper [this message]
2018-10-03 17:55   ` [PATCH v3 2/8] verifiers: Framework core Ross Philipson
2018-10-09 13:48     ` Daniel Kiper
2018-10-03  9:36 ` [PATCH v3 3/8] verifiers: Add possibility to verify kernel and modules command lines Daniel Kiper
2018-10-03  9:36 ` [PATCH v3 4/8] verifiers: Add possibility to defer verification to other verifiers Daniel Kiper
2018-10-05 16:09   ` Ross Philipson
2018-10-09 13:57     ` Daniel Kiper
2018-10-03  9:36 ` [PATCH v3 5/8] verifiers: Rename verify module to pgp module Daniel Kiper
2018-10-05 16:24   ` Ross Philipson
2018-10-09 14:11     ` Daniel Kiper
2018-10-09 14:17       ` Michel Hermier
2018-10-09 14:20         ` Michel Hermier
2018-10-09 14:44           ` Ross Philipson
2018-10-09 15:06       ` Konrad Rzeszutek Wilk
2018-10-03  9:36 ` [PATCH v3 6/8] verifiers: Add the documentation Daniel Kiper
2018-10-05 16:43   ` Ross Philipson
2018-10-09 14:26     ` Daniel Kiper
2018-10-09 14:46       ` Ross Philipson
2018-10-09 14:59   ` Ross Philipson
2018-10-03  9:36 ` [PATCH v3 7/8] dl: Add support for persistent modules Daniel Kiper
2018-10-03  9:36 ` [PATCH v3 8/8] efi: Add EFI shim lock verifier Daniel Kiper

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1538559415-6233-3-git-send-email-daniel.kiper@oracle.com \
    --to=daniel.kiper@oracle.com \
    --cc=dpsmith.dev@gmail.com \
    --cc=eric.snowberg@oracle.com \
    --cc=grub-devel@gnu.org \
    --cc=javierm@redhat.com \
    --cc=jonmccune@google.com \
    --cc=kanth.ghatraju@oracle.com \
    --cc=keng-yu.lin@hpe.com \
    --cc=konrad.wilk@oracle.com \
    --cc=leif.lindholm@linaro.org \
    --cc=mjg59@srcf.ucam.org \
    --cc=phcoder@gmail.com \
    --cc=philip.b.tricca@intel.com \
    --cc=ross.philipson@oracle.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).