agent: Avoid appending a '\0' byte to the response of READKEY
[gnupg.git] / sm / decrypt.c
index 6e88565..60ed14a 100644 (file)
@@ -1,11 +1,11 @@
 /* decrypt.c - Decrypt a message
- *     Copyright (C) 2001 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2003, 2010 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,8 +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <unistd.h> 
+#include <unistd.h>
 #include <time.h>
 #include <assert.h>
 
+#include "gpgsm.h"
 #include <gcrypt.h>
 #include <ksba.h>
 
-#include "gpgsm.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;
   int blklen;
-  GCRY_CIPHER_HD hd;
+  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
@@ -50,36 +51,23 @@ struct decrypt_filter_parm_s {
 };
 
 
-static void
-print_integer (unsigned char *p)
-{
-  unsigned long len;
 
-  if (!p)
-    log_printf ("none");
-  else
-    {
-      len = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
-      for (p+=4; len; len--, p++)
-        log_printf ("%02X", *p);
-    }
-}
-
-/* decrypt the session key and fill in the parm structure.  The
+/* Decrypt the session key and fill in the parm structure.  The
    algo and the IV is expected to be already in PARM. */
-static int 
-prepare_decryption (const char *hexkeygrip, const char *enc_val,
+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)
 {
   char *seskey = NULL;
   size_t n, seskeylen;
   int rc;
 
-  rc = gpgsm_agent_pkdecrypt (hexkeygrip, enc_val, strlen (enc_val),
+  rc = gpgsm_agent_pkdecrypt (ctrl, hexkeygrip, desc, enc_val,
                               &seskey, &seskeylen);
   if (rc)
     {
-      log_error ("error decrypting session key: %s\n", gnupg_strerror (rc));
+      log_error ("error decrypting session key: %s\n", gpg_strerror (rc));
       goto leave;
     }
 
@@ -87,41 +75,57 @@ prepare_decryption (const char *hexkeygrip, const char *enc_val,
     log_printhex ("pkcs1 encoded session key:", seskey, seskeylen);
 
   n=0;
-  if (n + 7 > seskeylen )
+  if (seskeylen == 24 || seskeylen == 16)
     {
-      rc = seterr (Invalid_Session_Key);
-      goto leave; 
+      /* 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;
+        }
 
-  if (seskey[n] != 2 )  /* wrong block type version */
-    { 
-      rc = seterr (Invalid_Session_Key);
-      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
+         doing it the right way and therefore we have to skip the zero.  This
+         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;
+        }
 
-  for (n++; n < seskeylen && seskey[n]; n++) /* skip the random bytes */
-    ;
-  n++; /* and the zero byte */
-  if (n >= seskeylen )
-    { 
-      rc = seterr (Invalid_Session_Key);
-      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;
+        }
     }
-  
+
   if (DBG_CRYPTO)
     log_printhex ("session key:", seskey+n, seskeylen-n);
 
-  parm->hd = gcry_cipher_open (parm->algo, parm->mode, 0);
-  if (!parm->hd)
+  rc = gcry_cipher_open (&parm->hd, parm->algo, parm->mode, 0);
+  if (rc)
     {
-      rc = gcry_errno ();
-      log_error ("error creating decryptor: %s\n", gcry_strerror (rc));
-      rc = map_gcry_err (rc);
+      log_error ("error creating decryptor: %s\n", gpg_strerror (rc));
       goto leave;
     }
-                        
+
   rc = gcry_cipher_setkey (parm->hd, seskey+n, seskeylen-n);
-  if (rc == GCRYERR_WEAK_KEY)
+  if (gpg_err_code (rc) == GPG_ERR_WEAK_KEY)
     {
       log_info (_("WARNING: message was encrypted with "
                   "a weak key in the symmetric cipher.\n"));
@@ -129,8 +133,7 @@ prepare_decryption (const char *hexkeygrip, const char *enc_val,
     }
   if (rc)
     {
-      log_error("key setup failed: %s\n", gcry_strerror(rc) );
-      rc = map_gcry_err (rc);
+      log_error("key setup failed: %s\n", gpg_strerror(rc) );
       goto leave;
     }
 
@@ -147,9 +150,9 @@ prepare_decryption (const char *hexkeygrip, const char *enc_val,
    decrypt it and store it inoutbuf which has a maximum size of
    maxoutlen.  The valid bytes in outbuf should be return in outlen.
    Due to different buffer sizes or different length of input and
-   output, it may happen that fewer bytes are process or fewer bytes
+   output, it may happen that fewer bytes are processed or fewer bytes
    are written. */
-static KsbaError  
+static gpg_error_t
 decrypt_filter (void *arg,
                 const void *inbuf, size_t inlen, size_t *inused,
                 void *outbuf, size_t maxoutlen, size_t *outlen)
@@ -160,11 +163,11 @@ decrypt_filter (void *arg,
 
   /* fixme: Should we issue an error when we have not seen one full block? */
   if (!inlen)
-    return KSBA_Bug;
+    return gpg_error (GPG_ERR_BUG);
 
   if (maxoutlen < 2*parm->blklen)
-    return KSBA_Bug;
-  /* make some space becuase we will later need an extra block at the end */
+    return gpg_error (GPG_ERR_BUG);
+  /* Make some space because we will later need an extra block at the end.  */
   maxoutlen -= blklen;
 
   if (parm->helpblocklen)
@@ -175,7 +178,7 @@ decrypt_filter (void *arg,
         parm->helpblock[i] = ((const char*)inbuf)[j];
       inlen -= j;
       if (blklen > maxoutlen)
-        return KSBA_Bug;
+        return gpg_error (GPG_ERR_BUG);
       if (i < blklen)
         {
           parm->helpblocklen = i;
@@ -238,78 +241,82 @@ decrypt_filter (void *arg,
 \f
 /* Perform a decrypt operation.  */
 int
-gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *out_fp)
+gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
 {
   int rc;
-  KsbaError err;
-  Base64Context b64reader = NULL;
-  Base64Context b64writer = NULL;
-  KsbaReader reader;
-  KsbaWriter writer;
-  KsbaCMS cms = NULL;
-  KsbaStopReason stopreason;
+  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;
+  ksba_stop_reason_t stopreason;
   KEYDB_HANDLE kh;
   int recp;
-  FILE *in_fp = NULL;
+  estream_t in_fp = NULL;
   struct decrypt_filter_parm_s dfparm;
 
   memset (&dfparm, 0, sizeof dfparm);
 
-  kh = keydb_new (0);
+  audit_set_type (ctrl->audit, AUDIT_TYPE_DECRYPT);
+
+  kh = keydb_new ();
   if (!kh)
     {
-      log_error (_("failed to allocated keyDB handle\n"));
-      rc = GNUPG_General_Error;
+      log_error (_("failed to allocate keyDB handle\n"));
+      rc = gpg_error (GPG_ERR_GENERAL);
       goto leave;
     }
 
-
-  in_fp = fdopen ( dup (in_fd), "rb");
+  in_fp = es_fdopen_nc (in_fd, "rb");
   if (!in_fp)
     {
+      rc = gpg_error_from_syserror ();
       log_error ("fdopen() failed: %s\n", strerror (errno));
-      rc = seterr (IO_Error);
       goto leave;
     }
 
-  rc = gpgsm_create_reader (&b64reader, ctrl, in_fp, &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", gnupg_strerror (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", gnupg_strerror (rc));
+      log_error ("can't create writer: %s\n", gpg_strerror (rc));
       goto leave;
     }
 
-  cms = ksba_cms_new ();
-  if (!cms)
-    {
-      rc = seterr (Out_Of_Core);
-      goto leave;
-    }
+  rc = ksba_cms_new (&cms);
+  if (rc)
+    goto leave;
 
-  err = ksba_cms_set_reader_writer (cms, reader, writer);
-  if (err)
+  rc = ksba_cms_set_reader_writer (cms, reader, writer);
+  if (rc)
     {
       log_debug ("ksba_cms_set_reader_writer failed: %s\n",
-                 ksba_strerror (err));
-      rc = map_ksba_err (err);
+                 gpg_strerror (rc));
       goto leave;
     }
 
-  /* parser loop */
-  do 
+  audit_log (ctrl->audit, AUDIT_SETUP_READY);
+
+  /* Parser loop. */
+  do
     {
-      err = ksba_cms_parse (cms, &stopreason);
-      if (err)
+      rc = ksba_cms_parse (cms, &stopreason);
+      if (rc)
         {
-          log_debug ("ksba_cms_parse failed: %s\n", ksba_strerror (err));
-          rc = map_ksba_err (err);
+          log_debug ("ksba_cms_parse failed: %s\n", gpg_strerror (rc));
           goto leave;
         }
 
@@ -318,21 +325,59 @@ gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *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*/);
           algo = gcry_cipher_map_name (algoid);
           mode = gcry_cipher_mode_from_oid (algoid);
           if (!algo || !mode)
             {
-              log_error ("unsupported algorithm `%s'\n", algoid? algoid:"?");
-              rc = GNUPG_Unsupported_Algorithm;
+              rc = gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+              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)
+                log_info (_("(this does not seem to be an encrypted"
+                            " message)\n"));
+              {
+                char numbuf[50];
+                sprintf (numbuf, "%d", rc);
+                gpgsm_status2 (ctrl, STATUS_ERROR, "decrypt.algorithm",
+                               numbuf, algoid?algoid:"?", NULL);
+                audit_log_s (ctrl->audit, AUDIT_BAD_DATA_CIPHER_ALGO, algoid);
+              }
+
+              /* If it seems that this is not an encrypted message we
+                 return a more sensible error code. */
+              if (!algoid)
+                rc = gpg_error (GPG_ERR_NO_DATA);
+
+              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;
           dfparm.blklen = gcry_cipher_get_algo_blklen (algo);
           if (dfparm.blklen > sizeof (dfparm.helpblock))
-            return GNUPG_Bug;
+            return gpg_error (GPG_ERR_BUG);
 
           rc = ksba_cms_get_content_enc_iv (cms,
                                             dfparm.iv,
@@ -340,76 +385,206 @@ gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *out_fp)
                                             &dfparm.ivlen);
           if (rc)
             {
-              log_error ("error getting IV: %s\n", ksba_strerror (err));
-              rc = map_ksba_err (err);
+              log_error ("error getting IV: %s\n", gpg_strerror (rc));
               goto leave;
             }
-          
-          for (recp=0; recp < 1; recp++)
+
+          for (recp=0; !any_key; recp++)
             {
               char *issuer;
-              unsigned char *serial;
-              char *enc_val;
+              ksba_sexp_t serial;
+              ksba_sexp_t enc_val;
               char *hexkeygrip = NULL;
+              char *desc = NULL;
+              char kidbuf[16+1];
+
+              *kidbuf = 0;
 
-              err = ksba_cms_get_issuer_serial (cms, recp, &issuer, &serial);
-              if (err)
+              rc = ksba_cms_get_issuer_serial (cms, recp, &issuer, &serial);
+              if (rc == -1 && recp)
+                break; /* no more recipients */
+              audit_log_i (ctrl->audit, AUDIT_NEW_RECP, recp);
+              if (rc)
                 log_error ("recp %d - error getting info: %s\n",
-                           recp, ksba_strerror (err));
+                           recp, gpg_strerror (rc));
               else
                 {
-                  KsbaCert cert = NULL;
+                  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);
-                  print_integer (serial);
+                  gpgsm_dump_serial (serial);
                   log_printf ("\n");
 
+                  if (ctrl->audit)
+                    {
+                      char *tmpstr = gpgsm_format_sn_issuer (serial, issuer);
+                      audit_log_s (ctrl->audit, AUDIT_RECP_NAME, tmpstr);
+                      xfree (tmpstr);
+                    }
+
                   keydb_search_reset (kh);
-                  rc = keydb_search_issuer_sn (kh, issuer, serial);
+                  rc = keydb_search_issuer_sn (ctrl, kh, issuer, serial);
                   if (rc)
                     {
-                      log_debug ("failed to find the certificate: %s\n",
-                                 gnupg_strerror(rc));
+                      log_error ("failed to find the certificate: %s\n",
+                                 gpg_strerror(rc));
                       goto oops;
                     }
 
                   rc = keydb_get_cert (kh, &cert);
                   if (rc)
                     {
-                      log_debug ("failed to get cert: %s\n", gnupg_strerror (rc));
-                      goto oops;                    }
+                      log_error ("failed to get cert: %s\n", gpg_strerror (rc));
+                      goto oops;
+                    }
+
+                  /* Print the ENC_TO status line.  Note that we can
+                     do so only if we have the certificate.  This is
+                     in contrast to gpg where the keyID is commonly
+                     included in the encrypted messages. It is too
+                     cumbersome to retrieve the used algorithm, thus
+                     we don't print it for now.  We also record the
+                     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,
+                                   kidbuf, "0", "0", NULL);
+                  }
+
+                  /* Put the certificate into the audit log.  */
+                  audit_log_cert (ctrl->audit, AUDIT_SAVE_CERT, cert, 0);
+
+                  /* Just in case there is a problem with the own
+                     certificate we print this message - should never
+                     happen of course */
+                  rc = gpgsm_cert_use_decrypt_p (cert);
+                  if (rc)
+                    {
+                      char numbuf[50];
+                      sprintf (numbuf, "%d", rc);
+                      gpgsm_status2 (ctrl, STATUS_ERROR, "decrypt.keyusage",
+                                     numbuf, NULL);
+                      rc = 0;
+                    }
 
                   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);
                 }
 
-              enc_val = ksba_cms_get_enc_val (cms, recp);
-              if (!enc_val)
+              if (!hexkeygrip)
+                ;
+              else if (!(enc_val = ksba_cms_get_enc_val (cms, recp)))
                 log_error ("recp %d - error getting encrypted session key\n",
                            recp);
               else
                 {
-                  log_debug ("recp %d - enc-val: `%s'\n",
-                             recp, enc_val);
-                  rc = prepare_decryption (hexkeygrip, enc_val,
-                                           &dfparm);
+                  rc = prepare_decryption (ctrl,
+                                           hexkeygrip, desc, enc_val, &dfparm);
                   xfree (enc_val);
                   if (rc)
-                    log_error ("decrypting session key failed: %s\n",
-                               gnupg_strerror (rc));
+                    {
+                      log_info ("decrypting session key failed: %s\n",
+                                gpg_strerror (rc));
+                      if (gpg_err_code (rc) == GPG_ERR_NO_SECKEY && *kidbuf)
+                        gpgsm_status2 (ctrl, STATUS_NO_SECKEY, kidbuf, NULL);
+                    }
                   else
                     { /* setup the bulk decrypter */
+                      any_key = 1;
                       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);
                 }
+              xfree (hexkeygrip);
+              xfree (desc);
+            }
+
+          /* If we write an audit log add the unused recipients to the
+             log as well.  */
+          if (ctrl->audit && any_key)
+            {
+              for (;; recp++)
+                {
+                  char *issuer;
+                  ksba_sexp_t serial;
+                  int tmp_rc;
+
+                  tmp_rc = ksba_cms_get_issuer_serial (cms, recp,
+                                                       &issuer, &serial);
+                  if (tmp_rc == -1)
+                    break; /* no more recipients */
+                  audit_log_i (ctrl->audit, AUDIT_NEW_RECP, recp);
+                  if (tmp_rc)
+                    log_error ("recp %d - error getting info: %s\n",
+                               recp, gpg_strerror (rc));
+                  else
+                    {
+                      char *tmpstr = gpgsm_format_sn_issuer (serial, issuer);
+                      audit_log_s (ctrl->audit, AUDIT_RECP_NAME, tmpstr);
+                      xfree (tmpstr);
+                      xfree (issuer);
+                      xfree (serial);
+                    }
+                }
+            }
+
+          if (!any_key)
+            {
+              rc = gpg_error (GPG_ERR_NO_SECKEY);
+              goto leave;
             }
         }
       else if (stopreason == KSBA_SR_END_DATA)
@@ -421,23 +596,21 @@ gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *out_fp)
               if (!npadding || npadding > dfparm.blklen)
                 {
                   log_error ("invalid padding with value %d\n", npadding);
-                  rc = seterr (Invalid_Data);
+                  rc = gpg_error (GPG_ERR_INV_DATA);
                   goto leave;
                 }
               rc = ksba_writer_write (writer,
-                                      dfparm.lastblock, 
+                                      dfparm.lastblock,
                                       dfparm.blklen - npadding);
               if (rc)
