With --enable-gpg the keyservers are now build and a first test using gpg2
[gnupg.git] / sm / sign.c
index 061dfee..d656825 100644 (file)
--- a/sm/sign.c
+++ b/sm/sign.c
@@ -1,5 +1,5 @@
 /* sign.c - Sign a message
- *     Copyright (C) 2001, 2002 Free Software Foundation, Inc.
+ *     Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -15,7 +15,8 @@
  *
  * 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.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_data (int fd, gcry_md_hd_t md)
 {
   FILE *fp;
   char buffer[4096];
@@ -61,9 +62,9 @@ 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, ksba_writer_t writer)
 {
-  KsbaError err;
+  gpg_error_t err;
   FILE *fp;
   char buffer[4096];
   int nread;
@@ -73,8 +74,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 
@@ -87,34 +89,34 @@ hash_and_copy_data (int fd, GCRY_MD_HD md, KsbaWriter writer)
           err = ksba_writer_write_octet_string (writer, buffer, nread, 0);
           if (err)
             {
-              log_error ("write failed: %s\n", ksba_strerror (err));
-              rc = map_ksba_err (err);
+              log_error ("write failed: %s\n", gpg_strerror (err));
+              rc = err;
             }
         }
     }
   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)
     {
       err = ksba_writer_write_octet_string (writer, NULL, 0, 1);
       if (err)
         {
-          log_error ("write failed: %s\n", ksba_strerror (err));
-          rc = map_ksba_err (err);
+          log_error ("write failed: %s\n", gpg_strerror (err));
+          rc = err;
         }
     }
 
@@ -123,18 +125,18 @@ hash_and_copy_data (int fd, GCRY_MD_HD md, KsbaWriter writer)
 
 
 /* Get the default certificate which is defined as the first one our
-   keyDB retruns and has a secret key available */
+   keyDB returns and has a secret key available. */
 int
-gpgsm_get_default_cert (KsbaCert *r_cert)
+gpgsm_get_default_cert (ctrl_t ctrl, ksba_cert_t *r_cert)
 {
   KEYDB_HANDLE hd;
-  KsbaCert cert = NULL;
+  ksba_cert_t cert = NULL;
   int rc;
   char *p;
 
   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 +149,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;
         }
@@ -155,7 +157,7 @@ gpgsm_get_default_cert (KsbaCert *r_cert)
       p = gpgsm_get_keygrip_hexstring (cert);
       if (p)
         {
-          if (!gpgsm_agent_havekey (p))
+          if (!gpgsm_agent_havekey (ctrl, p))
             {
               xfree (p);
               keydb_release (hd);
@@ -170,7 +172,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);
@@ -178,22 +180,22 @@ gpgsm_get_default_cert (KsbaCert *r_cert)
 }
 
 
-static KsbaCert
-get_default_signer (void)
+static ksba_cert_t
+get_default_signer (ctrl_t ctrl)
 {
   KEYDB_SEARCH_DESC desc;
-  KsbaCert cert = NULL;
+  ksba_cert_t cert = NULL;
   KEYDB_HANDLE kh = NULL;
   int rc;
 
   if (!opt.local_user)
     {
-      rc = gpgsm_get_default_cert (&cert);
+      rc = gpgsm_get_default_cert (ctrl, &cert);
       if (rc)
         {
           if (rc != -1)
             log_debug ("failed to find default certificate: %s\n",
-                       gnupg_strerror (rc));
+                       gpg_strerror (rc));
           return NULL;
         }
       return cert;
@@ -202,7 +204,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;
     }
 
@@ -232,17 +234,18 @@ get_default_signer (void)
    other certificate up in the chain to the Root-CA to the CMS
    object. */
 static int 
-add_certificate_list (CTRL ctrl, KsbaCMS cms, KsbaCert cert)
+add_certificate_list (CTRL ctrl, ksba_cms_t cms, ksba_cert_t cert)
 {
-  KsbaError err;
+  gpg_error_t err;
   int rc = 0;
-  KsbaCert next = NULL;
+  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;
@@ -251,6 +254,8 @@ add_certificate_list (CTRL ctrl, KsbaCMS cms, KsbaCert cert)
   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))
