common: Fix -Wswitch warning.
[gnupg.git] / g10 / parse-packet.c
index c80b7df..dbb7af8 100644 (file)
@@ -2,6 +2,7 @@
  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
  *               2007, 2009, 2010 Free Software Foundation, Inc.
  * Copyright (C) 2014 Werner Koch
+ * Copyright (C) 2015 g10 Code GmbH
  *
  * This file is part of GnuPG.
  *
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <assert.h>
 
 #include "gpg.h"
-#include "util.h"
+#include "../common/util.h"
 #include "packet.h"
-#include "iobuf.h"
+#include "../common/iobuf.h"
 #include "filter.h"
 #include "photoid.h"
 #include "options.h"
 #include "main.h"
-#include "i18n.h"
-#include "host2net.h"
+#include "../common/i18n.h"
+#include "../common/host2net.h"
 
 
 /* Maximum length of packets to avoid excessive memory allocation.  */
@@ -48,9 +48,9 @@ static int mpi_print_mode;
 static int list_mode;
 static estream_t listfp;
 
-static int parse (IOBUF inp, PACKET * pkt, int onlykeypkts,
+static int parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts,
                  off_t * retpos, int *skip, IOBUF out, int do_skip
-#ifdef DEBUG_PARSE_PACKET
+#if DEBUG_PARSE_PACKET
                  , const char *dbg_w, const char *dbg_f, int dbg_l
 #endif
   );
@@ -74,8 +74,8 @@ static int parse_attribute (IOBUF inp, int pkttype, unsigned long pktlen,
                            PACKET * packet);
 static int parse_comment (IOBUF inp, int pkttype, unsigned long pktlen,
                          PACKET * packet);
