gpg: Screen keyserver responses.
[gnupg.git] / g10 / parse-packet.c
index 3b2698f..c69393a 100644 (file)
@@ -1,6 +1,7 @@
 /* parse-packet.c  - read packets
  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
  *               2007, 2009, 2010 Free Software Foundation, Inc.
+ * Copyright (C) 2014 Werner Koch
  *
  * This file is part of GnuPG.
  *
@@ -28,7 +29,6 @@
 #include "util.h"
 #include "packet.h"
 #include "iobuf.h"
-#include "cipher.h"
 #include "filter.h"
 #include "photoid.h"
 #include "options.h"
@@ -108,27 +108,32 @@ read_32 (IOBUF inp)
 static gcry_mpi_t
 mpi_read (iobuf_t inp, unsigned int *ret_nread, int secure)
 {
-  /*FIXME: Needs to be synced with gnupg14/mpi/mpicoder.c */
-
   int c, c1, c2, i;
+  unsigned int nmax = *ret_nread;
   unsigned int nbits, nbytes;
   size_t nread = 0;
   gcry_mpi_t a = NULL;
   byte *buf = NULL;
   byte *p;
 
+  if (!nmax)
+    goto overflow;
+
   if ((c = c1 = iobuf_get (inp)) == -1)
     goto leave;
+  if (++nread == nmax)
+    goto overflow;
   nbits = c << 8;
   if ((c = c2 = iobuf_get (inp)) == -1)
     goto leave;
+  ++nread;
   nbits |= c;
   if (nbits > MAX_EXTERN_MPI_BITS)
     {
       log_error ("mpi too large (%u bits)\n", nbits);
       goto leave;
     }
-  nread = 2;
+
   nbytes = (nbits + 7) / 8;
   buf = secure ? gcry_xmalloc_secure (nbytes + 2) : gcry_xmalloc (nbytes + 2);
   p = buf;
@@ -137,18 +142,23 @@ mpi_read (iobuf_t inp, unsigned int *ret_nread, int secure)
   for (i = 0; i < nbytes; i++)
     {
       p[i + 2] = iobuf_get (inp) & 0xff;
+      if (nread == nmax)
+        goto overflow;
       nread++;
     }
 
   if (gcry_mpi_scan (&a, GCRYMPI_FMT_PGP, buf, nread, &nread))
     a = NULL;
 
+  *ret_nread = nread;
+  gcry_free(buf);
+  return a;
+
+ overflow:
+  log_error ("mpi larger than indicated length (%u bits)\n", 8*nmax);
  leave:
-  gcry_free (buf);
-  if (nread > *ret_nread)
-    log_bug ("mpi larger than packet (%zu/%u)", nread, *ret_nread);
-  else
-    *ret_nread = nread;
+  *ret_nread = nread;
+  gcry_free(buf);
   return a;
 }
 
@@ -394,11 +404,18 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
   int hdrlen;
   int new_ctb = 0, partial = 0;
   int with_uid = (onlykeypkts == 2);
+  off_t pos;
 
   *skip = 0;
   assert (!pkt->pkt.generic);
-  if (retpos)
-    *retpos = iobuf_tell (inp);
+  if (retpos || list_mode)
+    {
+      pos = iobuf_tell (inp);
+      if (retpos)
+        *retpos = pos;
+    }
+  else
+    pos = 0; /* (silence compiler warning) */
 
   if ((ctb = iobuf_get (inp)) == -1)
     {
@@ -550,6 +567,12 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
 #endif
     }
 
+  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":"",
+                new_ctb? " new-ctb":"");
+
   pkt->pkttype = pkttype;
   rc = G10ERR_UNKNOWN_PACKET;  /* default error */
   switch (pkttype)
@@ -831,6 +854,8 @@ parse_marker (IOBUF inp, int pkttype, unsigned long pktlen)
 
  fail:
   log_error ("invalid marker packet\n");
+  if (list_mode)
+    es_fputs (":marker packet: [invalid]\n", listfp);
   iobuf_skip_rest (inp, pktlen, 0);
   return G10ERR_INVALID_PACKET;
 }