@@ -259,7 +264,12 @@ add_certificate_list (CTRL ctrl, KsbaCMS cms, KsbaCert cert)
         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 (cert, &next)) )
     {
       if (not_root && gpgsm_is_root_cert (next))
@@ -277,8 +287,8 @@ add_certificate_list (CTRL ctrl, KsbaCMS cms, KsbaCert cert)
 
  ksba_failure:
   ksba_cert_release (cert);
-  log_error ("ksba_cms_add_cert failed: %s\n", ksba_strerror (err));
-  return map_ksba_err (err);
+  log_error ("ksba_cms_add_cert failed: %s\n", gpg_strerror (err));
+  return err;
 }
 
 
@@ -295,17 +305,17 @@ gpgsm_sign (CTRL ctrl, CERTLIST signerlist,
             int data_fd, int detached, FILE *out_fp)
 {
   int i, rc;
-  KsbaError err;
+  gpg_error_t err;
   Base64Context b64writer = NULL;
-  KsbaWriter writer;
-  KsbaCMS cms = NULL;
-  KsbaStopReason stopreason;
+  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;
-  time_t signed_at;
+  ksba_isotime_t signed_at;
   CERTLIST cl;
   int release_signerlist = 0;
 
@@ -313,7 +323,7 @@ gpgsm_sign (CTRL ctrl, CERTLIST signerlist,
   if (!kh)
     {
       log_error (_("failed to allocated keyDB handle\n"));
-      rc = GNUPG_General_Error;
+      rc = gpg_error (GPG_ERR_GENERAL);
       goto leave;
     }
 
@@ -321,14 +331,14 @@ gpgsm_sign (CTRL ctrl, CERTLIST signerlist,
   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;
     }
 
@@ -336,8 +346,8 @@ gpgsm_sign (CTRL ctrl, CERTLIST signerlist,
   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;
     }
 
@@ -348,25 +358,36 @@ gpgsm_sign (CTRL ctrl, CERTLIST signerlist,
   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 a default one. */
   if (!signerlist)
     {
-      KsbaCert cert = get_default_signer ();
+      ksba_cert_t cert = get_default_signer (ctrl);
       if (!cert)
         {
           log_error ("no default signer found\n");
-          rc = seterr (General_Error);
+          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 is usable and
+         valid. */
+      rc = gpgsm_cert_use_sign_p (cert);
+      if (!rc)
+        rc = gpgsm_validate_chain (ctrl, cert, NULL, 0, NULL, 0);
+      if (rc)
+        goto leave;
+
+      /* That one is fine - create signerlist. */
       signerlist = xtrycalloc (1, sizeof *signerlist);
       if (!signerlist)
         {
-          rc = GNUPG_Out_Of_Core;
+          rc = OUT_OF_CORE (errno);
           ksba_cert_release (cert);
           goto leave;
         }
@@ -385,15 +406,15 @@ gpgsm_sign (CTRL ctrl, CERTLIST signerlist,
       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);
+          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",
-                     gnupg_strerror(rc));
+                     gpg_strerror(rc));
           goto leave;
         }
       /* Set the hash algorithm we are going to use */
@@ -401,18 +422,46 @@ gpgsm_sign (CTRL ctrl, CERTLIST signerlist,
       if (err)
         {
           log_debug ("ksba_cms_add_digest_algo failed: %s\n",
-                     ksba_strerror (err));
-          rc = map_ksba_err (err);
+                     gpg_strerror (err));
+          rc = err;
+          goto leave;
+        }
+    }
+
+
+  /* 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. */
+  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)
+  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)
@@ -424,7 +473,11 @@ gpgsm_sign (CTRL ctrl, CERTLIST signerlist,
       if (!algo)
         {
           log_error ("unknown hash algorithm `%s'\n", algoid? algoid:"?");
-          rc = GNUPG_Bug;
+          if (algoid
+              && (  !strcmp (algoid, "1.2.840.113549.1.1.2")
+                    ||!strcmp (algoid, "1.2.840.113549.2.2")))
+            log_info (_("(this is the MD2 algorithm)\n"));
+          rc = gpg_error (GPG_ERR_BUG);
           goto leave;
         }
       gcry_md_enable (data_md, algo);
@@ -437,8 +490,8 @@ gpgsm_sign (CTRL ctrl, CERTLIST signerlist,
       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 */
+         the signer's certificate - does not make mich sense, but 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);
@@ -446,7 +499,7 @@ gpgsm_sign (CTRL ctrl, CERTLIST signerlist,
       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;
         }
       for (cl=signerlist,signer=0; cl; cl = cl->next, signer++)
@@ -455,33 +508,52 @@ gpgsm_sign (CTRL ctrl, CERTLIST signerlist,
           if (err)
             {
               log_error ("ksba_cms_set_message_digest failed: %s\n",
-                         ksba_strerror (err));
-              rc = map_ksba_err (err);
+                         gpg_strerror (err));
+              rc = err;
               goto leave;
             }
         }
     }
 
