gpgparsemail: Allow weirdly-mixed pkcs7 signatures.
[gnupg.git] / kbx / keybox-openpgp.c
index 8ac7139..a0e4ab9 100644 (file)
@@ -1,11 +1,11 @@
 /* keybox-openpgp.c - OpenPGP key parsing
- *     Copyright (C) 2001, 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2003, 2011 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
  * GnuPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
@@ -14,9 +14,7 @@
  * 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, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 /* This is a simple OpenPGP parser suitable for all OpenPGP key
 
 #include <gcrypt.h>
 
-
-enum packet_types
-  {
-    PKT_NONE              =0,
-    PKT_PUBKEY_ENC        =1, /* public key encrypted packet */
-    PKT_SIGNATURE         =2, /* secret key encrypted packet */
-    PKT_SYMKEY_ENC        =3, /* session key packet (OpenPGP)*/
-    PKT_ONEPASS_SIG        =4, /* one pass sig packet (OpenPGP)*/
-    PKT_SECRET_KEY        =5, /* secret key */
-    PKT_PUBLIC_KEY        =6, /* public key */
-    PKT_SECRET_SUBKEY      =7, /* secret subkey (OpenPGP) */
-    PKT_COMPRESSED        =8, /* compressed data packet */
-    PKT_ENCRYPTED         =9, /* conventional encrypted data */
-    PKT_MARKER           =10, /* marker packet (OpenPGP) */
-    PKT_PLAINTEXT        =11, /* plaintext data with filename and mode */
-    PKT_RING_TRUST       =12, /* keyring trust packet */
-    PKT_USER_ID                  =13, /* user id packet */
-    PKT_PUBLIC_SUBKEY     =14, /* public subkey (OpenPGP) */
-    PKT_OLD_COMMENT       =16, /* comment packet from an OpenPGP draft */
-    PKT_ATTRIBUTE         =17, /* PGP's attribute packet */
-    PKT_ENCRYPTED_MDC     =18, /* integrity protected encrypted data */
-    PKT_MDC              =19, /* manipulation detection code packet */
-    PKT_COMMENT                  =61, /* new comment packet (private) */
-    PKT_GPG_CONTROL       =63  /* internal control packet */
-  };
-
-
+#include "../common/openpgpdefs.h"
+#include "host2net.h"
 
 /* Assume a valid OpenPGP packet at the address pointed to by BUFBTR
-   which is of amaximum length as stored at BUFLEN.  Return the header
+   which has a maximum length as stored at BUFLEN.  Return the header
    information of that packet and advance the pointer stored at BUFPTR
    to the next packet; also adjust the length stored at BUFLEN to
    match the remaining bytes. If there are no more packets, store NULL
    at BUFPTR.  Return an non-zero error code on failure or the
-   follwing data on success:
+   following data on success:
 
    R_DATAPKT = Pointer to the begin of the packet data.
-   R_DATALEN = Length of this data.  This has already been checked to fit 
-               into the buffer. 
+   R_DATALEN = Length of this data.  This has already been checked to fit
+               into the buffer.
    R_PKTTYPE = The packet type.
    R_NTOTAL  = The total number of bytes of this packet
 
@@ -93,12 +66,11 @@ next_packet (unsigned char const **bufptr, size_t *buflen,
 
   if (!len)
     return gpg_error (GPG_ERR_NO_DATA);
-  
+
   ctb = *buf++; len--;
   if ( !(ctb & 0x80) )
     return gpg_error (GPG_ERR_INV_PACKET); /* Invalid CTB. */
