agent: Avoid appending a '\0' byte to the response of READKEY
[gnupg.git] / sm / decrypt.c
index 4fb3c2d..60ed14a 100644 (file)
@@ -14,7 +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, see <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
@@ -22,7 +22,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <unistd.h> 
+#include <unistd.h>
 #include <time.h>
 #include <assert.h>
 
 #include <ksba.h>
 
 #include "keydb.h"
-#include "i18n.h"
+#include "../common/i18n.h"
+#include "../common/compliance.h"
 
-struct decrypt_filter_parm_s 
+struct decrypt_filter_parm_s
 {
   int algo;
   int mode;
@@ -41,7 +42,7 @@ struct decrypt_filter_parm_s
   gcry_cipher_hd_t hd;
   char iv[16];
   size_t ivlen;
-  int any_data;  /* dod we push anything through the filter at all? */
+  int any_data;  /* did we push anything through the filter at all? */
   unsigned char lastblock[16];  /* to strip the padding we have to
                                    keep this one */
   char helpblock[16];  /* needed because there is no block buffering in
@@ -53,7 +54,7 @@ struct decrypt_filter_parm_s
 
 /* Decrypt the session key and fill in the parm structure.  The
    algo and the IV is expected to be already in PARM. */
-static int 
+static int
 prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
                     ksba_const_sexp_t enc_val,
                     struct decrypt_filter_parm_s *parm)
@@ -74,19 +75,21 @@ prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
     log_printhex ("pkcs1 encoded session key:", seskey, seskeylen);
 
   n=0;