-  signed_at = gnupg_get_time ();
+  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_signing_time failed: %s\n",
-                     ksba_strerror (err));
-          rc = map_ksba_err (err);
+                     gpg_strerror (err));
+          rc = err;
           goto leave;
         }
     }
 
+  /* 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_add_smime_capability failed: %s\n",
+                 gpg_strerror (err));
+      goto leave;
+    }
+
+
+  /* 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;
         }
 
@@ -505,7 +577,7 @@ gpgsm_sign (CTRL ctrl, CERTLIST signerlist,
           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;
             }
           for (cl=signerlist,signer=0; cl; cl = cl->next, signer++)
@@ -515,30 +587,29 @@ gpgsm_sign (CTRL ctrl, CERTLIST signerlist,
               if (err)
                 {
                   log_error ("ksba_cms_set_message_digest failed: %s\n",
-                             ksba_strerror (err));
-                  rc = map_ksba_err (err);
+                             gpg_strerror (err));
+                  rc = 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;
-          md = gcry_md_open (algo, 0);
-          if (DBG_HASHING)
-            gcry_md_start_debug (md, "sign.attr");
-
-          if (!md)
+          rc = gcry_md_open (&md, algo, 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);
           for (cl=signerlist,signer=0; cl; cl = cl->next, signer++)
             {
-              char *sigval = NULL;
+              unsigned char *sigval = NULL;
               char *buf, *fpr;
 
               if (signer)
@@ -547,12 +618,13 @@ gpgsm_sign (CTRL ctrl, CERTLIST signerlist,
               if (rc)
                 {
                   log_debug ("hashing signed attrs failed: %s\n",
-                             ksba_strerror (rc));
+                             gpg_strerror (rc));
                   gcry_md_close (md);
                   goto leave;
                 }
             
-              rc = gpgsm_create_cms_signature (cl->cert, md, algo, &sigval);
+              rc = gpgsm_create_cms_signature (ctrl, cl->cert,
+                                               md, algo, &sigval);
               if (rc)
                 {
                   gcry_md_close (md);
@@ -564,8 +636,8 @@ gpgsm_sign (CTRL ctrl, CERTLIST signerlist,
               if (err)
                 {
                   log_error ("failed to store the signature: %s\n",
-                             ksba_strerror (err));
-                  rc = map_ksba_err (err);
+                             gpg_strerror (err));
+                  rc = err;
                   gcry_md_close (md);
                   goto leave;
                 }
@@ -574,20 +646,23 @@ gpgsm_sign (CTRL ctrl, CERTLIST signerlist,
               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);
+              {
+                int pkalgo = gpgsm_get_key_algo_info (cl->cert, NULL);
+                rc = asprintf (&buf, "%c %d %d 00 %s %s",
+                               detached? 'D':'S',
+                               pkalgo, 
+                               algo, 
+                               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;
                 }
@@ -604,7 +679,7 @@ gpgsm_sign (CTRL ctrl, CERTLIST signerlist,
   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;
     }
 
@@ -612,6 +687,9 @@ gpgsm_sign (CTRL ctrl, CERTLIST signerlist,
 
 
  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);