gpg: Graceful skip reading of corrupt MPIs.
[gnupg.git] / g10 / encr-data.c
index c56e017..105b105 100644 (file)
@@ -1,12 +1,12 @@
 /* encr-data.c -  process an encrypted data packet
  * Copyright (C) 1998, 1999, 2000, 2001, 2005,
- *               2006 Free Software Foundation, Inc.
+ *               2006, 2009, 2012 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,
@@ -15,9 +15,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/>.
  */
 
 #include <config.h>
@@ -32,6 +30,7 @@
 #include "cipher.h"
 #include "options.h"
 #include "i18n.h"
+#include "status.h"
 
 
 static int mdc_decode_filter ( void *opaque, int control, IOBUF a,
@@ -39,14 +38,35 @@ static int mdc_decode_filter ( void *opaque, int control, IOBUF a,
 static int decode_filter ( void *opaque, int control, IOBUF a,
                                        byte *buf, size_t *ret_len);
 
-typedef struct 
+typedef struct decode_filter_context_s
 {
   gcry_cipher_hd_t cipher_hd;
   gcry_md_hd_t mdc_hash;
   char defer[22];
   int  defer_filled;
   int  eof_seen;
-} decode_filter_ctx_t;
+  int  refcount;
+} *decode_filter_ctx_t;
+
+
+/* Helper to release the decode context.  */
+static void
+release_dfx_context (decode_filter_ctx_t dfx)
+{
+  if (!dfx)
+    return;
+
+  assert (dfx->refcount);
+  if ( !--dfx->refcount )
+    {
+      gcry_cipher_close (dfx->cipher_hd);
+      dfx->cipher_hd = NULL;
+      gcry_md_close (dfx->mdc_hash);
+      dfx->mdc_hash = NULL;
+      xfree (dfx);
+    }
+}
+
 
 
 /****************
@@ -61,40 +81,69 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek )
   byte temp[32];
   unsigned blocksize;
   unsigned nprefix;
-  
-  memset( &dfx, 0, sizeof dfx );
+
+  dfx = xtrycalloc (1, sizeof *dfx);
+  if (!dfx)
+    return gpg_error_from_syserror ();
+  dfx->refcount = 1;
+
   if ( opt.verbose && !dek->algo_info_printed )
     {
-      const char *s = gcry_cipher_algo_name (dek->algo);
-      if (s && *s)
-        log_info(_("%s encrypted data\n"), s );
+      if (!openpgp_cipher_test_algo (dek->algo))
+        log_info (_("%s encrypted data\n"),
+                  openpgp_cipher_algo_name (dek->algo));
       else
-        log_info(_("encrypted with unknown algorithm %d\n"), dek->algo );
+        log_info (_("encrypted with unknown algorithm %d\n"), dek->algo );
       dek->algo_info_printed = 1;
     }
+
+  {
+    char buf[20];
+
+    snprintf (buf, sizeof buf, "%d %d", ed->mdc_method, dek->algo);
+    write_status_text (STATUS_DECRYPTION_INFO, buf);
+  }
+
+  if (opt.show_session_key)
+    {
+      char numbuf[25];
+      char *hexbuf;
+
+      snprintf (numbuf, sizeof numbuf, "%d:", dek->algo);
+      hexbuf = bin2hex (dek->key, dek->keylen, NULL);
+      if (!hexbuf)
+        {
+          rc = gpg_error_from_syserror ();
+          goto leave;
+        }
+      log_info ("session key: '%s%s'\n", numbuf, hexbuf);
+      write_status_strings (STATUS_SESSION_KEY, numbuf, hexbuf, NULL);
+      xfree (hexbuf);
+    }
+
   rc = openpgp_cipher_test_algo (dek->algo);
   if (rc)
     goto leave;
-  blocksize = gcry_cipher_get_algo_blklen (dek->algo);
+  blocksize = openpgp_cipher_get_algo_blklen (dek->algo);
   if ( !blocksize || blocksize > 16 )
-    log_fatal("unsupported blocksize %u\n", blocksize );
+    log_fatal ("unsupported blocksize %u\n", blocksize );
   nprefix = blocksize;
   if ( ed->len && ed->len < (nprefix+2) )
     BUG();
 
-  if ( ed->mdc_method ) 
+  if ( ed->mdc_method )
     {
-      if (gcry_md_open (&dfx.mdc_hash, ed->mdc_method, 0 ))
+      if (gcry_md_open (&dfx->mdc_hash, ed->mdc_method, 0 ))
         BUG ();
       if ( DBG_HASHING )
-        gcry_md_start_debug (dfx.mdc_hash, "checkmdc");
+        gcry_md_debug (dfx->mdc_hash, "checkmdc");
     }
 
-  rc = gcry_cipher_open (&dfx.cipher_hd, dek->algo,
-                         GCRY_CIPHER_MODE_CFB,
-                         (GCRY_CIPHER_SECURE
-                          | ((ed->mdc_method || dek->algo >= 100)?
-                             0 : GCRY_CIPHER_ENABLE_SYNC)));
+  rc = openpgp_cipher_open (&dfx->cipher_hd, dek->algo,
+                           GCRY_CIPHER_MODE_CFB,
+                           (GCRY_CIPHER_SECURE
+                            | ((ed->mdc_method || dek->algo >= 100)?
+                               0 : GCRY_CIPHER_ENABLE_SYNC)));
   if (rc)
     {
       /* We should never get an error here cause we already checked
@@ -104,7 +153,7 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek )
 
 
   /* log_hexdump( "thekey", dek->key, dek->keylen );*/
-  rc = gcry_cipher_setkey (dfx.cipher_hd, dek->key, dek->keylen);
+  rc = gcry_cipher_setkey (dfx->cipher_hd, dek->key, dek->keylen);
   if ( gpg_err_code (rc) == GPG_ERR_WEAK_KEY )
     {
       log_info(_("WARNING: message was encrypted with"
@@ -117,17 +166,17 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek )
       goto leave;
     }
 
-  if (!ed->buf) 
+  if (!ed->buf)
     {
       log_error(_("problem handling encrypted packet\n"));
       goto leave;
     }
 
-  gcry_cipher_setiv (dfx.cipher_hd, NULL, 0);
+  gcry_cipher_setiv (dfx->cipher_hd, NULL, 0);
 
   if ( ed->len )
     {
-      for (i=0; i < (nprefix+2) && ed->len; i++, ed->len-- ) 
+      for (i=0; i < (nprefix+2) && ed->len; i++, ed->len-- )
         {
           if ( (c=iobuf_get(ed->buf)) == -1 )
             break;
@@ -135,7 +184,7 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek )
             temp[i] = c;
         }
     }
-  else 
+  else
     {
       for (i=0; i < (nprefix+2); i++ )
         if ( (c=iobuf_get(ed->buf)) == -1 )
@@ -143,9 +192,9 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek )
         else
           temp[i] = c;
     }
