Prepare for 1.5.0beta2
[gnupg.git] / sm / sign.c
index 6ea0ffe..0f83db6 100644 (file)
--- a/sm/sign.c
+++ b/sm/sign.c
@@ -1,11 +1,12 @@
 /* sign.c - Sign a message
- *     Copyright (C) 2001 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2002, 2003, 2008,
+ *               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 +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * along with this program; if not, see <http://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"
 
 
-static void
-hash_data (int fd, GCRY_MD_HD md)
+/* Hash the data and return if something was hashed.  Return -1 on error.  */
+static int
+hash_data (int fd, gcry_md_hd_t md)
 {
-  FILE *fp;
+  estream_t fp;
   char buffer[4096];
   int nread;
+  int rc = 0;
 
-  fp = fdopen ( dup (fd), "rb");
+  fp = es_fdopen_nc (fd, "rb");
   if (!fp)
     {
       log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno));
-      return;
+      return -1;
     }
 
-  do 
+  do
     {
-      nread = fread (buffer, 1, DIM(buffer), fp);
+      nread = es_fread (buffer, 1, DIM(buffer), fp);
       gcry_md_write (md, buffer, nread);
     }
   while (nread);
-  if (ferror (fp))
+  if (es_ferror (fp))
+    {
+      log_error ("read error on fd %d: %s\n", fd, strerror (errno));
+      rc = -1;
+    }
+  es_fclose (fp);
+  return rc;
+}
+
+
+static int
+hash_and_copy_data (int fd, gcry_md_hd_t md, ksba_writer_t writer)
+{
+  gpg_error_t err;
+  estream_t fp;
+  char buffer[4096];
+  int nread;
+  int rc = 0;
+  int any = 0;
+
+  fp = es_fdopen_nc (fd, "rb");
+  if (!fp)
+    {
+      gpg_error_t tmperr = gpg_error_from_syserror ();
+      log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno));
+      return tmperr;
+    }
+
+  do
+    {
+      nread = es_fread (buffer, 1, DIM(buffer), fp);
+      if (nread)
+        {
+          any = 1;
+          gcry_md_write (md, buffer, nread);
+          err = ksba_writer_write_octet_string (writer, buffer, nread, 0);
+          if (err)
+            {
+              log_error ("write failed: %s\n", gpg_strerror (err));
+              rc = err;
+            }
+        }
+    }
+  while (nread && !rc);
+  if (es_ferror (fp))
+    {
+      rc = gpg_error_from_syserror ();
       log_error ("read error on fd %d: %s\n", fd, strerror (errno));
-  fclose (fp);
+    }
+  es_fclose (fp);
+  if (!any)
+    {
+      /* We can't allow to sign an empty message because it does not
+         make much sense and more seriously, ksba_cms_build has
+         already written the tag for data and now expects an octet
+         string and an octet string of size 0 is illegal.  */
+      log_error ("cannot sign an empty message\n");
+      rc = gpg_error (GPG_ERR_NO_DATA);
+    }
+  if (!rc)
+    {
+      err = ksba_writer_write_octet_string (writer, NULL, 0, 1);
+      if (err)
+        {
+          log_error ("write failed: %s\n", gpg_strerror (err));
+          rc = err;
+        }
+    }
+
+  return rc;
 }
 
 
