* keygen.c (ask_key_flags): New. (ask_algo): Call it here in --expert mode
[gnupg.git] / sm / sign.c
index 4cce9f9..0afb52b 100644 (file)
--- a/sm/sign.c
+++ b/sm/sign.c
@@ -1,5 +1,5 @@
 /* sign.c - Sign a message
- *     Copyright (C) 2001 Free Software Foundation, Inc.
+ *     Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #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_data (int fd, gcry_md_hd_t md)
 {
   FILE *fp;
   char buffer[4096];
@@ -61,7 +61,7 @@ hash_data (int fd, GCRY_MD_HD md)
 }
 
 static int
-hash_and_copy_data (int fd, GCRY_MD_HD md, KsbaWriter writer)
+hash_and_copy_data (int fd, gcry_md_hd_t md, KsbaWriter writer)
 {
   KsbaError err;
   FILE *fp;
@@ -73,8 +73,9 @@ hash_and_copy_data (int fd, GCRY_MD_HD md, KsbaWriter writer)
   fp = fdopen ( dup (fd), "rb");
   if (!fp)
     {
+      gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
       log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno));
-      return GNUPG_File_Open_Error;
+      return tmperr;
     }
 
   do 
@@ -95,18 +96,18 @@ hash_and_copy_data (int fd, GCRY_MD_HD md, KsbaWriter writer)
   while (nread && !rc);
   if (ferror (fp))
     {
+      rc = gpg_error (gpg_err_code_from_errno (errno));
       log_error ("read error on fd %d: %s\n", fd, strerror (errno));
-      rc = GNUPG_Read_Error;
     }
   fclose (fp);
   if (!any)
     {
-      /* We can't allow to sign an empty message becuase it does not
-         make mnuch sense and more seriously, ksba-cms_build has
+      /* 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 but an octet string of zeize 0 is illegal. */
       log_error ("cannot sign an empty message\n");
-      rc = GNUPG_No_Data;
+      rc = gpg_error (GPG_ERR_NO_DATA);
     }
   if (!rc)
     {
@@ -134,7 +135,7 @@ gpgsm_get_default_cert (KsbaCert *r_cert)
 
   hd = keydb_new (0);
   if (!hd)
-    return GNUPG_General_Error;
+    return gpg_error (GPG_ERR_GENERAL);
   rc = keydb_search_first (hd);
   if (rc)
     {
@@ -147,7 +148,7 @@ gpgsm_get_default_cert (KsbaCert *r_cert)
       rc = keydb_get_cert (hd, &cert);
       if (rc) 
         {
-          log_error ("keydb_get_cert failed: %s\n", gnupg_strerror (rc));
+          log_error ("keydb_get_cert failed: %s\n", gpg_strerror (rc));
           keydb_release (hd);
           return rc;
         }
@@ -170,7 +171,7 @@ gpgsm_get_default_cert (KsbaCert *r_cert)
     }
   while (!(rc = keydb_search_next (hd)));
   if (rc && rc != -1)
-    log_error ("keydb_search_next failed: %s\n", gnupg_strerror (rc));
+    log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc));
   
   ksba_cert_release (cert);
   keydb_release (hd);
@@ -193,7 +194,7 @@ get_default_signer (void)
         {
           if (rc != -1)
             log_debug ("failed to find default certificate: %s\n",
-                       gnupg_strerror (rc));
+                       gpg_strerror (rc));
           return NULL;
         }
       return cert;
@@ -202,7 +203,7 @@ get_default_signer (void)
   rc = keydb_classify_name (opt.local_user, &desc);
   if (rc)
     {
-      log_error ("failed to find default signer: %s\n", gnupg_strerror (rc));
+      log_error ("failed to find default signer: %s\n", gpg_strerror (rc));
       return NULL;
     }
 
@@ -228,8 +229,6 @@ 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. */
@@ -290,10 +289,11 @@ add_certificate_list (CTRL ctrl, KsbaCMS cms, KsbaCert cert)
 
    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
-   key used to sign is the default one - we will extend the function
-   to take a list of fingerprints in the future. */
+   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 ctrl, CERTLIST signerlist,
+            int data_fd, int detached, FILE *out_fp)
 {
   int i, rc;
   KsbaError err;
@@ -301,19 +301,20 @@ gpgsm_sign (CTRL ctrl, int data_fd, int detached, FILE *out_fp)
   KsbaWriter writer;
   KsbaCMS cms = NULL;
   KsbaStopReason stopreason;
-  KsbaCert cert = NULL;
   KEYDB_HANDLE kh = NULL;
-  GCRY_MD_HD data_md = NULL;
+  gcry_md_hd_t data_md = NULL;
   int signer;
   const char *algoid;
   int algo;
   time_t signed_at;
+  CERTLIST cl;
+  int release_signerlist = 0;
 
   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;
     }
 
@@ -321,14 +322,14 @@ gpgsm_sign (CTRL ctrl, int data_fd, int detached, FILE *out_fp)
   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)
     {
-      rc = seterr (Out_Of_Core);
+      rc = gpg_error (GPG_ERR_ENOMEM);
       goto leave;
     }
 
