common: Fix -Wswitch warning.
[gnupg.git] / g10 / parse-packet.c
index 38cfdd9..dbb7af8 100644 (file)
 #include <string.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,
@@ -261,28 +261,29 @@ unknown_pubkey_warning (int algo)
 }
 
 
-#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 && ! 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 && ! rc);
   return rc;
@@ -294,31 +295,32 @@ 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 && ! 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 && ! rc);
   return rc;
@@ -329,40 +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*/
@@ -373,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*/
@@ -411,32 +448,47 @@ 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*/
@@ -466,18 +518,20 @@ skip_some_packets (IOBUF inp, unsigned n)
    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 INP (as returned by
-   iobuf_tell) is saved there before any data is read from INP.
+   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;
@@ -486,6 +540,9 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
   off_t pos;
 
   *skip = 0;
+  inp = ctx->inp;
+
+ again:
   log_assert (!pkt->pkt.generic);
   if (retpos || list_mode)
     {
@@ -690,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);
@@ -707,6 +764,9 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
                 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)
@@ -744,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);
@@ -772,6 +835,17 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
       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: We leak in case of an error (see the xmalloc's above).  */
   if (!rc && iobuf_error (inp))
@@ -809,8 +883,8 @@ dump_hex_line (int c, int *i)
    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_mode should already have
-   been called on INP and PARTIAL should be set.
+   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.
@@ -1572,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:
@@ -2807,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;
 }
 
 
@@ -3050,7 +3247,7 @@ 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 coming from outside, we
  * first check that there is a unique tag in it.