-static KsbaCert
-get_default_signer (void)
+/* Get the default certificate which is defined as the first
+   certificate capable of signing returned by the keyDB and has a
+   secret key available. */
+int
+gpgsm_get_default_cert (ctrl_t ctrl, ksba_cert_t *r_cert)
 {
-  //  const char key[] = "1.2.840.113549.1.9.1=#7472757374407765622E6465#,CN=WEB.DE TrustCenter,OU=TrustCenter,O=WEB.DE AG,L=D-76227 Karlsruhe,C=DE";
-  const char key[] =
-    "CN=test cert 1,OU=Aegypten Project,O=g10 Code GmbH,L=#44FC7373656C646F7266#,C=DE";
+  KEYDB_HANDLE hd;
+  ksba_cert_t cert = NULL;
+  int rc;
+  char *p;
+
+  hd = keydb_new (0);
+  if (!hd)
+    return gpg_error (GPG_ERR_GENERAL);
+  rc = keydb_search_first (hd);
+  if (rc)
+    {
+      keydb_release (hd);
+      return rc;
+    }
+
+  do
+    {
+      rc = keydb_get_cert (hd, &cert);
+      if (rc)
+        {
+          log_error ("keydb_get_cert failed: %s\n", gpg_strerror (rc));
+          keydb_release (hd);
+          return rc;
+        }
 
-  KsbaCert cert = NULL;
+      if (!gpgsm_cert_use_sign_p (cert))
+        {
+          p = gpgsm_get_keygrip_hexstring (cert);
+          if (p)
+            {
+              if (!gpgsm_agent_havekey (ctrl, p))
+                {
+                  xfree (p);
+                  keydb_release (hd);
+                  *r_cert = cert;
+                  return 0; /* got it */
+                }
+              xfree (p);
+            }
+        }
+
+      ksba_cert_release (cert);
+      cert = NULL;
+    }
+  while (!(rc = keydb_search_next (hd)));
+  if (rc && rc != -1)
+    log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc));
+
+  ksba_cert_release (cert);
+  keydb_release (hd);
+  return rc;
+}
+
+
+static ksba_cert_t
+get_default_signer (ctrl_t ctrl)
+{
+  KEYDB_SEARCH_DESC desc;
+  ksba_cert_t cert = NULL;
   KEYDB_HANDLE kh = NULL;
   int rc;
 
+  if (!opt.local_user)
+    {
+      rc = gpgsm_get_default_cert (ctrl, &cert);
+      if (rc)
+        {
+          if (rc != -1)
+            log_debug ("failed to find default certificate: %s\n",
+                       gpg_strerror (rc));
+          return NULL;
+        }
+      return cert;
+    }
+
+  rc = classify_user_id (opt.local_user, &desc);
+  if (rc)
+    {
+      log_error ("failed to find default signer: %s\n", gpg_strerror (rc));
+      return NULL;
+    }
+
   kh = keydb_new (0);
   if (!kh)
     return NULL;
 
-  rc = keydb_search_subject (kh, key);
+  rc = keydb_search (kh, &desc, 1);
   if (rc)
     {
       log_debug ("failed to find default certificate: rc=%d\n", rc);
     }
-  else 
+  else
     {
       rc = keydb_get_cert (kh, &cert);
       if (rc)
@@ -94,56 +240,117 @@ get_default_signer (void)
   return cert;
 }
 
+/* Depending on the options in CTRL add the certificate CERT as well as
+   other certificate up in the chain to the Root-CA to the CMS
+   object. */
+static int
+add_certificate_list (ctrl_t ctrl, ksba_cms_t cms, ksba_cert_t cert)
+{
+  gpg_error_t err;
+  int rc = 0;
+  ksba_cert_t next = NULL;
+  int n;
+  int not_root = 0;
+
+  ksba_cert_ref (cert);
+
+  n = ctrl->include_certs;
+  log_debug ("adding certificates at level %d\n", n);
+  if (n == -2)
+    {
+      not_root = 1;
+      n = -1;
+    }
+  if (n < 0 || n > 50)
+    n = 50; /* We better apply an upper bound */
+
+  /* First add my own certificate unless we don't want any certificate
+     included at all. */
+  if (n)
+    {
+      if (not_root && gpgsm_is_root_cert (cert))
+        err = 0;
+      else
+        err = ksba_cms_add_cert (cms, cert);
+      if (err)
+        goto ksba_failure;
+      if (n>0)
+        n--;
+    }
+  /* Walk the chain to include all other certificates.  Note that a -1
+     used for N makes sure that there is no limit and all certs get
+     included. */
+  while ( n-- && !(rc = gpgsm_walk_cert_chain (ctrl, cert, &next)) )
+    {
+      if (not_root && gpgsm_is_root_cert (next))
+        err = 0;
+      else
+        err = ksba_cms_add_cert (cms, next);
+      ksba_cert_release (cert);
+      cert = next; next = NULL;
+      if (err)
+        goto ksba_failure;
+    }
+  ksba_cert_release (cert);
+
+  return rc == -1? 0: rc;
+
+ ksba_failure:
+  ksba_cert_release (cert);
+  log_error ("ksba_cms_add_cert failed: %s\n", gpg_strerror (err));
+  return err;
+}
+
+
 
 \f
-/* Perform a sign operation.  
+/* Perform a sign operation.
 
-   Sign the data received on DATA-FD in embedded mode or in deatched
-   mode when DETACHED is true.  Write the signature to OUT_FP The key
-   used to sign is the default - we will extend the fucntion to take a
-   list of fingerprints in the future. */
+   Sign the data received on DATA-FD in embedded mode or in detached
+   mode when DETACHED is true.  Write the signature to OUT_FP.  The
+   keys used to sign are taken from SIGNERLIST or the default one will
+   be used if the value of this argument is NULL. */
 int
-gpgsm_sign (CTRL ctrl, int data_fd, int detached, FILE *out_fp)
+gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
+            int data_fd, int detached, estream_t out_fp)
 {
   int i, rc;
-  KsbaError err;
+  gpg_error_t err;
   Base64Context b64writer = NULL;
-  KsbaWriter writer;
-  KsbaCMS cms = NULL;
-  KsbaStopReason stopreason;
-  KsbaCert cert;
+  ksba_writer_t writer;
+  ksba_cms_t cms = NULL;
+  ksba_stop_reason_t stopreason;
   KEYDB_HANDLE kh = NULL;
-  GCRY_MD_HD data_md = NULL;
+  gcry_md_hd_t data_md = NULL;
   int signer;
   const char *algoid;
   int algo;
+  ksba_isotime_t signed_at;
+  certlist_t cl;
+  int release_signerlist = 0;
 
-  if (!detached)
-    {
-       rc = seterr (Not_Implemented);
-       goto leave;
-    }
-
+  audit_set_type (ctrl->audit, AUDIT_TYPE_SIGN);
 
   kh = keydb_new (0);
   if (!kh)
     {
       log_error (_("failed to allocated keyDB handle\n"));
-      rc = GNUPG_General_Error;
+      rc = gpg_error (GPG_ERR_GENERAL);
       goto leave;
     }
 
+  ctrl->pem_name = "SIGNED MESSAGE";
   rc = gpgsm_create_writer (&b64writer, ctrl, 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)
+  err = ksba_cms_new (&cms);
+  if (err)
     {
-      rc = seterr (Out_Of_Core);
+      rc = err;
       goto leave;
     }
 
@@ -151,8 +358,8 @@ gpgsm_sign (CTRL ctrl, int data_fd, int detached, FILE *out_fp)
   if (err)
     {
       log_debug ("ksba_cms_set_reader_writer failed: %s\n",
-                 ksba_strerror (err));
-      rc = map_ksba_err (err);
+                 gpg_strerror (err));
+      rc = err;
       goto leave;
     }
 