-  if (seskeylen == 24)
+  if (seskeylen == 24 || seskeylen == 16)
     {
-      /* Smells like a 3-des key.  This might happen because a SC has
-         already done the unpacking. */
+      /* Smells like a 3-DES or AES-128 key.  This might happen
+       * because a SC has already done the unpacking.  A better
+       * solution would be to test for this only after we triggered
+       * the GPG_ERR_INV_SESSION_KEY. */
     }
   else
     {
       if (n + 7 > seskeylen )
         {
           rc = gpg_error (GPG_ERR_INV_SESSION_KEY);
-          goto leave; 
+          goto leave;
         }
-      
+
       /* FIXME: Actually the leading zero is required but due to the way
          we encode the output in libgcrypt as an MPI we are not able to
          encode that leading zero.  However, when using a Smartcard we are
@@ -94,20 +97,20 @@ prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
          should be fixed in gpg-agent of course. */
       if (!seskey[n])
         n++;
-      
+
       if (seskey[n] != 2 )  /* Wrong block type version. */
-        { 
+        {
           rc = gpg_error (GPG_ERR_INV_SESSION_KEY);
-          goto leave; 
+          goto leave;
         }
-      
+
       for (n++; n < seskeylen && seskey[n]; n++) /* Skip the random bytes. */
         ;
       n++; /* and the zero byte */
       if (n >= seskeylen )
-        { 
+        {
           rc = gpg_error (GPG_ERR_INV_SESSION_KEY);
-          goto leave; 
+          goto leave;
         }
     }
 
@@ -120,7 +123,7 @@ prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
       log_error ("error creating decryptor: %s\n", gpg_strerror (rc));
       goto leave;
     }
-                        
+
   rc = gcry_cipher_setkey (parm->hd, seskey+n, seskeylen-n);
   if (gpg_err_code (rc) == GPG_ERR_WEAK_KEY)
     {
@@ -241,8 +244,8 @@ int
 gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
 {
   int rc;
-  Base64Context b64reader = NULL;
-  Base64Context b64writer = NULL;
+  gnupg_ksba_io_t b64reader = NULL;
+  gnupg_ksba_io_t b64writer = NULL;
   ksba_reader_t reader;
   ksba_writer_t writer;
   ksba_cms_t cms = NULL;
@@ -256,10 +259,10 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
 
   audit_set_type (ctrl->audit, AUDIT_TYPE_DECRYPT);
 
-  kh = keydb_new (0);
+  kh = keydb_new ();
   if (!kh)
     {
-      log_error (_("failed to allocated keyDB handle\n"));
+      log_error (_("failed to allocate keyDB handle\n"));
       rc = gpg_error (GPG_ERR_GENERAL);
       goto leave;
     }
@@ -272,14 +275,21 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
       goto leave;
     }
 
-  rc = gpgsm_create_reader (&b64reader, ctrl, in_fp, 0, &reader);
+  rc = gnupg_ksba_create_reader
+    (&b64reader, ((ctrl->is_pem? GNUPG_KSBA_IO_PEM : 0)
+                  | (ctrl->is_base64? GNUPG_KSBA_IO_BASE64 : 0)
+                  | (ctrl->autodetect_encoding? GNUPG_KSBA_IO_AUTODETECT : 0)),
+     in_fp, &reader);
   if (rc)
     {
       log_error ("can't create reader: %s\n", gpg_strerror (rc));
       goto leave;
     }
 
-  rc = gpgsm_create_writer (&b64writer, ctrl, out_fp, &writer);
+  rc = gnupg_ksba_create_writer
+    (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0)
+                  | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)),
+     ctrl->pem_name, out_fp, &writer);
   if (rc)
     {
       log_error ("can't create writer: %s\n", gpg_strerror (rc));
@@ -301,7 +311,7 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
   audit_log (ctrl->audit, AUDIT_SETUP_READY);
 
   /* Parser loop. */
-  do 
+  do
     {
       rc = ksba_cms_parse (cms, &stopreason);
       if (rc)
@@ -316,7 +326,8 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
           int algo, mode;
           const char *algoid;
           int any_key = 0;
-          
+          int is_de_vs;        /* Computed compliance with CO_DE_VS.  */
+
           audit_log (ctrl->audit, AUDIT_GOT_DATA);
 
           algoid = ksba_cms_get_content_oid (cms, 2/* encryption algo*/);
@@ -325,7 +336,7 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
           if (!algo || !mode)
             {
               rc = gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
-              log_error ("unsupported algorithm `%s'\n", algoid? algoid:"?");
+              log_error ("unsupported algorithm '%s'\n", algoid? algoid:"?");
               if (algoid && !strcmp (algoid, "1.2.840.113549.3.2"))
                 log_info (_("(this is the RC2 algorithm)\n"));
               else if (!algoid)
@@ -347,6 +358,20 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
               goto leave;
             }
 
+          /* Check compliance.  */
+          if (! gnupg_cipher_is_allowed (opt.compliance, 0, algo, mode))
+            {
+              log_error (_("cipher algorithm '%s'"
+                           " may not be used in %s mode\n"),
+                         gcry_cipher_algo_name (algo),
+                         gnupg_compliance_option_string (opt.compliance));
+              rc = gpg_error (GPG_ERR_CIPHER_ALGO);
+              goto leave;
+            }
+
+          /* For CMS, CO_DE_VS demands CBC mode.  */
+          is_de_vs = gnupg_cipher_is_compliant (CO_DE_VS, algo, mode);
+
           audit_log_i (ctrl->audit, AUDIT_DATA_CIPHER_ALGO, algo);
           dfparm.algo = algo;
           dfparm.mode = mode;
@@ -363,7 +388,7 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
               log_error ("error getting IV: %s\n", gpg_strerror (rc));
               goto leave;
             }
-          
+
           for (recp=0; !any_key; recp++)
             {
               char *issuer;
@@ -386,7 +411,7 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
                 {
                   ksba_cert_t cert = NULL;
 
-                  log_debug ("recp %d - issuer: `%s'\n",
+                  log_debug ("recp %d - issuer: '%s'\n",
                              recp, issuer? issuer:"[NONE]");
                   log_debug ("recp %d - serial: ", recp);
                   gpgsm_dump_serial (serial);
@@ -400,7 +425,7 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
                     }
 
                   keydb_search_reset (kh);
-                  rc = keydb_search_issuer_sn (kh, issuer, serial);
+                  rc = keydb_search_issuer_sn (ctrl, kh, issuer, serial);
                   if (rc)
                     {
                       log_error ("failed to find the certificate: %s\n",
@@ -412,7 +437,7 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
                   if (rc)
                     {
                       log_error ("failed to get cert: %s\n", gpg_strerror (rc));
-                      goto oops;     
+                      goto oops;
                     }
 
                   /* Print the ENC_TO status line.  Note that we can
@@ -424,11 +449,11 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
                      keyid for later use.  */
                   {
                     unsigned long kid[2];
-                    
+
                     kid[0] = gpgsm_get_short_fingerprint (cert, kid+1);
                     snprintf (kidbuf, sizeof kidbuf, "%08lX%08lX",
                               kid[1], kid[0]);
-                    gpgsm_status2 (ctrl, STATUS_ENC_TO, 
+                    gpgsm_status2 (ctrl, STATUS_ENC_TO,
                                    kidbuf, "0", "0", NULL);
                   }
 
@@ -451,7 +476,42 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
                   hexkeygrip = gpgsm_get_keygrip_hexstring (cert);
                   desc = gpgsm_format_keydesc (cert);
 
+                  {
+                    unsigned int nbits;
+                    int pk_algo = gpgsm_get_key_algo_info (cert, &nbits);
+
+                    /* Check compliance.  */
+                    if (!gnupg_pk_is_allowed (opt.compliance,
+                                              PK_USE_DECRYPTION,
+                                              pk_algo, NULL, nbits, NULL))
+                      {
+                        char  kidstr[10+1];
+
+                        snprintf (kidstr, sizeof kidstr, "0x%08lX",
+                                  gpgsm_get_short_fingerprint (cert, NULL));
+                        log_info
+                          (_("key %s is not suitable for decryption"
+                             " in %s mode\n"),
+                           kidstr,
+                           gnupg_compliance_option_string (opt.compliance));
+                        rc = gpg_error (GPG_ERR_PUBKEY_ALGO);
+                        goto oops;
+                      }
+
+                    /* Check that all certs are compliant with CO_DE_VS.  */
+                    is_de_vs =
+                      (is_de_vs
+                       && gnupg_pk_is_compliant (CO_DE_VS, pk_algo, NULL,
+                                                 nbits, NULL));
+                  }
+
                 oops:
+                  if (rc)
+                    {
+                      /* We cannot check compliance of certs that we
+                       * don't have.  */
+                      is_de_vs = 0;
+                    }
                   xfree (issuer);
                   xfree (serial);
                   ksba_cert_release (cert);
@@ -480,6 +540,11 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
                       ksba_writer_set_filter (writer,
                                               decrypt_filter,
                                               &dfparm);
+
+                      if (is_de_vs)
+                        gpgsm_status (ctrl, STATUS_DECRYPTION_COMPLIANCE_MODE,
+                                      gnupg_status_compliance_flag (CO_DE_VS));
+
                     }
                   audit_log_ok (ctrl->audit, AUDIT_RECP_RESULT, rc);
                 }
@@ -535,7 +600,7 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
                   goto leave;
                 }
               rc = ksba_writer_write (writer,
-                                      dfparm.lastblock, 
+                                      dfparm.lastblock,
                                       dfparm.blklen - npadding);
               if (rc)
                 goto leave;
@@ -553,10 +618,10 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
         }
 
     }
-  while (stopreason != KSBA_SR_READY);   
+  while (stopreason != KSBA_SR_READY);
 
-  rc = gpgsm_finish_writer (b64writer);
-  if (rc) 
+  rc = gnupg_ksba_finish_writer (b64writer);
+  if (rc)
     {
       log_error ("write failed: %s\n", gpg_strerror (rc));
       goto leave;
@@ -573,13 +638,11 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
                  gpg_strerror (rc), gpg_strsource (rc));
     }
   ksba_cms_release (cms);
-  gpgsm_destroy_reader (b64reader);
-  gpgsm_destroy_writer (b64writer);
-  keydb_release (kh); 
+  gnupg_ksba_destroy_reader (b64reader);
+  gnupg_ksba_destroy_writer (b64writer);
+  keydb_release (kh);
   es_fclose (in_fp);
   if (dfparm.hd)
-    gcry_cipher_close (dfparm.hd); 
+    gcry_cipher_close (dfparm.hd);
   return rc;
 }
-
-