-  
-  gcry_cipher_decrypt (dfx.cipher_hd, temp, nprefix+2, NULL, 0);
-  gcry_cipher_sync (dfx.cipher_hd);
+
+  gcry_cipher_decrypt (dfx->cipher_hd, temp, nprefix+2, NULL, 0);
+  gcry_cipher_sync (dfx->cipher_hd);
   p = temp;
   /* log_hexdump( "prefix", temp, nprefix+2 ); */
   if (dek->symmetric
@@ -154,21 +203,22 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek )
       rc = gpg_error (GPG_ERR_BAD_KEY);
       goto leave;
     }
-  
-  if ( dfx.mdc_hash )
-    gcry_md_write (dfx.mdc_hash, temp, nprefix+2);
-  
+
+  if ( dfx->mdc_hash )
+    gcry_md_write (dfx->mdc_hash, temp, nprefix+2);
+
+  dfx->refcount++;
   if ( ed->mdc_method )
-    iobuf_push_filter( ed->buf, mdc_decode_filter, &dfx );
+    iobuf_push_filter ( ed->buf, mdc_decode_filter, dfx );
   else
-    iobuf_push_filter( ed->buf, decode_filter, &dfx );
+    iobuf_push_filter ( ed->buf, decode_filter, dfx );
 
   proc_packets ( procctx, ed->buf );
   ed->buf = NULL;
-  if ( ed->mdc_method && dfx.eof_seen == 2 )
+  if ( ed->mdc_method && dfx->eof_seen == 2 )
     rc = gpg_error (GPG_ERR_INV_PACKET);
   else if ( ed->mdc_method )
-    { 
+    {
       /* We used to let parse-packet.c handle the MDC packet but this
          turned out to be a problem with compressed packets: With old
          style packets there is no length information available and
@@ -184,26 +234,28 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek )
          bytes are appended.  */
       int datalen = gcry_md_get_algo_dlen (ed->mdc_method);
 
-      gcry_cipher_decrypt (dfx.cipher_hd, dfx.defer, 22, NULL, 0);
-      gcry_md_write (dfx.mdc_hash, dfx.defer, 2);
-      gcry_md_final (dfx.mdc_hash);
+      assert (dfx->cipher_hd);
+      assert (dfx->mdc_hash);
+      gcry_cipher_decrypt (dfx->cipher_hd, dfx->defer, 22, NULL, 0);
+      gcry_md_write (dfx->mdc_hash, dfx->defer, 2);
+      gcry_md_final (dfx->mdc_hash);
 
-      if (dfx.defer[0] != '\xd3' || dfx.defer[1] != '\x14' )
+      if (dfx->defer[0] != '\xd3' || dfx->defer[1] != '\x14' )
         {
           log_error("mdc_packet with invalid encoding\n");
           rc = gpg_error (GPG_ERR_INV_PACKET);
         }
       else if (datalen != 20
-               || memcmp (gcry_md_read (dfx.mdc_hash, 0),dfx.defer+2,datalen))
+               || memcmp (gcry_md_read (dfx->mdc_hash, 0),
+                          dfx->defer+2,datalen ))
         rc = gpg_error (GPG_ERR_BAD_SIGNATURE);
-      /* log_printhex("MDC message:", dfx.defer, 22); */
-      /* log_printhex("MDC calc:", gcry_md_read (dfx.mdc_hash,0), datalen); */
+      /* log_printhex("MDC message:", dfx->defer, 22); */
+      /* log_printhex("MDC calc:", gcry_md_read (dfx->mdc_hash,0), datalen); */
     }