@@ -163,185 +370,416 @@ gpgsm_sign (CTRL ctrl, int data_fd, int detached, FILE *out_fp)
   if (err)
     {
       log_debug ("ksba_cms_set_content_type failed: %s\n",
-                 ksba_strerror (err));
-      rc = map_ksba_err (err);
+                 gpg_strerror (err));
+      rc = err;
       goto leave;
     }
 
+  /* If no list of signers is given, use the default certificate. */
+  if (!signerlist)
+    {
+      ksba_cert_t cert = get_default_signer (ctrl);
+      if (!cert)
+        {
+          log_error ("no default signer found\n");
+          gpgsm_status2 (ctrl, STATUS_INV_SGNR,
+                         get_inv_recpsgnr_code (GPG_ERR_NO_SECKEY), NULL);
+          rc = gpg_error (GPG_ERR_GENERAL);
+          goto leave;
+        }
+
+      /* Although we don't check for ambigious specification we will
+         check that the signer's certificate is usable and valid.  */
+      rc = gpgsm_cert_use_sign_p (cert);
+      if (!rc)
+        rc = gpgsm_validate_chain (ctrl, cert, "", NULL, 0, NULL, 0, NULL);
+      if (rc)
+        {
+          char *tmpfpr;
+
+          tmpfpr = gpgsm_get_fingerprint_hexstring (cert, 0);
+          gpgsm_status2 (ctrl, STATUS_INV_SGNR,
+                         get_inv_recpsgnr_code (rc), tmpfpr, NULL);
+          xfree (tmpfpr);
+          goto leave;
+        }
+
+      /* That one is fine - create signerlist. */
+      signerlist = xtrycalloc (1, sizeof *signerlist);
+      if (!signerlist)
+        {
+          rc = out_of_core ();
+          ksba_cert_release (cert);
+          goto leave;
+        }
+      signerlist->cert = cert;
+      release_signerlist = 1;
+    }
+
+  /* Figure out the hash algorithm to use. We do not want to use the
+     one for the certificate but if possible an OID for the plain
+     algorithm.  */
+  if (opt.forced_digest_algo && opt.verbose)
+    log_info ("user requested hash algorithm %d\n", opt.forced_digest_algo);
+  for (i=0, cl=signerlist; cl; cl = cl->next, i++)
+    {
+      const char *oid;
 
-  /* gather certificates of signers  and store in theCMS object */
-  /* fixme: process a list of fingerprints and store the certificate of
-     each given fingerprint */
-  cert = get_default_signer ();
-  if (!cert)
+      if (opt.forced_digest_algo)
+        {
+          oid = NULL;
+          cl->hash_algo = opt.forced_digest_algo;
+        }
+      else
+        {
+          oid = ksba_cert_get_digest_algo (cl->cert);
+          cl->hash_algo = oid ? gcry_md_map_name (oid) : 0;
+        }
+      switch (cl->hash_algo)
+        {
+        case GCRY_MD_SHA1:   oid = "1.3.14.3.2.26"; break;
+        case GCRY_MD_RMD160: oid = "1.3.36.3.2.1"; break;
+        case GCRY_MD_SHA224: oid = "2.16.840.1.101.3.4.2.4"; break;
+        case GCRY_MD_SHA256: oid = "2.16.840.1.101.3.4.2.1"; break;
+        case GCRY_MD_SHA384: oid = "2.16.840.1.101.3.4.2.2"; break;
+        case GCRY_MD_SHA512: oid = "2.16.840.1.101.3.4.2.3"; break;
+/*         case GCRY_MD_WHIRLPOOL: oid = "No OID yet"; break; */
+
+        case GCRY_MD_MD5:  /* We don't want to use MD5.  */
+        case 0:            /* No algorithm found in cert.  */
+        default:           /* Other algorithms.  */
+          log_info (_("hash algorithm %d (%s) for signer %d not supported;"
+                      " using %s\n"),
+                    cl->hash_algo, oid? oid: "?", i,
+                    gcry_md_algo_name (GCRY_MD_SHA1));
+          cl->hash_algo = GCRY_MD_SHA1;
+          oid = "1.3.14.3.2.26";
+          break;
+        }
+      cl->hash_algo_oid = oid;
+    }
+
+  if (opt.verbose)
     {
-      log_error ("no default signer found\n");
-      rc = seterr (General_Error);
-      goto leave;
+      for (i=0, cl=signerlist; cl; cl = cl->next, i++)
+        log_info (_("hash algorithm used for signer %d: %s (%s)\n"),
+                  i, gcry_md_algo_name (cl->hash_algo), cl->hash_algo_oid);
     }