-static void parse_trust (IOBUF inp, int pkttype, unsigned long pktlen,
-                        PACKET * packet);
+static gpg_error_t parse_ring_trust (parse_packet_ctx_t ctx,
+                                     unsigned long pktlen);
 static int parse_plaintext (IOBUF inp, int pkttype, unsigned long pktlen,
                            PACKET * packet, int new_ctb, int partial);
 static int parse_compressed (IOBUF inp, int pkttype, unsigned long pktlen,
@@ -87,6 +87,7 @@ static int parse_mdc (IOBUF inp, int pkttype, unsigned long pktlen,
 static int parse_gpg_control (IOBUF inp, int pkttype, unsigned long pktlen,
                              PACKET * packet, int partial);
 
+/* Read a 16-bit value in MSB order (big endian) from an iobuf.  */
 static unsigned short
 read_16 (IOBUF inp)
 {
@@ -97,6 +98,7 @@ read_16 (IOBUF inp)
 }
 
 
+/* Read a 32-bit value in MSB order (big endian) from an iobuf.  */
 static unsigned long
 read_32 (IOBUF inp)
 {
@@ -109,11 +111,18 @@ read_32 (IOBUF inp)
 }
 
 
-/* Read an external representation of an mpi and return the MPI.  The
- * external format is a 16 bit unsigned value stored in network byte
- * order, giving the number of bits for the following integer. The
- * integer is stored with MSB first (left padded with zero bits to align
- * on a byte boundary).  */
+/* Read an external representation of an MPI and return the MPI.  The
+   external format is a 16-bit unsigned value stored in network byte
+   order giving the number of bits for the following integer.  The
+   integer is stored MSB first and is left padded with zero bits to
+   align on a byte boundary.
+
+   The caller must set *RET_NREAD to the maximum number of bytes to
+   read from the pipeline INP.  This function sets *RET_NREAD to be
+   the number of bytes actually read from the pipeline.
+
+   If SECURE is true, the integer is stored in secure memory
+   (allocated using gcry_xmalloc_secure).  */
 static gcry_mpi_t
 mpi_read (iobuf_t inp, unsigned int *ret_nread, int secure)
 {
@@ -150,10 +159,15 @@ mpi_read (iobuf_t inp, unsigned int *ret_nread, int secure)
   p[1] = c2;
   for (i = 0; i < nbytes; i++)
     {
-      p[i + 2] = iobuf_get (inp) & 0xff;
       if (nread == nmax)
-        goto overflow;
-      nread++;
+       goto overflow;
+
+      c = iobuf_get (inp);
+      if (c == -1)
+       goto leave;
+
+      p[i + 2] = c;
+      nread ++;
     }
 
   if (gcry_mpi_scan (&a, GCRYMPI_FMT_PGP, buf, nread, &nread))
@@ -197,7 +211,7 @@ set_packet_list_mode (int mode)
      enable the list mode only with a special option. */
   if (!listfp)
     {
-      if (opt.list_packets == 2)
+      if (opt.list_packets)
         {
           listfp = es_stdout;
           if (opt.verbose)
@@ -206,13 +220,15 @@ set_packet_list_mode (int mode)
       else
         listfp = es_stderr;
 
-      if (opt.debug && DBG_MPI_VALUE)
+      if (DBG_MPI)
         mpi_print_mode = 1;
     }
   return old;
 }
 
 
+/* If OPT.VERBOSE is set, print a warning that the algorithm ALGO is
+   not suitable for signing and encryption.  */
 static void
 unknown_pubkey_warning (int algo)
 {
@@ -245,36 +261,31 @@ unknown_pubkey_warning (int algo)
 }
 
 
-/* Parse a packet and return it in packet structure.
- * Returns: 0 := valid packet in pkt
- *        -1 := no more packets
- *        >0 := error
- * Note: The function may return an error and a partly valid packet;
- * caller must free this packet.   */
-#ifdef DEBUG_PARSE_PACKET
+#if DEBUG_PARSE_PACKET
 int
-dbg_parse_packet (IOBUF inp, PACKET *pkt, const char *dbg_f, int dbg_l)
+dbg_parse_packet (parse_packet_ctx_t ctx, PACKET *pkt,
+                  const char *dbg_f, int dbg_l)
 {
   int skip, rc;
 
   do
     {
-      rc = parse (inp, pkt, 0, NULL, &skip, NULL, 0, "parse", dbg_f, dbg_l);
+      rc = parse (ctx, pkt, 0, NULL, &skip, NULL, 0, "parse", dbg_f, dbg_l);
     }
-  while (skip);
+  while (skip && ! rc);
   return rc;
 }
 #else /*!DEBUG_PARSE_PACKET*/
 int
-parse_packet (IOBUF inp, PACKET * pkt)
+parse_packet (parse_packet_ctx_t ctx, PACKET *pkt)
 {
   int skip, rc;
 
   do
     {
-      rc = parse (inp, pkt, 0, NULL, &skip, NULL, 0);
+      rc = parse (ctx, pkt, 0, NULL, &skip, NULL, 0);
     }
-  while (skip);
+  while (skip && ! rc);
   return rc;
 }
 #endif /*!DEBUG_PARSE_PACKET*/
@@ -284,33 +295,34 @@ parse_packet (IOBUF inp, PACKET * pkt)
  * Like parse packet, but only return secret or public (sub)key
  * packets.
  */
-#ifdef DEBUG_PARSE_PACKET
+#if DEBUG_PARSE_PACKET
 int
-dbg_search_packet (IOBUF inp, PACKET * pkt, off_t * retpos, int with_uid,
+dbg_search_packet (parse_packet_ctx_t ctx, PACKET *pkt,
+                   off_t * retpos, int with_uid,
                   const char *dbg_f, int dbg_l)
 {
   int skip, rc;
 
   do
     {
-      rc =
-       parse (inp, pkt, with_uid ? 2 : 1, retpos, &skip, NULL, 0, "search",
-              dbg_f, dbg_l);
+      rc = parse (ctx, pkt, with_uid ? 2 : 1, retpos, &skip, NULL, 0, "search",
+                  dbg_f, dbg_l);
     }
-  while (skip);
+  while (skip && ! rc);
   return rc;
 }
 #else /*!DEBUG_PARSE_PACKET*/
 int
-search_packet (IOBUF inp, PACKET * pkt, off_t * retpos, int with_uid)
+search_packet (parse_packet_ctx_t ctx, PACKET *pkt,
+               off_t * retpos, int with_uid)
 {
   int skip, rc;
 
   do
     {
-      rc = parse (inp, pkt, with_uid ? 2 : 1, retpos, &skip, NULL, 0);
+      rc = parse (ctx, pkt, with_uid ? 2 : 1, retpos, &skip, NULL, 0);
     }
-  while (skip);
+  while (skip && ! rc);
   return rc;
 }
 #endif /*!DEBUG_PARSE_PACKET*/
@@ -319,32 +331,53 @@ search_packet (IOBUF inp, PACKET * pkt, off_t * retpos, int with_uid)
 /*
  * Copy all packets from INP to OUT, thereby removing unused spaces.
  */
-#ifdef DEBUG_PARSE_PACKET
+#if DEBUG_PARSE_PACKET
 int
-dbg_copy_all_packets (IOBUF inp, IOBUF out, const char *dbg_f, int dbg_l)
+dbg_copy_all_packets (iobuf_t inp, iobuf_t out, const char *dbg_f, int dbg_l)
 {
   PACKET pkt;
+  struct parse_packet_ctx_s parsectx;
   int skip, rc = 0;
+
+  if (! out)
+    log_bug ("copy_all_packets: OUT may not be NULL.\n");
+
+  init_parse_packet (&parsectx, inp);
+
   do
     {
       init_packet (&pkt);
     }
   while (!
         (rc =
-         parse (inp, &pkt, 0, NULL, &skip, out, 0, "copy", dbg_f, dbg_l)));
+         parse (&parsectx, &pkt, 0, NULL, &skip, out, 0, "copy",
+                 dbg_f, dbg_l)));
+
+  deinit_parse_packet (&parsectx);
+
   return rc;
 }
 #else /*!DEBUG_PARSE_PACKET*/
 int
-copy_all_packets (IOBUF inp, IOBUF out)
+copy_all_packets (iobuf_t inp, iobuf_t out)
 {
   PACKET pkt;
+  struct parse_packet_ctx_s parsectx;
   int skip, rc = 0;
+
+  if (! out)
+    log_bug ("copy_all_packets: OUT may not be NULL.\n");
+
+  init_parse_packet (&parsectx, inp);
+
   do
     {
       init_packet (&pkt);
     }
-  while (!(rc = parse (inp, &pkt, 0, NULL, &skip, out, 0)));
+  while (!(rc = parse (&parsectx, &pkt, 0, NULL, &skip, out, 0)));
+
+  deinit_parse_packet (&parsectx);
+
   return rc;
 }
 #endif /*!DEBUG_PARSE_PACKET*/
@@ -355,36 +388,58 @@ copy_all_packets (IOBUF inp, IOBUF out)
  * Stop at offset STOPoff (i.e. don't copy packets at this or later
  * offsets)
  */
-#ifdef DEBUG_PARSE_PACKET
+#if DEBUG_PARSE_PACKET
 int
-dbg_copy_some_packets (IOBUF inp, IOBUF out, off_t stopoff,
+dbg_copy_some_packets (iobuf_t inp, iobuf_t out, off_t stopoff,
                       const char *dbg_f, int dbg_l)
 {
+  int rc = 0;
   PACKET pkt;
-  int skip, rc = 0;
+  int skip;
+  struct parse_packet_ctx_s parsectx;
+
+  init_parse_packet (&parsectx, inp);
+
   do
     {
       if (iobuf_tell (inp) >= stopoff)
-       return 0;
+        {
+          deinit_parse_packet (&parsectx);
+          return 0;
+        }
       init_packet (&pkt);
     }
-  while (!(rc = parse (inp, &pkt, 0, NULL, &skip, out, 0,
+  while (!(rc = parse (&parsectx, &pkt, 0, NULL, &skip, out, 0,
                       "some", dbg_f, dbg_l)));
+
+  deinit_parse_packet (&parsectx);
+
   return rc;
 }
 #else /*!DEBUG_PARSE_PACKET*/
 int
-copy_some_packets (IOBUF inp, IOBUF out, off_t stopoff)
+copy_some_packets (iobuf_t inp, iobuf_t out, off_t stopoff)
 {
+  int rc = 0;
   PACKET pkt;
-  int skip, rc = 0;
+  struct parse_packet_ctx_s parsectx;
+  int skip;
+
+  init_parse_packet (&parsectx, inp);
+
   do
     {
       if (iobuf_tell (inp) >= stopoff)
-       return 0;
+        {
+          deinit_parse_packet (&parsectx);
+          return 0;
+        }
       init_packet (&pkt);
     }
-  while (!(rc = parse (inp, &pkt, 0, NULL, &skip, out, 0)));
+  while (!(rc = parse (&parsectx, &pkt, 0, NULL, &skip, out, 0)));
+
+  deinit_parse_packet (&parsectx);
+
   return rc;
 }
 #endif /*!DEBUG_PARSE_PACKET*/
@@ -393,52 +448,90 @@ copy_some_packets (IOBUF inp, IOBUF out, off_t stopoff)
 /*
  * Skip over N packets
  */
-#ifdef DEBUG_PARSE_PACKET
+#if DEBUG_PARSE_PACKET
 int
-dbg_skip_some_packets (IOBUF inp, unsigned n, const char *dbg_f, int dbg_l)
+dbg_skip_some_packets (iobuf_t inp, unsigned n, const char *dbg_f, int dbg_l)
 {
-  int skip, rc = 0;
+  int rc = 0;
+  int skip;
   PACKET pkt;
+  struct parse_packet_ctx_s parsectx;
+
+  init_parse_packet (&parsectx, inp);
 
   for (; n && !rc; n--)
     {
       init_packet (&pkt);
-      rc = parse (inp, &pkt, 0, NULL, &skip, NULL, 1, "skip", dbg_f, dbg_l);
+      rc = parse (&parsectx, &pkt, 0, NULL, &skip, NULL, 1, "skip",
+                  dbg_f, dbg_l);
     }
+
+  deinit_parse_packet (&parsectx);
+
   return rc;
 }
 #else /*!DEBUG_PARSE_PACKET*/
 int
-skip_some_packets (IOBUF inp, unsigned n)
+skip_some_packets (iobuf_t inp, unsigned int n)
 {
-  int skip, rc = 0;
+  int rc = 0;
+  int skip;
   PACKET pkt;
+  struct parse_packet_ctx_s parsectx;
+
+  init_parse_packet (&parsectx, inp);
 
   for (; n && !rc; n--)
     {
       init_packet (&pkt);
-      rc = parse (inp, &pkt, 0, NULL, &skip, NULL, 1);
+      rc = parse (&parsectx, &pkt, 0, NULL, &skip, NULL, 1);
     }
+
+  deinit_parse_packet (&parsectx);
+
   return rc;
 }
 #endif /*!DEBUG_PARSE_PACKET*/
 
 
-/*
- * Parse packet.  Stores 1 at SKIP 1 if the packet should be skipped;
- * this is the case if either ONLYKEYPKTS is set and the parsed packet
- * isn't a key packet or the packet-type is 0, indicating deleted
- * stuff.  If OUT is not NULL, a special copymode is used.
- */
+/* Parse a packet and save it in *PKT.
+
+   If OUT is not NULL and the packet is valid (its type is not 0),
+   then the header, the initial length field and the packet's contents
+   are written to OUT.  In this case, the packet is not saved in *PKT.
+
+   ONLYKEYPKTS is a simple packet filter.  If ONLYKEYPKTS is set to 1,
+   then only public subkey packets, public key packets, private subkey
+   packets and private key packets are parsed.  The rest are skipped
+   (i.e., the header and the contents are read from the pipeline and
+   discarded).  If ONLYKEYPKTS is set to 2, then in addition to the
+   above 4 types of packets, user id packets are also accepted.
+
+   DO_SKIP is a more coarse grained filter.  Unless ONLYKEYPKTS is set
+   to 2 and the packet is a user id packet, all packets are skipped.
+
+   Finally, if a packet is invalid (it's type is 0), it is skipped.
+
+   If a packet is skipped and SKIP is not NULL, then *SKIP is set to
+   1.
+
+   Note: ONLYKEYPKTS and DO_SKIP are only respected if OUT is NULL,
+   i.e., the packets are not simply being copied.
+
+   If RETPOS is not NULL, then the position of CTX->INP (as returned by
+   iobuf_tell) is saved there before any data is read from CTX->INP.
+  */
 static int
-parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
+parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts, off_t * retpos,
        int *skip, IOBUF out, int do_skip
-#ifdef DEBUG_PARSE_PACKET
+#if DEBUG_PARSE_PACKET
        , const char *dbg_w, const char *dbg_f, int dbg_l
 #endif
        )
 {
-  int rc = 0, c, ctb, pkttype, lenbytes;
+  int rc = 0;
+  iobuf_t inp;
+  int c, ctb, pkttype, lenbytes;
   unsigned long pktlen;
   byte hdr[8];
   int hdrlen;
@@ -447,7 +540,10 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
   off_t pos;
 
   *skip = 0;
-  assert (!pkt->pkt.generic);
+  inp = ctx->inp;
+
+ again:
+  log_assert (!pkt->pkt.generic);
   if (retpos || list_mode)
     {
       pos = iobuf_tell (inp);
@@ -457,6 +553,8 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
   else
     pos = 0; /* (silence compiler warning) */
 
+  /* The first byte of a packet is the so-called tag.  The highest bit
+     must be set.  */
   if ((ctb = iobuf_get (inp)) == -1)
     {
       rc = -1;
@@ -464,17 +562,31 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
     }
   hdrlen = 0;
   hdr[hdrlen++] = ctb;
+
   if (!(ctb & 0x80))
     {
       log_error ("%s: invalid packet (ctb=%02x)\n", iobuf_where (inp), ctb);
       rc = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
+
+  /* Immediately following the header is the length.  There are two
+     formats: the old format and the new format.  If bit 6 (where the
+     least significant bit is bit 0) is set in the tag, then we are
+     dealing with a new format packet.  Otherwise, it is an old format
+     packet.  */
   pktlen = 0;
   new_ctb = !!(ctb & 0x40);
   if (new_ctb)
     {
+      /* Get the packet's type.  This is encoded in the 6 least
+        significant bits of the tag.  */
       pkttype = ctb & 0x3f;
+
+      /* Extract the packet's length.  New format packets have 4 ways
+        to encode the packet length.  The value of the first byte
+        determines the encoding and partially determines the length.
+        See section 4.2.2 of RFC 4880 for details.  */
       if ((c = iobuf_get (inp)) == -1)
        {
          log_error ("%s: 1st length byte missing\n", iobuf_where (inp));
@@ -501,16 +613,21 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
         }
       else if (c == 255)
         {
-          pktlen = (unsigned long)(hdr[hdrlen++] = iobuf_get_noeof (inp)) << 24;
-          pktlen |= (hdr[hdrlen++] = iobuf_get_noeof (inp)) << 16;
-          pktlen |= (hdr[hdrlen++] = iobuf_get_noeof (inp)) << 8;
-          if ((c = iobuf_get (inp)) == -1)
+         int i;
+         char value[4];
+
+         for (i = 0; i < 4; i ++)
             {
-              log_error ("%s: 4 byte length invalid\n", iobuf_where (inp));
-              rc = gpg_error (GPG_ERR_INV_PACKET);
-              goto leave;
+              if ((c = iobuf_get (inp)) == -1)
+                {
+                  log_error ("%s: 4 byte length invalid\n", iobuf_where (inp));
+                  rc = gpg_error (GPG_ERR_INV_PACKET);
+                  goto leave;
+                }
+              value[i] = hdr[hdrlen++] = c;
             }
-          pktlen |= (hdr[hdrlen++] = c);
+
+         pktlen = buf32_to_ulong (value);
         }
       else /* Partial body length.  */
         {
@@ -520,13 +637,13 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
             case PKT_ENCRYPTED:
             case PKT_ENCRYPTED_MDC:
             case PKT_COMPRESSED:
-              iobuf_set_partial_block_mode (inp, c & 0xff);
+              iobuf_set_partial_body_length_mode (inp, c & 0xff);
               pktlen = 0;      /* To indicate partial length.  */
               partial = 1;
               break;
 
             default:
-              log_error ("%s: partial length for invalid"
+              log_error ("%s: partial length invalid for"
                          " packet type %d\n", iobuf_where (inp), pkttype);
               rc = gpg_error (GPG_ERR_INV_PACKET);
               goto leave;
@@ -535,8 +652,13 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
 
     }
   else
+    /* This is an old format packet.  */
     {
+      /* Extract the packet's type.  This is encoded in bits 2-5.  */
       pkttype = (ctb >> 2) & 0xf;
+
+      /* The type of length encoding is encoded in bits 0-1 of the
+        tag.  */
       lenbytes = ((ctb & 3) == 3) ? 0 : (1 << (ctb & 3));
       if (!lenbytes)
        {
@@ -558,12 +680,26 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
          for (; lenbytes; lenbytes--)
            {
              pktlen <<= 8;
-             pktlen |= hdr[hdrlen++] = iobuf_get_noeof (inp);
+             c = iobuf_get (inp);
+             if (c == -1)
+               {
+                 log_error ("%s: length invalid\n", iobuf_where (inp));
+                 rc = gpg_error (GPG_ERR_INV_PACKET);
+                 goto leave;
+               }
+             pktlen |= hdr[hdrlen++] = c;
            }
        }
     }
 
-  if (pktlen == (unsigned long) (-1))
+  /* Sometimes the decompressing layer enters an error state in which
+     it simply outputs 0xff for every byte read.  If we have a stream
+     of 0xff bytes, then it will be detected as a new format packet
+     with type 63 and a 4-byte encoded length that is 4G-1.  Since
+     packets with type 63 are private and we use them as a control
+     packet, which won't be 4 GB, we reject such packets as
+     invalid.  */
+  if (pkttype == 63 && pktlen == 0xFFFFFFFF)
     {
       /* With some probability this is caused by a problem in the
        * the uncompressing layer - in some error cases it just loops
@@ -574,6 +710,17 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
 
   if (out && pkttype)
     {
+      /* This type of copying won't work if the packet uses a partial
+        body length.  (In other words, this only works if HDR is
+        actually the length.)  Currently, no callers require this
+        functionality so we just log this as an error.  */
+      if (partial)
+       {
+         log_error ("parse: Can't copy partial packet.  Aborting.\n");
+         rc = gpg_error (GPG_ERR_INV_PACKET);
+         goto leave;
+       }
+
       rc = iobuf_write (out, hdr, hdrlen);
       if (!rc)
        rc = copy_packet (inp, out, pkttype, pktlen, partial);
@@ -581,9 +728,13 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
     }
 
   if (with_uid && pkttype == PKT_USER_ID)
+    /* If ONLYKEYPKTS is set to 2, then we never skip user id packets,
+       even if DO_SKIP is set.  */
     ;
   else if (do_skip
+          /* type==0 is not allowed.  This is an invalid packet.  */
           || !pkttype
+          /* When ONLYKEYPKTS is set, we don't skip keys.  */
           || (onlykeypkts && pkttype != PKT_PUBLIC_SUBKEY
               && pkttype != PKT_PUBLIC_KEY
               && pkttype != PKT_SECRET_SUBKEY && pkttype != PKT_SECRET_KEY))
@@ -596,7 +747,7 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
 
   if (DBG_PACKET)
     {
-#ifdef DEBUG_PARSE_PACKET
+#if DEBUG_PARSE_PACKET
       log_debug ("parse_packet(iob=%d): type=%d length=%lu%s (%s.%s.%d)\n",
                 iobuf_id (inp), pkttype, pktlen, new_ctb ? " (new_ctb)" : "",
                 dbg_w, dbg_f, dbg_l);
@@ -610,9 +761,12 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
   if (list_mode)
     es_fprintf (listfp, "# off=%lu ctb=%02x tag=%d hlen=%d plen=%lu%s%s\n",
                 (unsigned long)pos, ctb, pkttype, hdrlen, pktlen,
-                partial? " partial":"",
+                partial? (new_ctb ? " partial" : " indeterminate") :"",
                 new_ctb? " new-ctb":"");
 
+  /* Count it.  */
+  ctx->n_parsed_packets++;
+
   pkt->pkttype = pkttype;
   rc = GPG_ERR_UNKNOWN_PACKET; /* default error */
   switch (pkttype)
@@ -650,8 +804,11 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
       rc = parse_comment (inp, pkttype, pktlen, pkt);
       break;
     case PKT_RING_TRUST:
-      parse_trust (inp, pkttype, pktlen, pkt);
-      rc = 0;
+      {
+        rc = parse_ring_trust (ctx, pktlen);
+        if (!rc)
+          goto again; /* Directly read the next packet.  */
+      }
       break;
     case PKT_PLAINTEXT:
       rc = parse_plaintext (inp, pkttype, pktlen, pkt, new_ctb, partial);
@@ -673,12 +830,24 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
       rc = parse_marker (inp, pkttype, pktlen);
       break;
     default:
+      /* Unknown packet.  Skip it.  */
       skip_packet (inp, pkttype, pktlen, partial);
       break;
     }
 
+  /* Store a shallow copy of certain packets in the context.  */
+  free_packet (NULL, ctx);
+  if (!rc && (pkttype == PKT_PUBLIC_KEY
+              || pkttype == PKT_SECRET_KEY
+              || pkttype == PKT_USER_ID
+              || pkttype == PKT_ATTRIBUTE
+              || pkttype == PKT_SIGNATURE))
+    {
+      ctx->last_pkt = *pkt;
+    }
+
  leave:
-  /* FIXME: Do we leak in case of an error?  */
+  /* FIXME: We leak in case of an error (see the xmalloc's above).  */
   if (!rc && iobuf_error (inp))
     rc = GPG_ERR_INV_KEYRING;
 
@@ -707,6 +876,20 @@ dump_hex_line (int c, int *i)
 }
 
 
+/* Copy the contents of a packet from the pipeline IN to the pipeline
+   OUT.
+
+   The header and length have already been read from INP and the
+   decoded values are given as PKGTYPE and PKTLEN.
+
+   If the packet is a partial body length packet (RFC 4880, Section
+   4.2.2.4), then iobuf_set_partial_block_modeiobuf_set_partial_block_mode
+   should already have been called on INP and PARTIAL should be set.
+
+   If PARTIAL is set or PKTLEN is 0 and PKTTYPE is PKT_COMPRESSED,
+   copy until the first EOF is encountered on INP.
+
+   Returns 0 on success and an error code if an error occurs.  */
 static int
 copy_packet (IOBUF inp, IOBUF out, int pkttype,
             unsigned long pktlen, int partial)
@@ -717,7 +900,7 @@ copy_packet (IOBUF inp, IOBUF out, int pkttype,
 
   if (partial)
     {
-      while ((n = iobuf_read (inp, buf, 100)) != -1)
+      while ((n = iobuf_read (inp, buf, sizeof (buf))) != -1)
        if ((rc = iobuf_write (out, buf, n)))
          return rc;            /* write error */
     }
@@ -725,7 +908,7 @@ copy_packet (IOBUF inp, IOBUF out, int pkttype,
     {
       log_debug ("copy_packet: compressed!\n");
       /* compressed packet, copy till EOF */
-      while ((n = iobuf_read (inp, buf, 100)) != -1)
+      while ((n = iobuf_read (inp, buf, sizeof (buf))) != -1)
        if ((rc = iobuf_write (out, buf, n)))
          return rc;            /* write error */
     }
@@ -733,7 +916,7 @@ copy_packet (IOBUF inp, IOBUF out, int pkttype,
     {
       for (; pktlen; pktlen -= n)
        {
-         n = pktlen > 100 ? 100 : pktlen;
+         n = pktlen > sizeof (buf) ? sizeof (buf) : pktlen;
          n = iobuf_read (inp, buf, n);
          if (n == -1)
            return gpg_error (GPG_ERR_EOF);
@@ -745,6 +928,9 @@ copy_packet (IOBUF inp, IOBUF out, int pkttype,
 }
 
 
+/* Skip an unknown packet.  PKTTYPE is the packet's type, PKTLEN is
+   the length of the packet's content and PARTIAL is whether partial
+   body length encoding in used (in this case PKTLEN is ignored).  */
 static void
 skip_packet (IOBUF inp, int pkttype, unsigned long pktlen, int partial)
 {
@@ -779,8 +965,9 @@ skip_packet (IOBUF inp, int pkttype, unsigned long pktlen, int partial)
 
 
 /* Read PKTLEN bytes form INP and return them in a newly allocated
-   buffer.  In case of an error NULL is returned and a error messages
-   printed.  */
+   buffer.  In case of an error (including reading fewer than PKTLEN
+   bytes from INP before EOF is returned), NULL is returned and an
+   error message is logged.  */
 static void *
 read_rest (IOBUF inp, size_t pktlen)
 {
@@ -1000,7 +1187,7 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
        log_info (_("WARNING: potentially insecure symmetrically"
                    " encrypted session key\n"));
     }
-  assert (!pktlen);
+  log_assert (!pktlen);
 
   if (list_mode)
     {
@@ -1111,6 +1298,12 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
 }
 
 
+/* Dump a subpacket to LISTFP.  BUFFER contains the subpacket in
+   question and points to the type field in the subpacket header (not
+   the start of the header).  TYPE is the subpacket's type with the
+   critical bit cleared.  CRITICAL is the value of the CRITICAL bit.
+   BUFLEN is the length of the buffer and LENGTH is the length of the
+   subpacket according to the subpacket's header.  */
 static void
 dump_sig_subpkt (int hashed, int type, int critical,
                 const byte * buffer, size_t buflen, size_t length)
@@ -1216,6 +1409,19 @@ dump_sig_subpkt (int hashed, int type, int critical,
                     (ulong) buf32_to_u32 (buffer),
                     (ulong) buf32_to_u32 (buffer + 4));
       break;
+    case SIGSUBPKT_ISSUER_FPR:
+      if (length >= 21)
+        {
+          char *tmp;
+          es_fprintf (listfp, "issuer fpr v%d ", buffer[0]);
+          tmp = bin2hex (buffer+1, length-1, NULL);
+          if (tmp)
+            {
+              es_fputs (tmp, listfp);
+              xfree (tmp);
+            }
+        }
+      break;
     case SIGSUBPKT_NOTATION:
       {
        es_fputs ("notation: ", listfp);
@@ -1255,12 +1461,12 @@ dump_sig_subpkt (int hashed, int type, int critical,
        es_fprintf (listfp, " %d", buffer[i]);
       break;
     case SIGSUBPKT_KS_FLAGS:
-      es_fputs ("key server preferences:", listfp);
+      es_fputs ("keyserver preferences:", listfp);
       for (i = 0; i < length; i++)
        es_fprintf (listfp, " %02X", buffer[i]);
       break;
     case SIGSUBPKT_PREF_KS:
-      es_fputs ("preferred key server: ", listfp);
+      es_fputs ("preferred keyserver: ", listfp);
       es_write_sanitized (listfp, buffer, length, ")", NULL);
       break;
     case SIGSUBPKT_PRIMARY_UID:
@@ -1366,6 +1572,10 @@ parse_one_sig_subpkt (const byte * buffer, size_t n, int type)
       if (n < 8)
        break;
       return 0;
+    case SIGSUBPKT_ISSUER_FPR: /* issuer key ID */
+      if (n < 21)
+       break;
+      return 0;
     case SIGSUBPKT_NOTATION:
       /* minimum length needed, and the subpacket must be well-formed
          where the name length and value length all fit inside the
@@ -1424,6 +1634,7 @@ can_handle_critical (const byte * buffer, size_t n, int type)
     case SIGSUBPKT_REVOCABLE:
     case SIGSUBPKT_REV_KEY:
     case SIGSUBPKT_ISSUER:     /* issuer key ID */
+    case SIGSUBPKT_ISSUER_FPR: /* issuer fingerprint */
     case SIGSUBPKT_PREF_SYM:
     case SIGSUBPKT_PREF_HASH:
     case SIGSUBPKT_PREF_COMPR:
@@ -1435,6 +1646,7 @@ can_handle_critical (const byte * buffer, size_t n, int type)
       /* Is it enough to show the policy or keyserver? */
     case SIGSUBPKT_POLICY:
     case SIGSUBPKT_PREF_KS:
+    case SIGSUBPKT_REVOC_REASON: /* At least we know about it.  */
       return 1;
 
     default:
@@ -1547,7 +1759,11 @@ enum_sig_subpkt (const subpktarea_t * pktbuf, sigsubpkttype_t reqtype,
       buflen -= n;
     }
   if (reqtype == SIGSUBPKT_TEST_CRITICAL)
-    return buffer;  /* Used as True to indicate that there is no. */
+    /* Returning NULL means we found a subpacket with the critical bit
+       set that we don't grok.  We've iterated over all the subpackets
+       and haven't found such a packet so we need to return a non-NULL
+       value.  */
+    return buffer;
 
   /* Critical bit we don't understand. */
   if (start)
@@ -1572,14 +1788,13 @@ parse_sig_subpkt (const subpktarea_t * buffer, sigsubpkttype_t reqtype,
 
 
 const byte *
-parse_sig_subpkt2 (PKT_signature * sig, sigsubpkttype_t reqtype,
-                  size_t * ret_n)
+parse_sig_subpkt2 (PKT_signature * sig, sigsubpkttype_t reqtype)
 {
   const byte *p;
 
-  p = parse_sig_subpkt (sig->hashed, reqtype, ret_n);
+  p = parse_sig_subpkt (sig->hashed, reqtype, NULL);
   if (!p)
-    p = parse_sig_subpkt (sig->unhashed, reqtype, ret_n);
+    p = parse_sig_subpkt (sig->unhashed, reqtype, NULL);
   return p;
 }
 
@@ -1588,25 +1803,31 @@ parse_sig_subpkt2 (PKT_signature * sig, sigsubpkttype_t reqtype,
 void
 parse_revkeys (PKT_signature * sig)
 {
-  struct revocation_key *revkey;
+  const byte *revkey;
   int seq = 0;
   size_t len;
 
   if (sig->sig_class != 0x1F)
     return;
 
-  while ((revkey =
-         (struct revocation_key *) enum_sig_subpkt (sig->hashed,
-                                                    SIGSUBPKT_REV_KEY,
-                                                    &len, &seq, NULL)))
+  while ((revkey = enum_sig_subpkt (sig->hashed, SIGSUBPKT_REV_KEY,
+                                   &len, &seq, NULL)))
     {
-      if (len == sizeof (struct revocation_key)
-          && (revkey->class & 0x80))  /* 0x80 bit must be set.  */
+      if (/* The only valid length is 22 bytes.  See RFC 4880
+            5.2.3.15.  */
+         len == 22
+         /* 0x80 bit must be set on the class.  */
+          && (revkey[0] & 0x80))
        {
          sig->revkey = xrealloc (sig->revkey,
-                                 sizeof (struct revocation_key *) *
+                                 sizeof (struct revocation_key) *
                                  (sig->numrevkeys + 1));
-         sig->revkey[sig->numrevkeys] = revkey;
+
+         /* Copy the individual fields.  */
+         sig->revkey[sig->numrevkeys].class = revkey[0];
+         sig->revkey[sig->numrevkeys].algid = revkey[1];
+         memcpy (sig->revkey[sig->numrevkeys].fpr, &revkey[2], 20);
+
          sig->numrevkeys++;
        }
     }
@@ -1646,13 +1867,19 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
 
   if (!is_v4)
     {
+      if (pktlen == 0)
+       goto underflow;
       md5_len = iobuf_get_noeof (inp);
       pktlen--;
     }
+  if (pktlen == 0)
+    goto underflow;
   sig->sig_class = iobuf_get_noeof (inp);
   pktlen--;
   if (!is_v4)
     {
+      if (pktlen < 12)
+       goto underflow;
       sig->timestamp = read_32 (inp);
       pktlen -= 4;
       sig->keyid[0] = read_32 (inp);
@@ -1660,6 +1887,8 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
       sig->keyid[1] = read_32 (inp);
       pktlen -= 4;
     }
+  if (pktlen < 2)
+    goto underflow;
   sig->pubkey_algo = iobuf_get_noeof (inp);
   pktlen--;
   sig->digest_algo = iobuf_get_noeof (inp);
@@ -1668,8 +1897,12 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
   sig->flags.revocable = 1;
   if (is_v4) /* Read subpackets.  */
     {
+      if (pktlen < 2)
+       goto underflow;
       n = read_16 (inp);
       pktlen -= 2;  /* Length of hashed data. */
+      if (pktlen < n)
+       goto underflow;
       if (n > 10000)
        {
          log_error ("signature packet: hashed data too long\n");
@@ -1694,8 +1927,12 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
            }
          pktlen -= n;
        }
+      if (pktlen < 2)
+       goto underflow;
       n = read_16 (inp);
       pktlen -= 2;  /* Length of unhashed data.  */
+      if (pktlen < n)
+       goto underflow;
       if (n > 10000)
        {
          log_error ("signature packet: unhashed data too long\n");
@@ -1722,15 +1959,8 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
        }
     }
 
-  if (pktlen < 5)  /* Sanity check.  */
-    {
-      log_error ("packet(%d) too short\n", pkttype);
-      if (list_mode)
-        es_fputs (":signature packet: [too short]\n", listfp);
-      rc = GPG_ERR_INV_PACKET;
-      goto leave;
-    }
-
+  if (pktlen < 2)
+    goto underflow;
   sig->digest_start[0] = iobuf_get_noeof (inp);
   pktlen--;
   sig->digest_start[1] = iobuf_get_noeof (inp);
@@ -1754,7 +1984,7 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
               && opt.verbose)
        log_info ("signature packet without timestamp\n");
 
-      p = parse_sig_subpkt2 (sig, SIGSUBPKT_ISSUER, NULL);
+      p = parse_sig_subpkt2 (sig, SIGSUBPKT_ISSUER);
       if (p)
        {
          sig->keyid[0] = buf32_to_u32 (p);
@@ -1778,6 +2008,17 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
       if (p)
        sig->flags.pref_ks = 1;
 
+      p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIGNERS_UID, &len);
+      if (p && len)
+        {
+          sig->signers_uid = try_make_printable_string (p, len, 0);
+          if (!sig->signers_uid)
+            {
+              rc = gpg_error_from_syserror ();
+              goto leave;
+            }
+        }
+
       p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_NOTATION, NULL);
       if (p)
        sig->flags.notation = 1;
@@ -1808,7 +2049,7 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
          unhashed area.  In theory, anyway, we should never see this
          packet off of a local keyring. */
 
-      p = parse_sig_subpkt2 (sig, SIGSUBPKT_EXPORTABLE, NULL);
+      p = parse_sig_subpkt2 (sig, SIGSUBPKT_EXPORTABLE);
       if (p && *p == 0)
        sig->flags.exportable = 0;
 
@@ -1877,6 +2118,15 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
  leave:
   iobuf_skip_rest (inp, pktlen, 0);
   return rc;
+
+ underflow:
+  log_error ("packet(%d) too short\n", pkttype);
+  if (list_mode)
+    es_fputs (":signature packet: [too short]\n", listfp);
+
+  iobuf_skip_rest (inp, pktlen, 0);
+
+  return GPG_ERR_INV_PACKET;
 }
 
 
@@ -2086,8 +2336,8 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
                    || algorithm == PUBKEY_ALGO_ECDH) && i==0)
                 {
                   char *curve = openpgp_oid_to_str (pk->pkey[0]);
-                  es_fprintf (listfp, " %s (%s)",
-                              openpgp_oid_to_curve (curve), curve);
+                  const char *name = openpgp_oid_to_curve (curve, 0);
+                  es_fprintf (listfp, " %s (%s)", name?name:"", curve);
                   xfree (curve);
                 }
               es_putc ('\n', listfp);
@@ -2166,6 +2416,11 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
                case 3:
                  for (i = 0; i < 8 && pktlen; i++, pktlen--)
                    temp[i] = iobuf_get_noeof (inp);
+                  if (i < 8)
+                    {
+                     err = gpg_error (GPG_ERR_INV_PACKET);
+                     goto leave;
+                    }
                  memcpy (ski->s2k.salt, temp, 8);
                  break;
                }
@@ -2269,7 +2524,7 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
           * NOTE: if you change the ivlen above 16, don't forget to
           * enlarge temp.  */
          ski->ivlen = openpgp_cipher_blocklen (ski->algo);
-         assert (ski->ivlen <= sizeof (temp));
+         log_assert (ski->ivlen <= sizeof (temp));
 
          if (ski->s2k.mode == 1001)
            ski->ivlen = 0;
@@ -2281,7 +2536,7 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
               err = gpg_error (GPG_ERR_INV_PACKET);
              goto leave;
            }
-         for (i = 0; i < ski->ivlen && pktlen; i++, pktlen--)
+         for (i = 0; i < ski->ivlen; i++, pktlen--)
            temp[i] = iobuf_get_noeof (inp);
          if (list_mode)
            {
@@ -2370,6 +2625,7 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
        }
     }
 
+  /* Note that KEYID below has been initialized above in list_mode.  */
   if (list_mode)
     es_fprintf (listfp, "\tkeyid: %08lX%08lX\n",
                 (ulong) keyid[0], (ulong) keyid[1]);
@@ -2507,7 +2763,7 @@ parse_user_id (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet)
 void
 make_attribute_uidname (PKT_user_id * uid, size_t max_namelen)
 {
-  assert (max_namelen > 70);
+  log_assert (max_namelen > 70);
   if (uid->numattribs <= 0)
     sprintf (uid->name, "[bad attribute packet of size %lu]",
             uid->attrib_len);
@@ -2626,42 +2882,164 @@ parse_comment (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet)
 }
 
 
-static void
-parse_trust (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * pkt)
+/* Parse a ring trust packet RFC4880 (5.10).
+ *
+ * This parser is special in that the packet is not stored as a packet
+ * but its content is merged into the previous packet.  */
+static gpg_error_t
+parse_ring_trust (parse_packet_ctx_t ctx, unsigned long pktlen)
 {
+  gpg_error_t err;
+  iobuf_t inp = ctx->inp;
+  PKT_ring_trust rt = {0};
   int c;
+  int not_gpg = 0;
 
-  (void) pkttype;
+  if (!pktlen)
+    {
+      if (list_mode)
+       es_fprintf (listfp, ":trust packet: empty\n");
+      err = 0;
+      goto leave;
+    }
 
-  pkt->pkt.ring_trust = xmalloc (sizeof *pkt->pkt.ring_trust);
+  c = iobuf_get_noeof (inp);
+  pktlen--;
+  rt.trustval = c;
   if (pktlen)
     {
-      c = iobuf_get_noeof (inp);
+      if (!c)
+        {
+          c = iobuf_get_noeof (inp);
+          /* We require that bit 7 of the sigcache is 0 (easier
+           * eof handling).  */
+          if (!(c & 0x80))
+            rt.sigcache = c;
+        }
+      else
+        iobuf_get_noeof (inp);  /* Dummy read.  */
       pktlen--;
-      pkt->pkt.ring_trust->trustval = c;
-      pkt->pkt.ring_trust->sigcache = 0;
-      if (!c && pktlen == 1)
-       {
-         c = iobuf_get_noeof (inp);
-         pktlen--;
-         /* We require that bit 7 of the sigcache is 0 (easier eof
-             handling).  */
-         if (!(c & 0x80))
-           pkt->pkt.ring_trust->sigcache = c;
-       }
-      if (list_mode)
-       es_fprintf (listfp, ":trust packet: flag=%02x sigcache=%02x\n",
-                    pkt->pkt.ring_trust->trustval,
-                    pkt->pkt.ring_trust->sigcache);
     }
-  else
+
+  /* Next is the optional subtype.  */
+  if (pktlen > 3)
     {
-      pkt->pkt.ring_trust->trustval = 0;
-      pkt->pkt.ring_trust->sigcache = 0;
-      if (list_mode)
-       es_fprintf (listfp, ":trust packet: empty\n");
+      char tmp[4];
+      tmp[0] = iobuf_get_noeof (inp);
+      tmp[1] = iobuf_get_noeof (inp);
+      tmp[2] = iobuf_get_noeof (inp);
+      tmp[3] = iobuf_get_noeof (inp);
+      pktlen -= 4;
+      if (!memcmp (tmp, "gpg", 3))
+        rt.subtype = tmp[3];
+      else
+        not_gpg = 1;
     }
+  /* If it is a key or uid subtype read the remaining data.  */
+  if ((rt.subtype == RING_TRUST_KEY || rt.subtype == RING_TRUST_UID)
+      && pktlen >= 6 )
+    {
+      int i;
+      unsigned int namelen;
+
+      rt.keysrc = iobuf_get_noeof (inp);
+      pktlen--;
+      rt.keyupdate = read_32 (inp);
+      pktlen -= 4;
+      namelen = iobuf_get_noeof (inp);
+      pktlen--;
+      if (namelen && pktlen)
+        {
+          rt.url = xtrymalloc (namelen + 1);
+          if (!rt.url)
+            {
+              err = gpg_error_from_syserror ();
+              goto leave;
+            }
+          for (i = 0; pktlen && i < namelen; pktlen--, i++)
+            rt.url[i] = iobuf_get_noeof (inp);
+          rt.url[i] = 0;
+        }
+    }
+
+  if (list_mode)
+    {
+      if (rt.subtype == RING_TRUST_SIG)
+        es_fprintf (listfp, ":trust packet: sig flag=%02x sigcache=%02x\n",
+                    rt.trustval, rt.sigcache);
+      else if (rt.subtype == RING_TRUST_UID || rt.subtype == RING_TRUST_KEY)
+        {
+          unsigned char *p;
+
+          es_fprintf (listfp, ":trust packet: %s upd=%lu src=%d%s",
+                      (rt.subtype == RING_TRUST_UID? "uid" : "key"),
+                      (unsigned long)rt.keyupdate,
+                      rt.keysrc,
+                      (rt.url? " url=":""));
+          if (rt.url)
+            {
+              for (p = rt.url; *p; p++)
+                {
+                  if (*p >= ' ' && *p <= 'z')
+                    es_putc (*p, listfp);
+                  else
+                    es_fprintf (listfp, "\\x%02x", *p);
+                }
+            }
+          es_putc ('\n', listfp);
+        }
+      else if (not_gpg)
+        es_fprintf (listfp, ":trust packet: not created by gpg\n");
+      else
+        es_fprintf (listfp, ":trust packet: subtype=%02x\n",
+                    rt.subtype);
+    }
+
+  /* Now transfer the data to the respective packet.  Do not do this
+   * if SKIP_META is set.  */
+  if (!ctx->last_pkt.pkt.generic || ctx->skip_meta)
+    ;
+  else if (rt.subtype == RING_TRUST_SIG
+           && ctx->last_pkt.pkttype == PKT_SIGNATURE)
+    {
+      PKT_signature *sig = ctx->last_pkt.pkt.signature;
+
+      if ((rt.sigcache & 1))
+        {
+          sig->flags.checked = 1;
+          sig->flags.valid = !!(rt.sigcache & 2);
+        }
+    }
+  else if (rt.subtype == RING_TRUST_UID
+           && (ctx->last_pkt.pkttype == PKT_USER_ID
+               || ctx->last_pkt.pkttype == PKT_ATTRIBUTE))
+    {
+      PKT_user_id *uid = ctx->last_pkt.pkt.user_id;
+
+      uid->keysrc = rt.keysrc;
+      uid->keyupdate = rt.keyupdate;
+      uid->updateurl = rt.url;
+      rt.url = NULL;
+    }
+  else if (rt.subtype == RING_TRUST_KEY
+           && (ctx->last_pkt.pkttype == PKT_PUBLIC_KEY
+               || ctx->last_pkt.pkttype == PKT_SECRET_KEY))
+    {
+      PKT_public_key *pk = ctx->last_pkt.pkt.public_key;
+
+      pk->keysrc = rt.keysrc;
+      pk->keyupdate = rt.keyupdate;
+      pk->updateurl = rt.url;
+      rt.url = NULL;
+    }
+
+  err = 0;
+
+ leave:
+  xfree (rt.url);
+  free_packet (NULL, ctx); /* This sets ctx->last_pkt to NULL.  */
   iobuf_skip_rest (inp, pktlen, 0);
+  return err;
 }
 
 
@@ -2714,7 +3092,6 @@ parse_plaintext (IOBUF inp, int pkttype, unsigned long pktlen,
     pktlen -= 4;
   pt->len = pktlen;
   pt->buf = inp;
-  pktlen = 0;
 
   if (list_mode)
     {
@@ -2870,9 +3247,9 @@ parse_mdc (IOBUF inp, int pkttype, unsigned long pktlen,
 
 
 /*
- * This packet is internally generated by us (ibn armor.c) to transfer
+ * This packet is internally generated by us (in armor.c) to transfer
  * some information to the lower layer.  To make sure that this packet
- * is really a GPG faked one and not one comming from outside, we
+ * is really a GPG faked one and not one coming from outside, we
  * first check that there is a unique tag in it.
  *
  * The format of such a control packet is:
@@ -2906,7 +3283,7 @@ parse_gpg_control (IOBUF inp, int pkttype, unsigned long pktlen,
     goto skipit;  /* Definitely too large.  We skip it to avoid an
                      overflow in the malloc.  */
   if (list_mode)
-    puts ("- gpg control packet");
+    es_fputs ("- gpg control packet", listfp);
 
   packet->pkt.gpg_control = xmalloc (sizeof *packet->pkt.gpg_control
                                     + pktlen - 1);