gpg: Screen keyserver responses.
[gnupg.git] / g10 / parse-packet.c
index 26ca038..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.
  *
@@ -403,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)
     {
@@ -559,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)
@@ -840,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;
 }
@@ -856,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;
     }
@@ -864,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;
     }
@@ -892,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;
     }
@@ -970,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;
     }
@@ -978,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;
     }
@@ -1548,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);
@@ -1558,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;
     }
@@ -1591,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;
        }
@@ -1603,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;
            }
@@ -1613,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;
        }
@@ -1625,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;
            }
@@ -1635,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;
     }
@@ -1798,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;
     }
@@ -1806,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;
     }
@@ -1929,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;
     }
@@ -1936,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;
     }
@@ -2392,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;
     }
@@ -2465,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;
@@ -2503,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;
     }
@@ -2580,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;
     }
@@ -2690,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;
@@ -2706,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;