-  err = ksba_cms_add_signer (cms, cert);
-  if (err)
+
+
+  /* Gather certificates of signers and store them in the CMS object. */
+  for (cl=signerlist; cl; cl = cl->next)
     {
-      log_debug ("ksba_cms_add_signer failed: %s\n",  ksba_strerror (err));
-      rc = map_ksba_err (err);
-      goto leave;
+      rc = gpgsm_cert_use_sign_p (cl->cert);
+      if (rc)
+        goto leave;
+
+      err = ksba_cms_add_signer (cms, cl->cert);
+      if (err)
+        {
+          log_error ("ksba_cms_add_signer failed: %s\n", gpg_strerror (err));
+          rc = err;
+          goto leave;
+        }
+      rc = add_certificate_list (ctrl, cms, cl->cert);
+      if (rc)
+        {
+          log_error ("failed to store list of certificates: %s\n",
+                     gpg_strerror(rc));
+          goto leave;
+        }
+      /* Set the hash algorithm we are going to use */
+      err = ksba_cms_add_digest_algo (cms, cl->hash_algo_oid);
+      if (err)
+        {
+          log_debug ("ksba_cms_add_digest_algo failed: %s\n",
+                     gpg_strerror (err));
+          rc = err;
+          goto leave;
+        }
     }
-  ksba_cert_release (cert); cert = NULL;
 
