gpg: Move sqlite helper functions into their own file.
[gnupg.git] / g10 / parse-packet.c
index 1335403..4e236cb 100644 (file)
@@ -272,7 +272,7 @@ dbg_parse_packet (IOBUF inp, PACKET *pkt, const char *dbg_f, int dbg_l)
     {
       rc = parse (inp, pkt, 0, NULL, &skip, NULL, 0, "parse", dbg_f, dbg_l);
     }
-  while (skip);
+  while (skip && ! rc);
   return rc;
 }
 #else /*!DEBUG_PARSE_PACKET*/
@@ -285,7 +285,7 @@ parse_packet (IOBUF inp, PACKET * pkt)
     {
       rc = parse (inp, pkt, 0, NULL, &skip, NULL, 0);
     }
-  while (skip);
+  while (skip && ! rc);
   return rc;
 }
 #endif /*!DEBUG_PARSE_PACKET*/
@@ -308,7 +308,7 @@ dbg_search_packet (IOBUF inp, PACKET * pkt, off_t * retpos, int with_uid,
        parse (inp, 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*/
@@ -321,7 +321,7 @@ search_packet (IOBUF inp, PACKET * pkt, off_t * retpos, int with_uid)
     {
       rc = parse (inp, pkt, with_uid ? 2 : 1, retpos, &skip, NULL, 0);
     }
-  while (skip);
+  while (skip && ! rc);
   return rc;
 }
 #endif /*!DEBUG_PARSE_PACKET*/
@@ -336,6 +336,10 @@ dbg_copy_all_packets (IOBUF inp, IOBUF out, const char *dbg_f, int dbg_l)
 {
   PACKET pkt;
   int skip, rc = 0;
+
+  if (! out)
+    log_bug ("copy_all_packets: OUT may not be NULL.\n");
+
   do
     {
       init_packet (&pkt);
@@ -351,6 +355,10 @@ copy_all_packets (IOBUF inp, IOBUF out)
 {
   PACKET pkt;
   int skip, rc = 0;
+
+  if (! out)
+    log_bug ("copy_all_packets: OUT may not be NULL.\n");
+
   do
     {
       init_packet (&pkt);
@@ -549,16 +557,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.  */
         {
@@ -611,12 +624,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
@@ -627,6 +654,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);
@@ -1673,25 +1711,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++;
        }
     }
@@ -1731,13 +1775,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);
@@ -1745,6 +1795,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);
@@ -1753,8 +1805,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");
@@ -1779,8 +1835,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");
@@ -1807,15 +1867,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);
@@ -1962,6 +2015,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;
 }