@@ -353,53 +354,65 @@ gpgsm_sign (CTRL ctrl, int data_fd, int detached, FILE *out_fp)
       goto leave;
     }
 
-
-  /* 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 no list of signers is given, use a default one. */
+  if (!signerlist)
     {
-      log_error ("no default signer found\n");
-      rc = seterr (General_Error);
-      goto leave;
+      KsbaCert cert = get_default_signer ();
+      if (!cert)
+        {
+          log_error ("no default signer found\n");
+          rc = gpg_error (GPG_ERR_GENERAL);
+          goto leave;
+        }
+      signerlist = xtrycalloc (1, sizeof *signerlist);
+      if (!signerlist)
+        {
+          rc = OUT_OF_CORE (errno);
+          ksba_cert_release (cert);
+          goto leave;
+        }
+      signerlist->cert = cert;
+      release_signerlist = 1;
     }
-  rc = gpgsm_cert_use_sign_p (cert);
-  if (rc)
-    goto leave;
 
-  err = ksba_cms_add_signer (cms, cert);
-  if (err)
-    {
-      log_error ("ksba_cms_add_signer failed: %s\n",  ksba_strerror (err));
-      rc = map_ksba_err (err);
-      goto leave;
-    }
-  rc = add_certificate_list (ctrl, cms, cert);
-  if (rc)
-    {
-      log_error ("failed to store list of certificates: %s\n",
-                 gnupg_strerror(rc));
-      goto leave;
-    }
-  ksba_cert_release (cert); cert = NULL;
 
-  
-  /* 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)
+  /* Gather certificates of signers and store them in the CMS object. */
+  for (cl=signerlist; cl; cl = cl->next)
     {
-      log_debug ("ksba_cms_add_digest_algo 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",  ksba_strerror (err));
+          rc = map_ksba_err (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, "1.3.14.3.2.26" /*SHA-1*/);
+      if (err)
+        {
+          log_debug ("ksba_cms_add_digest_algo failed: %s\n",
+                     ksba_strerror (err));
+          rc = map_ksba_err (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)
+  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)
@@ -411,13 +424,12 @@ gpgsm_sign (CTRL ctrl, int data_fd, int detached, FILE *out_fp)
       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);
     }
 