-  
-  pktlen = 0;
+
   if ((ctb & 0x40))  /* New style (OpenPGP) CTB.  */
     {
       pkttype = (ctb & 0x3f);
@@ -110,7 +82,7 @@ next_packet (unsigned char const **bufptr, size_t *buflen,
       if ( c < 192 )
         pktlen = c;
       else if ( c < 224 )
-        { 
+        {
           pktlen = (c - 192) * 256;
           if (!len)
             return gpg_error (GPG_ERR_INV_PACKET); /* No 2nd length byte. */
@@ -121,10 +93,8 @@ next_packet (unsigned char const **bufptr, size_t *buflen,
         {
           if (len <4 )
             return gpg_error (GPG_ERR_INV_PACKET); /* No length bytes. */
-          pktlen  = (*buf++) << 24;
-          pktlen |= (*buf++) << 16;
-          pktlen |= (*buf++) << 8;
-          pktlen |= (*buf++);
+          pktlen = buf32_to_ulong (buf);
+          buf += 4;
           len -= 4;
       }
       else /* Partial length encoding is not allowed for key packets. */
@@ -152,7 +122,7 @@ next_packet (unsigned char const **bufptr, size_t *buflen,
   switch (pkttype)
     {
     case PKT_SIGNATURE:
-    case PKT_SECRET_KEY:       
+    case PKT_SECRET_KEY:
     case PKT_PUBLIC_KEY:
     case PKT_SECRET_SUBKEY:
     case PKT_MARKER:
@@ -168,9 +138,16 @@ next_packet (unsigned char const **bufptr, size_t *buflen,
       return gpg_error (GPG_ERR_UNEXPECTED);
     }
 
-  if (pktlen == 0xffffffff) 
-      return gpg_error (GPG_ERR_INV_PACKET);
-  
+  if (pkttype == 63 && pktlen == 0xFFFFFFFF)
+    /* 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.  */
+    return gpg_error (GPG_ERR_INV_PACKET);
+
   if (pktlen > len)
     return gpg_error (GPG_ERR_INV_PACKET); /* Packet length header too long. */
 
@@ -188,7 +165,7 @@ next_packet (unsigned char const **bufptr, size_t *buflen,
 }
 
 
-/* Parse a key packet and store the ionformation in KI. */
+/* Parse a key packet and store the information in KI. */
 static gpg_error_t
 parse_key (const unsigned char *data, size_t datalen,
            struct _keybox_openpgp_key_info *ki)
@@ -197,35 +174,28 @@ parse_key (const unsigned char *data, size_t datalen,
   const unsigned char *data_start = data;
   int i, version, algorithm;
   size_t n;
-  unsigned long timestamp, expiredate;
   int npkey;
   unsigned char hashbuffer[768];
   const unsigned char *mpi_n = NULL;
   size_t mpi_n_len = 0, mpi_e_len = 0;
   gcry_md_hd_t md;
+  int is_ecc = 0;
 
   if (datalen < 5)
     return gpg_error (GPG_ERR_INV_PACKET);
   version = *data++; datalen--;
   if (version < 2 || version > 4 )
     return gpg_error (GPG_ERR_INV_PACKET); /* Invalid version. */
-  
-  timestamp = ((data[0]<<24)|(data[1]<<16)|(data[2]<<8)|(data[3]));
+
+  /*timestamp = ((data[0]<<24)|(data[1]<<16)|(data[2]<<8)|(data[3]));*/
   data +=4; datalen -=4;
 
   if (version < 4)
     {
-      unsigned short ndays;
-      
       if (datalen < 2)
         return gpg_error (GPG_ERR_INV_PACKET);
-      ndays = ((data[0]<<8)|(data[1]));
       data +=2; datalen -= 2;
-      if (ndays)
-      expiredate = ndays? (timestamp + ndays * 86400L) : 0;
     }
-  else
-    expiredate = 0; /* This is stored in the self-signature. */
 
   if (!datalen)
     return gpg_error (GPG_ERR_INV_PACKET);
@@ -233,43 +203,68 @@ parse_key (const unsigned char *data, size_t datalen,
 
   switch (algorithm)
     {
-    case 1:
-    case 2:
-    case 3: /* RSA */
-      npkey = 2; 
+    case PUBKEY_ALGO_RSA:
+    case PUBKEY_ALGO_RSA_E:
+    case PUBKEY_ALGO_RSA_S:
+      npkey = 2;
       break;
-    case 16:
-    case 20: /* Elgamal */
+    case PUBKEY_ALGO_ELGAMAL_E:
+    case PUBKEY_ALGO_ELGAMAL:
       npkey = 3;
       break;
-    case 17: /* DSA */
+    case PUBKEY_ALGO_DSA:
       npkey = 4;
       break;
+    case PUBKEY_ALGO_ECDH:
+      npkey = 3;
+      is_ecc = 1;
+      break;
+    case PUBKEY_ALGO_ECDSA:
+    case PUBKEY_ALGO_EDDSA:
+      npkey = 2;
+      is_ecc = 1;
+      break;
     default: /* Unknown algorithm. */
       return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM);
     }
 
+  ki->algo = algorithm;
+
   for (i=0; i < npkey; i++ )
     {
       unsigned int nbits, nbytes;
-      
+
       if (datalen < 2)
         return gpg_error (GPG_ERR_INV_PACKET);
-      nbits = ((data[0]<<8)|(data[1]));
-      data += 2; datalen -=2;
-      nbytes = (nbits+7) / 8;
-      if (datalen < nbytes)
-        return gpg_error (GPG_ERR_INV_PACKET);
-      /* For use by v3 fingerprint calculation we need to know the RSA
-         modulus and exponent. */
-      if (i==0) 
+
+      if (is_ecc && (i == 0 || i == 2))
         {
-          mpi_n = data; 
-          mpi_n_len = nbytes;
+          nbytes = data[0];
+          if (nbytes < 2 || nbytes > 254)
+            return gpg_error (GPG_ERR_INV_PACKET);
+          nbytes++; /* The size byte itself.  */
+          if (datalen < nbytes)
+            return gpg_error (GPG_ERR_INV_PACKET);
         }
-      else if (i==1)
-        mpi_e_len = nbytes;
-        
+      else
+        {
+          nbits = ((data[0]<<8)|(data[1]));
+          data += 2;
+          datalen -= 2;
+          nbytes = (nbits+7) / 8;
+          if (datalen < nbytes)
+            return gpg_error (GPG_ERR_INV_PACKET);
+          /* For use by v3 fingerprint calculation we need to know the RSA
+             modulus and exponent. */
+          if (i==0)
+            {
+              mpi_n = data;
+              mpi_n_len = nbytes;
+            }
+          else if (i==1)
+            mpi_e_len = nbytes;
+        }
+
       data += nbytes; datalen -= nbytes;
     }
   n = data - data_start;
@@ -289,12 +284,12 @@ parse_key (const unsigned char *data, size_t datalen,
       memcpy (ki->fpr, gcry_md_read (md, 0), 16);
       gcry_md_close (md);
       ki->fprlen = 16;
-      
+
       if (mpi_n_len < 8)
         {
           /* Moduli less than 64 bit are out of the specs scope.  Zero
-             them out becuase this is what gpg does too. */
-          memset (ki->keyid, 0, 8); 
+             them out because this is what gpg does too. */
+          memset (ki->keyid, 0, 8);
         }
       else
         memcpy (ki->keyid, mpi_n + mpi_n_len - 8, 8);
@@ -303,10 +298,10 @@ parse_key (const unsigned char *data, size_t datalen,
     {
       /* Its a pitty that we need to prefix the buffer with the tag
          and a length header: We can't simply pass it to the fast
-         hashing fucntion for that reason.  It might be a good idea to
+         hashing function for that reason.  It might be a good idea to
          have a scatter-gather enabled hash function. What we do here
          is to use a static buffer if this one is large enough and
-         only use the regular hash fucntions if this buffer is not
+         only use the regular hash functions if this buffer is not
          large enough. */
       if ( 3 + n < sizeof hashbuffer )
         {
@@ -340,22 +335,22 @@ parse_key (const unsigned char *data, size_t datalen,
 /* The caller must pass the address of an INFO structure which will
    get filled on success with information pertaining to the OpenPGP
    keyblock IMAGE of length IMAGELEN.  Note that a caller does only
-   need to release this INFO structure when the function returns
+   need to release this INFO structure if the function returns
    success.  If NPARSED is not NULL the actual number of bytes parsed
    will be stored at this address.  */
 gpg_error_t
 _keybox_parse_openpgp (const unsigned char *image, size_t imagelen,
-                       size_t *nparsed,
-                       keybox_openpgp_info_t info)
+                       size_t *nparsed, keybox_openpgp_info_t info)
 {
   gpg_error_t err = 0;
   const unsigned char *image_start, *data;
   size_t n, datalen;
   int pkttype;
   int first = 1;
+  int read_error = 0;
   struct _keybox_openpgp_key_info *k, **ktail = NULL;
   struct _keybox_openpgp_uid_info *u, **utail = NULL;
-  
+
   memset (info, 0, sizeof *info);
   if (nparsed)
     *nparsed = 0;
@@ -365,7 +360,10 @@ _keybox_parse_openpgp (const unsigned char *image, size_t imagelen,
     {
       err = next_packet (&image, &imagelen, &data, &datalen, &pkttype, &n);
       if (err)
-        break;
+        {
+          read_error = 1;
+          break;
+        }
 
       if (first)
         {
@@ -376,13 +374,15 @@ _keybox_parse_openpgp (const unsigned char *image, size_t imagelen,
           else
             {
               err = gpg_error (GPG_ERR_UNEXPECTED);
+              if (nparsed)
+                *nparsed += n;
               break;
             }
           first = 0;
         }
       else if (pkttype == PKT_PUBLIC_KEY || pkttype == PKT_SECRET_KEY)
         break; /* Next keyblock encountered - ready. */
-      
+
       if (nparsed)
         *nparsed += n;
 
@@ -405,7 +405,7 @@ _keybox_parse_openpgp (const unsigned char *image, size_t imagelen,
               u = xtrycalloc (1, sizeof *u);
               if (!u)
                 {
-                  err = gpg_error_from_errno (errno);
+                  err = gpg_error_from_syserror ();
                   break;
                 }
               u->off = data - image_start;
@@ -420,7 +420,7 @@ _keybox_parse_openpgp (const unsigned char *image, size_t imagelen,
           if (err)
             break;
         }
-      else if( pkttype == PKT_PUBLIC_SUBKEY && datalen && *data == '#' ) 
+      else if( pkttype == PKT_PUBLIC_SUBKEY && datalen && *data == '#' )
         {
           /* Early versions of GnuPG used old PGP comment packets;
            * luckily all those comments are prefixed by a hash
@@ -435,9 +435,12 @@ _keybox_parse_openpgp (const unsigned char *image, size_t imagelen,
               if (err)
                 {
                   info->nsubkeys--;
-                  if (gpg_err_code (err) != GPG_ERR_UNKNOWN_ALGORITHM)
-                    break;
                   /* We ignore subkeys with unknown algorithms. */
+                  if (gpg_err_code (err) == GPG_ERR_UNKNOWN_ALGORITHM
+                      || gpg_err_code (err) == GPG_ERR_UNSUPPORTED_ALGORITHM)
+                    err = 0;
+                  if (err)
+                    break;
                 }
               else
                 ktail = &info->subkeys.next;
@@ -447,7 +450,7 @@ _keybox_parse_openpgp (const unsigned char *image, size_t imagelen,
               k = xtrycalloc (1, sizeof *k);
               if (!k)
                 {
-                  err = gpg_error_from_errno (errno);
+                  err = gpg_error_from_syserror ();
                   break;
                 }
               err = parse_key (data, datalen, k);
@@ -455,9 +458,12 @@ _keybox_parse_openpgp (const unsigned char *image, size_t imagelen,
                 {
                   xfree (k);
                   info->nsubkeys--;
-                  if (gpg_err_code (err) != GPG_ERR_UNKNOWN_ALGORITHM)
-                    break;
                   /* We ignore subkeys with unknown algorithms. */
+                  if (gpg_err_code (err) == GPG_ERR_UNKNOWN_ALGORITHM
+                      || gpg_err_code (err) == GPG_ERR_UNSUPPORTED_ALGORITHM)
+                    err = 0;
+                  if (err)
+                    break;
                 }
               else
                 {
@@ -471,11 +477,10 @@ _keybox_parse_openpgp (const unsigned char *image, size_t imagelen,
   if (err)
     {
       _keybox_destroy_openpgp_info (info);
-      if (!first
-          && (gpg_err_code (err) == GPG_ERR_UNSUPPORTED_ALGORITHM
-              || gpg_err_code (err) == GPG_ERR_UNKNOWN_ALGORITHM))
+      if (!read_error)
         {
-          /* We are able to skip to the end of this keyblock. */
+          /* Packet parsing worked, thus we should be able to skip the
+             rest of the keyblock.  */
           while (image)
             {
               if (next_packet (&image, &imagelen,
@@ -484,7 +489,7 @@ _keybox_parse_openpgp (const unsigned char *image, size_t imagelen,
 
               if (pkttype == PKT_PUBLIC_KEY || pkttype == PKT_SECRET_KEY)
                 break; /* Next keyblock encountered - ready. */
-              
+
               if (nparsed)
                 *nparsed += n;
             }