-  
-  
+
+
  leave:
-  gcry_cipher_close (dfx.cipher_hd);
-  gcry_md_close (dfx.mdc_hash);
+  release_dfx_context (dfx);
   return rc;
 }
 
@@ -214,11 +266,11 @@ static int
 mdc_decode_filter (void *opaque, int control, IOBUF a,
                    byte *buf, size_t *ret_len)
 {
-  decode_filter_ctx_t *dfx = opaque;
+  decode_filter_ctx_t dfx = opaque;
   size_t n, size = *ret_len;
   int rc = 0;
   int c;
-  
+
   if ( control == IOBUFCTRL_UNDERFLOW && dfx->eof_seen )
     {
       *ret_len = 0;
@@ -226,17 +278,17 @@ mdc_decode_filter (void *opaque, int control, IOBUF a,
     }
   else if( control == IOBUFCTRL_UNDERFLOW )
     {
-      assert(a);
-      assert( size > 44 );
-      
+      assert (a);
+      assert ( size > 44 );
+
       /* Get at least 22 bytes and put it somewhere ahead in the buffer. */
-      for(n=22; n < 44 ; n++ )
+      for (n=22; n < 44 ; n++ )
         {
           if( (c = iobuf_get(a)) == -1 )
             break;
           buf[n] = c;
        }
-      if ( n == 44 ) 
+      if ( n == 44 )
         {
           /* We have enough stuff - flush the deferred stuff.  */
           /* (we asserted that the buffer is large enough) */
@@ -250,7 +302,7 @@ mdc_decode_filter (void *opaque, int control, IOBUF a,
               memcpy (buf, dfx->defer, 22 );
            }
           /* Now fill up. */
-          for (; n < size; n++ ) 
+          for (; n < size; n++ )
             {
               if ( (c = iobuf_get(a)) == -1 )
                 break;
@@ -279,8 +331,10 @@ mdc_decode_filter (void *opaque, int control, IOBUF a,
 
       if ( n )
         {
-          gcry_cipher_decrypt (dfx->cipher_hd, buf, n, NULL, 0);
-          gcry_md_write (dfx->mdc_hash, buf, n);
+          if ( dfx->cipher_hd )
+            gcry_cipher_decrypt (dfx->cipher_hd, buf, n, NULL, 0);
+          if ( dfx->mdc_hash )
+            gcry_md_write (dfx->mdc_hash, buf, n);
        }
       else
         {
@@ -289,7 +343,11 @@ mdc_decode_filter (void *opaque, int control, IOBUF a,
        }
       *ret_len = n;
     }
-  else if ( control == IOBUFCTRL_DESC ) 
+  else if ( control == IOBUFCTRL_FREE )
+    {
+      release_dfx_context (dfx);
+    }
+  else if ( control == IOBUFCTRL_DESC )
     {
       *(char**)buf = "mdc_decode_filter";
     }
@@ -300,22 +358,29 @@ mdc_decode_filter (void *opaque, int control, IOBUF a,
 static int
 decode_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len)
 {
-  decode_filter_ctx_t *fc = opaque;
+  decode_filter_ctx_t fc = opaque;
   size_t n, size = *ret_len;
   int rc = 0;
-  
-  if ( control == IOBUFCTRL_UNDERFLOW ) 
+
+  if ( control == IOBUFCTRL_UNDERFLOW )
     {
       assert(a);
       n = iobuf_read ( a, buf, size );
       if ( n == -1 )
         n = 0;
       if ( n )
-        gcry_cipher_decrypt (fc->cipher_hd, buf, n, NULL, 0);
+        {
+          if (fc->cipher_hd)
+            gcry_cipher_decrypt (fc->cipher_hd, buf, n, NULL, 0);
+        }
       else
         rc = -1; /* EOF */
       *ret_len = n;
     }
+  else if ( control == IOBUFCTRL_FREE )
+    {
+      release_dfx_context (fc);
+    }
   else if ( control == IOBUFCTRL_DESC )
     {
       *(char**)buf = "decode_filter";