-  signer = 0;
   if (detached)
     { /* we hash the data right now so that we can store the message
          digest.  ksba_cms_build() takes this as an flag that detached
@@ -434,27 +446,33 @@ gpgsm_sign (CTRL ctrl, int data_fd, int detached, FILE *out_fp)
       if ( !digest || !digest_len)
         {
           log_error ("problem getting the hash of the data\n");
-          rc = GNUPG_Bug;
+          rc = gpg_error (GPG_ERR_BUG);
           goto leave;
         }
-      err = ksba_cms_set_message_digest (cms, signer, digest, digest_len);
-      if (err)
+      for (cl=signerlist,signer=0; cl; cl = cl->next, signer++)
         {
-          log_error ("ksba_cms_set_message_digest failed: %s\n",
-                     ksba_strerror (err));
-          rc = map_ksba_err (err);
-          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",
+                         ksba_strerror (err));
+              rc = map_ksba_err (err);
+              goto leave;
+            }
         }
     }
 
   signed_at = gnupg_get_time ();
-  err = ksba_cms_set_signing_time (cms, signer, signed_at);
-  if (err)
+  for (cl=signerlist,signer=0; cl; cl = cl->next, signer++)
     {
-      log_error ("ksba_cms_set_signing_time failed: %s\n",
-                 ksba_strerror (err));
-      rc = map_ksba_err (err);
-      goto leave;
+      err = ksba_cms_set_signing_time (cms, signer, signed_at);
+      if (err)
+        {
+          log_error ("ksba_cms_set_signing_time failed: %s\n",
+                     ksba_strerror (err));
+          rc = map_ksba_err (err);
+          goto leave;
+        }
     }
 
   do 
@@ -473,10 +491,10 @@ gpgsm_sign (CTRL ctrl, int data_fd, int detached, FILE *out_fp)
           size_t digest_len;
 
           assert (!detached);
-          /* 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.
-             Code is mostly duplicated above. */
+          /* Fixme: get the algo to use from the signer's certificate
+             - does not make much sense, but we should do this
+             consistent as we have already done it above.  Code is
+             mostly duplicated above. */
 
           algo = GCRY_MD_SHA1; 
           rc = hash_and_copy_data (data_fd, data_md, writer);
@@ -487,98 +505,97 @@ gpgsm_sign (CTRL ctrl, int data_fd, int detached, FILE *out_fp)
           if ( !digest || !digest_len)
             {
               log_error ("problem getting the hash of the data\n");
-              rc = GNUPG_Bug;
+              rc = gpg_error (GPG_ERR_BUG);
               goto leave;
             }
-          err = ksba_cms_set_message_digest (cms, signer, digest, digest_len);
-          if (err)
+          for (cl=signerlist,signer=0; cl; cl = cl->next, signer++)
             {
-              log_error ("ksba_cms_set_message_digest failed: %s\n",
-                         ksba_strerror (err));
-              rc = map_ksba_err (err);
-              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",
+                             ksba_strerror (err));
+                  rc = map_ksba_err (err);
+                  goto leave;
+                }
             }
         }
       else if (stopreason == KSBA_SR_NEED_SIG)
         { /* calculate the signature for all signers */
-          GCRY_MD_HD md;
+          gcry_md_hd_t md;
 
           algo = GCRY_MD_SHA1;
-          signer = 0;
-          md = gcry_md_open (algo, 0);
-          if (DBG_HASHING)
-            gcry_md_start_debug (md, "sign.attr");
-
-          if (!md)
-            {
-              log_error ("md_open failed: %s\n", gcry_strerror (-1));
-              goto leave;
-            }
-          ksba_cms_set_hash_function (cms, HASH_FNC, md);
-          rc = ksba_cms_hash_signed_attrs (cms, signer);
+          rc = gcry_md_open (&md, algo, 0);
           if (rc)
             {
-              log_debug ("hashing signed attrs failed: %s\n",
-                         ksba_strerror (rc));
-              gcry_md_close (md);
+              log_error ("md_open failed: %s\n", gpg_strerror (rc));
               goto leave;
             }
-          
-          { /* This is all an temporary hack */
-            char *sigval;
-            
-            ksba_cert_release (cert); 
-            cert = get_default_signer ();
-            if (!cert)
-              {
-                log_error ("oops - failed to get cert again\n");
-                rc = seterr (General_Error);
-                goto leave;
-              }
-
-            sigval = NULL;
-            rc = gpgsm_create_cms_signature (cert, md, algo, &sigval);
-            if (rc)
-             goto leave;
-
-            err = ksba_cms_set_sig_val (cms, signer, sigval);
-            xfree (sigval);
-            if (err)
-              {
-                log_error ("failed to store the signature: %s\n",
-                           ksba_strerror (err));
-                rc = map_ksba_err (err);
-                goto leave;
-              }
-
-            /* And write a status message */
+          if (DBG_HASHING)
+            gcry_md_start_debug (md, "sign.attr");
+          ksba_cms_set_hash_function (cms, HASH_FNC, md);
+          for (cl=signerlist,signer=0; cl; cl = cl->next, signer++)
             {
+              char *sigval = NULL;
               char *buf, *fpr;
-              
-              fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
+
+              if (signer)
+                gcry_md_reset (md);
+              rc = ksba_cms_hash_signed_attrs (cms, signer);
+              if (rc)
+                {
+                  log_debug ("hashing signed attrs failed: %s\n",
+                             ksba_strerror (rc));
+                  gcry_md_close (md);
+                  goto leave;
+                }
+            
+              rc = gpgsm_create_cms_signature (cl->cert, md, algo, &sigval);
+              if (rc)
+                {
+                  gcry_md_close (md);
+                  goto leave;
+                }
+
+              err = ksba_cms_set_sig_val (cms, signer, sigval);
+              xfree (sigval);
+              if (err)
+                {
+                  log_error ("failed to store the signature: %s\n",
+                             ksba_strerror (err));
+                  rc = map_ksba_err (err);
+                  gcry_md_close (md);
+                  goto leave;
+                }
+
+              /* write a status message */
+              fpr = gpgsm_get_fingerprint_hexstring (cl->cert, GCRY_MD_SHA1);
               if (!fpr)
                 {
-                  rc = seterr (Out_Of_Core);
+                  rc = gpg_error (GPG_ERR_ENOMEM);
+                  gcry_md_close (md);
                   goto leave;
                 }
               rc = asprintf (&buf, "%c %d %d 00 %lu %s",
-                        detached? 'D':'S',
-                        GCRY_PK_RSA,  /* FIXME: get pk algo from cert */
-                        algo, 
-                        (ulong)signed_at,
-                        fpr);
+                             detached? 'D':'S',
+                             GCRY_PK_RSA,  /* FIXME: get pk algo from cert */
+                             algo, 
+                             (ulong)signed_at,
+                             fpr);
               xfree (fpr);
               if (rc < 0)
                 {
-                  rc = seterr (Out_Of_Core);
+                  rc = gpg_error (GPG_ERR_ENOMEM);
+                  gcry_md_close (md);
                   goto leave;
                 }
               rc = 0;
-              gpgsm_status (ctrl, STATUS_SIG_CREATED, buf );
+              gpgsm_status (ctrl, STATUS_SIG_CREATED, buf);
               free (buf); /* yes, we must use the regular free() here */
             }
+          gcry_md_close (md);
 
-          }
         }
     }
   while (stopreason != KSBA_SR_READY);   
@@ -586,7 +603,7 @@ gpgsm_sign (CTRL ctrl, int data_fd, int detached, FILE *out_fp)
   rc = gpgsm_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;
     }
 
@@ -594,7 +611,8 @@ gpgsm_sign (CTRL ctrl, int data_fd, int detached, FILE *out_fp)
 
 
  leave:
-  ksba_cert_release (cert); 
+  if (release_signerlist)
+    gpgsm_release_certlist (signerlist);
   ksba_cms_release (cms);
   gpgsm_destroy_writer (b64writer);
   keydb_release (kh);