@@ -847,6 +872,8 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
   if (pktlen < 4)
     {
       log_error ("packet(%d) too short\n", pkttype);
+      if (list_mode)
+        es_fprintf (listfp, ":symkey enc packet: [too short]\n");
       rc = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
@@ -855,12 +882,16 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
   if (version != 4)
     {
       log_error ("packet(%d) with unknown version %d\n", pkttype, version);
+      if (list_mode)
+        es_fprintf (listfp, ":symkey enc packet: [unknown version]\n");
       rc = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
   if (pktlen > 200)
     {                          /* (we encode the seskeylen in a byte) */
       log_error ("packet(%d) too large\n", pkttype);
+      if (list_mode)
+        es_fprintf (listfp, ":symkey enc packet: [too large]\n");
       rc = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
@@ -883,11 +914,15 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
       break;
     default:
       log_error ("unknown S2K mode %d\n", s2kmode);
+      if (list_mode)
+        es_fprintf (listfp, ":symkey enc packet: [unknown S2K mode]\n");
       goto leave;
     }
   if (minlen > pktlen)
     {
       log_error ("packet with S2K %d too short\n", s2kmode);
+      if (list_mode)
+        es_fprintf (listfp, ":symkey enc packet: [too short]\n");
       rc = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
@@ -961,6 +996,8 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
   if (pktlen < 12)
     {
       log_error ("packet(%d) too short\n", pkttype);
+      if (list_mode)
+        es_fputs (":pubkey enc packet: [too short]\n", listfp);
       rc = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
@@ -969,6 +1006,8 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
   if (k->version != 2 && k->version != 3)
     {
       log_error ("packet(%d) with unknown version %d\n", pkttype, k->version);
+      if (list_mode)
+        es_fputs (":pubkey enc packet: [unknown version]\n", listfp);
       rc = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
@@ -1539,6 +1578,8 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
   if (pktlen < 16)
     {
       log_error ("packet(%d) too short\n", pkttype);
+      if (list_mode)
+        es_fputs (":signature packet: [too short]\n", listfp);
       goto leave;
     }
   sig->version = iobuf_get_noeof (inp);
@@ -1549,6 +1590,8 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
     {
       log_error ("packet(%d) with unknown version %d\n",
                 pkttype, sig->version);
+      if (list_mode)
+        es_fputs (":signature packet: [unknown version]\n", listfp);
       rc = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
@@ -1582,6 +1625,8 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
       if (n > 10000)
        {
          log_error ("signature packet: hashed data too long\n");
+          if (list_mode)
+            es_fputs (":signature packet: [hashed data too long]\n", listfp);
          rc = G10ERR_INVALID_PACKET;
          goto leave;
        }
@@ -1594,6 +1639,8 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
            {
              log_error ("premature eof while reading "
                         "hashed signature data\n");
+              if (list_mode)
+                es_fputs (":signature packet: [premature eof]\n", listfp);
              rc = -1;
              goto leave;
            }
@@ -1604,6 +1651,8 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
       if (n > 10000)
        {
          log_error ("signature packet: unhashed data too long\n");
+          if (list_mode)
+            es_fputs (":signature packet: [unhashed data too long]\n", listfp);
          rc = G10ERR_INVALID_PACKET;
          goto leave;
        }
@@ -1616,6 +1665,8 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
            {
              log_error ("premature eof while reading "
                         "unhashed signature data\n");
+              if (list_mode)
+                es_fputs (":signature packet: [premature eof]\n", listfp);
              rc = -1;
              goto leave;
            }
@@ -1626,6 +1677,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 = G10ERR_INVALID_PACKET;
       goto leave;
     }
@@ -1789,6 +1842,8 @@ parse_onepass_sig (IOBUF inp, int pkttype, unsigned long pktlen,
   if (pktlen < 13)
     {
       log_error ("packet(%d) too short\n", pkttype);
+      if (list_mode)
+        es_fputs (":onepass_sig packet: [too short]\n", listfp);
       rc = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
@@ -1797,6 +1852,8 @@ parse_onepass_sig (IOBUF inp, int pkttype, unsigned long pktlen,
   if (version != 3)
     {
       log_error ("onepass_sig with unknown version %d\n", version);
+      if (list_mode)
+        es_fputs (":onepass_sig packet: [unknown version]\n", listfp);
       rc = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
@@ -1920,6 +1977,8 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
   else if (version != 2 && version != 3)
     {
       log_error ("packet(%d) with unknown version %d\n", pkttype, version);
+      if (list_mode)
+        es_fputs (":key packet: [unknown version]\n", listfp);
       err = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
@@ -1927,6 +1986,8 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
   if (pktlen < 11)
     {
       log_error ("packet(%d) too short\n", pkttype);
+      if (list_mode)
+        es_fputs (":key packet: [too short]\n", listfp);
       err = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
@@ -1990,9 +2051,11 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
     {
       for (i = 0; i < npkey; i++)
         {
-          if ((algorithm == PUBKEY_ALGO_ECDSA && (i == 0))
-              || (algorithm == PUBKEY_ALGO_ECDH) && (i == 0 || i == 2))
+          if (    (algorithm == PUBKEY_ALGO_ECDSA && (i == 0))
+               || (algorithm == PUBKEY_ALGO_EDDSA && (i == 0))
+               || (algorithm == PUBKEY_ALGO_ECDH  && (i == 0 || i == 2)))
             {
+              /* Read the OID (i==1) or the KDF params (i==2).  */
               size_t n;
              err = read_size_body (inp, pktlen, &n, pk->pkey+i);
               pktlen -= n;
@@ -2012,6 +2075,7 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
               es_fprintf (listfp, "\tpkey[%d]: ", i);
               mpi_print (listfp, pk->pkey[i], mpi_print_mode);
               if ((algorithm == PUBKEY_ALGO_ECDSA
+                   || algorithm == PUBKEY_ALGO_EDDSA
                    || algorithm == PUBKEY_ALGO_ECDH) && i==0)
                 {
                   char *curve = openpgp_oid_to_str (pk->pkey[0]);
@@ -2238,6 +2302,11 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
          pk->pkey[npkey] = gcry_mpi_set_opaque (NULL,
                                                 read_rest (inp, pktlen),
                                                 pktlen * 8);
+          /* Mark that MPI as protected - we need this information for
+             importing a key.  The OPAQUE flag can't be used because
+             we also store public EdDSA values in opaque MPIs.  */
+          if (pk->pkey[npkey])
+            gcry_mpi_set_flag (pk->pkey[npkey], GCRYMPI_FLAG_USER1);
          pktlen = 0;
          if (list_mode)
             es_fprintf (listfp, "\tskey[%d]: [v4 protected]\n", npkey);
@@ -2250,6 +2319,8 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
              if (ski->is_protected)
                {
                  pk->pkey[i] = read_protected_v3_mpi (inp, &pktlen);
+                  if (pk->pkey[i])
+                    gcry_mpi_set_flag (pk->pkey[i], GCRYMPI_FLAG_USER1);
                  if (list_mode)
                    es_fprintf (listfp, "\tskey[%d]: [v3 protected]\n", i);
                }
@@ -2373,6 +2444,8 @@ parse_user_id (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet)
   if (pktlen > 2048)
     {
       log_error ("packet(%d) too large\n", pkttype);
+      if (list_mode)
+        es_fprintf (listfp, ":user ID packet: [too large]\n");
       iobuf_skip_rest (inp, pktlen, 0);
       return G10ERR_INVALID_PACKET;
     }
@@ -2446,11 +2519,23 @@ parse_attribute (IOBUF inp, int pkttype, unsigned long pktlen,
 
   (void) pkttype;
 
+  /* We better cap the size of an attribute packet to make DoS not too
+     easy.  16MB should be more then enough for one attribute packet
+     (ie. a photo).  */
+  if (pktlen > 16*1024*1024)
+    {
+      log_error ("packet(%d) too large\n", pkttype);
+      if (list_mode)
+        es_fprintf (listfp, ":attribute packet: [too large]\n");
+      iobuf_skip_rest (inp, pktlen, 0);
+      return G10ERR_INVALID_PACKET;
+    }
+
 #define EXTRA_UID_NAME_SPACE 71
   packet->pkt.user_id = xmalloc_clear (sizeof *packet->pkt.user_id
                                       + EXTRA_UID_NAME_SPACE);
   packet->pkt.user_id->ref = 1;
-  packet->pkt.user_id->attrib_data = xmalloc (pktlen);
+  packet->pkt.user_id->attrib_data = xmalloc (pktlen? pktlen:1);
   packet->pkt.user_id->attrib_len = pktlen;
 
   p = packet->pkt.user_id->attrib_data;
@@ -2484,6 +2569,9 @@ parse_comment (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet)
   if (pktlen > 65536)
     {
       log_error ("packet(%d) too large\n", pkttype);
+      if (list_mode)
+        es_fprintf (listfp, ":%scomment packet: [too large]\n",
+                    pkttype == PKT_OLD_COMMENT ? "OpenPGP draft " : "");
       iobuf_skip_rest (inp, pktlen, 0);
       return G10ERR_INVALID_PACKET;
     }
@@ -2561,6 +2649,8 @@ parse_plaintext (IOBUF inp, int pkttype, unsigned long pktlen,
   if (!partial && pktlen < 6)
     {
       log_error ("packet(%d) too short (%lu)\n", pkttype, (ulong) pktlen);
+      if (list_mode)
+        es_fputs (":literal data packet: [too short]\n", listfp);
       rc = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
@@ -2671,6 +2761,8 @@ parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen,
        {
          log_error ("encrypted_mdc packet with unknown version %d\n",
                     version);
+          if (list_mode)
+            es_fputs (":encrypted data packet: [unknown version]\n", listfp);
          /*skip_rest(inp, pktlen); should we really do this? */
          rc = gpg_error (GPG_ERR_INV_PACKET);
          goto leave;
@@ -2687,6 +2779,8 @@ parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen,
     {
       /* Actually this is blocksize+2.  */
       log_error ("packet(%d) too short\n", pkttype);
+      if (list_mode)
+        es_fputs (":encrypted data packet: [too short]\n", listfp);
       rc = G10ERR_INVALID_PACKET;
       iobuf_skip_rest (inp, pktlen, partial);
       goto leave;