-  /* fixme: We might want to include a list of certificate which are
-     put as info into the signed data object - maybe we should add a
-     flag to ksba_cms_add_signer to decider whether this cert should
-     be send along with the signature */
-  
-  /* Set the hash algorithm we are going to use */
-  err = ksba_cms_add_digest_algo (cms, "1.3.14.3.2.26" /*SHA-1*/);
-  if (err)
+
+  /* Check whether one of the certificates is qualified.  Note that we
+     already validated the certificate and thus the user data stored
+     flag must be available. */
+  if (!opt.no_chain_validation)
     {
-      log_debug ("ksba_cms_add_digest_algo failed: %s\n", ksba_strerror (err));
-      rc = map_ksba_err (err);
-      goto leave;
+      for (cl=signerlist; cl; cl = cl->next)
+        {
+          size_t buflen;
+          char buffer[1];
+
+          err = ksba_cert_get_user_data (cl->cert, "is_qualified",
+                                         &buffer, sizeof (buffer), &buflen);
+          if (err || !buflen)
+            {
+              log_error (_("checking for qualified certificate failed: %s\n"),
+                         gpg_strerror (err));
+              rc = err;
+              goto leave;
+            }
+          if (*buffer)
+            err = gpgsm_qualified_consent (ctrl, cl->cert);
+          else
+            err = gpgsm_not_qualified_warning (ctrl, cl->cert);
+          if (err)
+            {
+              rc = err;
+              goto leave;
+            }
+        }
     }
 
-  /* Prepare hashing (actually we are figuring out what we have set above)*/
-  data_md = gcry_md_open (0, 0);
-  if (!data_md)
+  /* Prepare hashing (actually we are figuring out what we have set
+     above). */
+  rc = gcry_md_open (&data_md, 0, 0);
+  if (rc)
     {
-      rc = map_gcry_err (gcry_errno());
-      log_error ("md_open failed: %s\n", gcry_strerror (-1));
+      log_error ("md_open failed: %s\n", gpg_strerror (rc));
       goto leave;
     }
+  if (DBG_HASHING)
+    gcry_md_start_debug (data_md, "sign.data");
+
   for (i=0; (algoid=ksba_cms_get_digest_algo_list (cms, i)); i++)
     {
       algo = gcry_md_map_name (algoid);
       if (!algo)
         {
           log_error ("unknown hash algorithm `%s'\n", algoid? algoid:"?");
-          rc = GNUPG_Bug;
+          rc = gpg_error (GPG_ERR_BUG);
           goto leave;
         }
       gcry_md_enable (data_md, algo);
+      audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO, algo);
     }
 
-  signer = 0;
+  audit_log (ctrl->audit, AUDIT_SETUP_READY);
+
   if (detached)
-    { /* we hash the data right now so that we can store the message
+    { /* We hash the data right now so that we can store the message
          digest.  ksba_cms_build() takes this as an flag that detached
          data is expected. */
       unsigned char *digest;
       size_t digest_len;
-      /* Fixme do this for all signers and get the algo to use from
-         the signer's certificate - does not make mich sense, bu we
-         should do this consistent as we have already done it above */
-      algo = GCRY_MD_SHA1; 
-      hash_data (data_fd, data_md);
-      digest = gcry_md_read (data_md, algo);
-      digest_len = gcry_md_get_algo_dlen (algo);
-      if ( !digest || !digest_len)
+
+      if (!hash_data (data_fd, data_md))
+        audit_log (ctrl->audit, AUDIT_GOT_DATA);
+      for (cl=signerlist,signer=0; cl; cl = cl->next, signer++)
         {
-          log_error ("problem getting the hash of the data\n");
-          rc = GNUPG_Bug;
-          goto leave;
+          digest = gcry_md_read (data_md, cl->hash_algo);
+          digest_len = gcry_md_get_algo_dlen (cl->hash_algo);
+          if ( !digest || !digest_len )
+            {
+              log_error ("problem getting the hash of the data\n");
+              rc = gpg_error (GPG_ERR_BUG);
+              goto leave;
+            }
+          err = ksba_cms_set_message_digest (cms, signer, digest, digest_len);
+          if (err)
+            {
+              log_error ("ksba_cms_set_message_digest failed: %s\n",
+                         gpg_strerror (err));
+              rc = err;
+              goto leave;
+            }
         }
-      err = ksba_cms_set_message_digest (cms, signer, digest, digest_len);
+    }
+
+  gnupg_get_isotime (signed_at);
+  for (cl=signerlist,signer=0; cl; cl = cl->next, signer++)
+    {
+      err = ksba_cms_set_signing_time (cms, signer, signed_at);
       if (err)
         {
-          log_error ("ksba_cms_set_message_digest failed: %s\n",
-                     ksba_strerror (err));
-          rc = map_ksba_err (err);
+          log_error ("ksba_cms_set_signing_time failed: %s\n",
+                     gpg_strerror (err));
+          rc = err;
           goto leave;
         }
     }