-                {
-                  rc = map_ksba_err (rc);
-                  goto leave;
-                }
+                goto leave;
+
               for (i=dfparm.blklen - npadding; i < dfparm.blklen; i++)
                 {
                   if (dfparm.lastblock[i] != npadding)
                     {
                       log_error ("inconsistent padding\n");
-                      rc = seterr (Invalid_Data);
+                      rc = gpg_error (GPG_ERR_INV_DATA);
                       goto leave;
                     }
                 }
@@ -445,26 +618,31 @@ gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *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", gnupg_strerror (rc));
+      log_error ("write failed: %s\n", gpg_strerror (rc));
       goto leave;
     }
+  gpgsm_status (ctrl, STATUS_DECRYPTION_OKAY, NULL);
 
 
  leave:
+  audit_log_ok (ctrl->audit, AUDIT_DECRYPTION_RESULT, rc);
+  if (rc)
+    {
+      gpgsm_status (ctrl, STATUS_DECRYPTION_FAILED, NULL);
+      log_error ("message decryption failed: %s <%s>\n",
+                 gpg_strerror (rc), gpg_strsource (rc));
+    }
   ksba_cms_release (cms);
-  gpgsm_destroy_reader (b64reader);
-  gpgsm_destroy_writer (b64writer);
-  keydb_release (kh); 
-  if (in_fp)
-    fclose (in_fp);
+  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;
 }
-
-