-#if 0
-  err = ksba_cms_set_signing_time (cms, signer, 0 /*now*/);
+
+  /* We need to write at least a minimal list of our capabilities to
+     try to convince some MUAs to use 3DES and not the crippled
+     RC2. Our list is:
+
+        aes128-CBC
+        des-EDE3-CBC
+  */
+  err = ksba_cms_add_smime_capability (cms, "2.16.840.1.101.3.4.1.2", NULL, 0);
+  if (!err)
+    err = ksba_cms_add_smime_capability (cms, "1.2.840.113549.3.7", NULL, 0);
   if (err)
     {
-      log_error ("ksba_cms_set_signing_time failed: %s\n",
-                 ksba_strerror (err));
-      rc = map_ksba_err (err);
+      log_error ("ksba_cms_add_smime_capability failed: %s\n",
+                 gpg_strerror (err));
       goto leave;
     }
-#endif
-  do 
+
+
+  /* Main building loop. */
+  do
     {
       err = ksba_cms_build (cms, &stopreason);
       if (err)
         {
-          log_debug ("ksba_cms_build failed: %s\n", ksba_strerror (err));
-          rc = map_ksba_err (err);
+          log_debug ("ksba_cms_build failed: %s\n", gpg_strerror (err));
+          rc = err;
           goto leave;
         }
 
       if (stopreason == KSBA_SR_BEGIN_DATA)
-        { /* hash the data and store the message digest */
+        {
+          /* Hash the data and store the message digest. */
+          unsigned char *digest;
+          size_t digest_len;
+
           assert (!detached);
+
+          rc = hash_and_copy_data (data_fd, data_md, writer);
+          if (rc)
+            goto leave;
+          audit_log (ctrl->audit, AUDIT_GOT_DATA);
+          for (cl=signerlist,signer=0; cl; cl = cl->next, signer++)
+            {
+              digest = gcry_md_read (data_md, cl->hash_algo);
+              digest_len = gcry_md_get_algo_dlen (cl->hash_algo);
+              if ( !digest || !digest_len )
+                {
+                  log_error ("problem getting the hash of the data\n");
+                  rc = gpg_error (GPG_ERR_BUG);
+                  goto leave;
+                }
+              err = ksba_cms_set_message_digest (cms, signer,
+                                                 digest, digest_len);
+              if (err)
+                {
+                  log_error ("ksba_cms_set_message_digest failed: %s\n",
+                             gpg_strerror (err));
+                  rc = err;
+                  goto leave;
+                }
+            }
         }
       else if (stopreason == KSBA_SR_NEED_SIG)
-        { /* calculate the signature for all signers */
-          GCRY_MD_HD md;
+        {
+          /* Compute the signature for all signers.  */
+          gcry_md_hd_t md;
 
-          algo = GCRY_MD_SHA1;
-          signer = 0;
-          md = gcry_md_open (algo, 0);
-          if (!md)
+          rc = gcry_md_open (&md, 0, 0);
+          if (rc)
             {
-              log_error ("md_open failed: %s\n", gcry_strerror (-1));
+              log_error ("md_open failed: %s\n", gpg_strerror (rc));
               goto leave;
             }
+          if (DBG_HASHING)
+            gcry_md_start_debug (md, "sign.attr");
           ksba_cms_set_hash_function (cms, HASH_FNC, md);
-          rc = ksba_cms_hash_signed_attrs (cms, signer);
-          if (rc)
+          for (cl=signerlist,signer=0; cl; cl = cl->next, signer++)
             {
-              log_debug ("hashing signed attrs failed: %s\n",
-                         ksba_strerror (rc));
-              gcry_md_close (md);
-              goto leave;
-            }
-          
-          { /* This is all an temporary hack */
-            char *sigval;
-
-            cert = get_default_signer ();
-            if (!cert)
-              {
-                log_error ("oops - failed to get cert again\n");
-                rc = seterr (General_Error);
-                goto leave;
-              }
+              unsigned char *sigval = NULL;
+              char *buf, *fpr;
 
-            sigval = NULL;
-            rc = gpgsm_create_cms_signature (cert, md, algo, &sigval);
-            if (rc)
+              audit_log_i (ctrl->audit, AUDIT_NEW_SIG, signer);
+              if (signer)
+                gcry_md_reset (md);
               {
-                ksba_cert_release (cert);
-                goto leave;
+                certlist_t cl_tmp;
+
+                for (cl_tmp=signerlist; cl_tmp; cl_tmp = cl_tmp->next)
+                  {
+                    gcry_md_enable (md, cl_tmp->hash_algo);
+                    audit_log_i (ctrl->audit, AUDIT_ATTR_HASH_ALGO,
+                                 cl_tmp->hash_algo);
+                  }
               }
 
-            err = ksba_cms_set_sig_val (cms, signer, sigval);
-            xfree (sigval);
-            if (err)
+              rc = ksba_cms_hash_signed_attrs (cms, signer);
+              if (rc)
+                {
+                  log_debug ("hashing signed attrs failed: %s\n",
+                             gpg_strerror (rc));
+                  gcry_md_close (md);
+                  goto leave;
+                }
+
+              rc = gpgsm_create_cms_signature (ctrl, cl->cert,
+                                               md, cl->hash_algo, &sigval);
+              if (rc)
+                {
+                  audit_log_cert (ctrl->audit, AUDIT_SIGNED_BY, cl->cert, rc);
+                  gcry_md_close (md);
+                  goto leave;
+                }
+
+              err = ksba_cms_set_sig_val (cms, signer, sigval);
+              xfree (sigval);
+              if (err)
+                {
+                  audit_log_cert (ctrl->audit, AUDIT_SIGNED_BY, cl->cert, err);
+                  log_error ("failed to store the signature: %s\n",
+                             gpg_strerror (err));
+                  rc = err;
+                  gcry_md_close (md);
+                  goto leave;
+                }
+
+              /* write a status message */
+              fpr = gpgsm_get_fingerprint_hexstring (cl->cert, GCRY_MD_SHA1);
+              if (!fpr)
+                {
+                  rc = gpg_error (GPG_ERR_ENOMEM);
+                  gcry_md_close (md);
+                  goto leave;
+                }
+              rc = 0;
               {
-                log_error ("failed to store the signature: %s\n",
-                           ksba_strerror (err));
-                rc = map_ksba_err (err);
-                goto leave;
+                int pkalgo = gpgsm_get_key_algo_info (cl->cert, NULL);
+                buf = xtryasprintf ("%c %d %d 00 %s %s",
+                                    detached? 'D':'S',
+                                    pkalgo,
+                                    cl->hash_algo,
+                                    signed_at,
+                                    fpr);
+                if (!buf)
+                  rc = gpg_error_from_syserror ();
               }
-          }
+              xfree (fpr);
+              if (rc)
+                {
+                  gcry_md_close (md);
+                  goto leave;
+                }
+              gpgsm_status (ctrl, STATUS_SIG_CREATED, buf);
+              xfree (buf);
+              audit_log_cert (ctrl->audit, AUDIT_SIGNED_BY, cl->cert, 0);
+            }
+          gcry_md_close (md);
         }
     }
-  while (stopreason != KSBA_SR_READY);   
+  while (stopreason != KSBA_SR_READY);
 
   rc = gpgsm_finish_writer (b64writer);
-  if (rc) 
+  if (rc)
     {
-      log_error ("write failed: %s\n", gnupg_strerror (rc));
+      log_error ("write failed: %s\n", gpg_strerror (rc));
       goto leave;
     }
+
+  audit_log (ctrl->audit, AUDIT_SIGNING_DONE);
   log_info ("signature created\n");
 
+
  leave:
+  if (rc)
+    log_error ("error creating signature: %s <%s>\n",
+               gpg_strerror (rc), gpg_strsource (rc) );
+  if (release_signerlist)
+    gpgsm_release_certlist (signerlist);
   ksba_cms_release (cms);
   gpgsm_destroy_writer (b64writer);
-  keydb_release (kh); 
+  keydb_release (kh);
   gcry_md_close (data_md);
   return rc;
 }