Changed the way i18n files are located under Windows. The setting of the
[gnupg.git] / sm / certreqgen.c
index 969ed14..30b8179 100644 (file)
@@ -1,11 +1,11 @@
 /* certreqgen.c - Generate a key and a certification request
- *     Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2002, 2003, 2005, 2007 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 <http://www.gnu.org/licenses/>.
  */
 
 /*
@@ -63,6 +62,9 @@ The format of the native parameter file is follows:
         algorithm is "rsa".
      Key-Length: <length-in-bits>
        Length of the key in bits.  Default is 1024.
+     Key-Grip: hexstring
+        This is optional and used to generate a request for an already
+        existing key.  Key-Length will be ignored when given,
      Key-Usage: <usage-list>
         Space or comma delimited list of key usage, allowed values are
         "encrypt" and "sign".  This is used to generate the KeyUsage extension.
@@ -71,7 +73,11 @@ The format of the native parameter file is follows:
      Name-DN: subject name
         This is the DN name of the subject in rfc2253 format.
      Name-Email: <string>
-       The ist the email address
+       The is an email address for the altSubjectName
+     Name-DNS: <string> 
+       The is an DNS name for the altSubjectName
+     Name-URI: <string> 
+       The is an URI for the altSubjectName
 
 Here is an example:
 $ cat >foo <<EOF
@@ -107,9 +113,12 @@ EOF
 enum para_name {
   pKEYTYPE,
   pKEYLENGTH,
+  pKEYGRIP,
   pKEYUSAGE,
   pNAMEDN,
-  pNAMEEMAIL
+  pNAMEEMAIL,
+  pNAMEDNS,
+  pNAMEURI
 };
 
 struct para_data_s {
@@ -137,6 +146,7 @@ static int proc_parameters (ctrl_t ctrl,
                             struct reqgen_ctrl_s *outctrl);
 static int create_request (ctrl_t ctrl,
                            struct para_data_s *para,
+                           const char *carddirect,
                            ksba_const_sexp_t public,
                            struct reqgen_ctrl_s *outctrl);
 
@@ -155,26 +165,27 @@ release_parameter_list (struct para_data_s *r)
 }
 
 static struct para_data_s *
-get_parameter (struct para_data_s *para, enum para_name key)
+get_parameter (struct para_data_s *para, enum para_name key, int seq)
 {
   struct para_data_s *r;
   
-  for (r = para; r && r->key != key; r = r->next)
-    ;
-  return r;
+  for (r = para; r ; r = r->next)
+    if ( r->key == key && !seq--)
+      return r;
+  return NULL;
 }
 
 static const char *
-get_parameter_value (struct para_data_s *para, enum para_name key)
+get_parameter_value (struct para_data_s *para, enum para_name key, int seq)
 {
-  struct para_data_s *r = get_parameter (para, key);
+  struct para_data_s *r = get_parameter (para, key, seq);
   return (r && *r->u.value)? r->u.value : NULL;
 }
 
 static int
 get_parameter_algo (struct para_data_s *para, enum para_name key)
 {
-  struct para_data_s *r = get_parameter (para, key);
+  struct para_data_s *r = get_parameter (para, key, 0);
   if (!r)
     return -1;
   if (digitp (r->u.value))
@@ -189,7 +200,7 @@ get_parameter_algo (struct para_data_s *para, enum para_name key)
 static int
 parse_parameter_usage (struct para_data_s *para, enum para_name key)
 {
-  struct para_data_s *r = get_parameter (para, key);
+  struct para_data_s *r = get_parameter (para, key, 0);
   char *p, *pn;
   unsigned int use;
   
@@ -220,7 +231,7 @@ parse_parameter_usage (struct para_data_s *para, enum para_name key)
 static unsigned int
 get_parameter_uint (struct para_data_s *para, enum para_name key)
 {
-  struct para_data_s *r = get_parameter (para, key);
+  struct para_data_s *r = get_parameter (para, key, 0);
 
   if (!r)
     return 0;
@@ -236,17 +247,21 @@ get_parameter_uint (struct para_data_s *para, enum para_name key)
 /* Read the certificate generation parameters from FP and generate
    (all) certificate requests.  */
 static int
-read_parameters (ctrl_t ctrl, FILE *fp, ksba_writer_t writer)
+read_parameters (ctrl_t ctrl, estream_t fp, ksba_writer_t writer)
 {
   static struct {
     const char *name;
     enum para_name key;
+    int allow_dups;
   } keywords[] = {
     { "Key-Type",       pKEYTYPE},
     { "Key-Length",     pKEYLENGTH },
+    { "Key-Grip",       pKEYGRIP },
     { "Key-Usage",      pKEYUSAGE },
     { "Name-DN",        pNAMEDN },
-    { "Name-Email",     pNAMEEMAIL },
+    { "Name-Email",     pNAMEEMAIL, 1 },
+    { "Name-DNS",       pNAMEDNS, 1 },
+    { "Name-URI",       pNAMEURI, 1 },
     { NULL, 0 }
   };
   char line[1024], *p;
@@ -260,7 +275,7 @@ read_parameters (ctrl_t ctrl, FILE *fp, ksba_writer_t writer)
 
   err = NULL;
   para = NULL;
-  while (fgets (line, DIM(line)-1, fp) )
+  while (es_fgets (line, DIM(line)-1, fp) )
     {
       char *keyword, *value;
 
@@ -347,7 +362,7 @@ read_parameters (ctrl_t ctrl, FILE *fp, ksba_writer_t writer)
           release_parameter_list (para);
           para = NULL;
        }
-      else
+      else if (!keywords[i].allow_dups)
         {
           for (r = para; r && r->key != keywords[i].key; r = r->next)
             ;
@@ -376,7 +391,7 @@ read_parameters (ctrl_t ctrl, FILE *fp, ksba_writer_t writer)
       log_error ("line %d: %s\n", outctrl.lnr, err);
       rc = gpg_error (GPG_ERR_GENERAL);
     }
-  else if (ferror(fp))
+  else if (es_ferror(fp))
     {
       log_error ("line %d: read error: %s\n", outctrl.lnr, strerror(errno) );
       rc = gpg_error (GPG_ERR_GENERAL);
@@ -425,6 +440,7 @@ static int
 proc_parameters (ctrl_t ctrl,
                  struct para_data_s *para, struct reqgen_ctrl_s *outctrl)
 {
+  gpg_error_t err;
   struct para_data_s *r;
   const char *s;
   int i;
@@ -433,50 +449,80 @@ proc_parameters (ctrl_t ctrl,
   unsigned char keyparms[100];
   int rc;
   ksba_sexp_t public;
-  
-  /* check that we have all required parameters */
-  assert (get_parameter (para, pKEYTYPE));
+  int seq;
+  size_t erroff, errlen;
+  char *cardkeyid = NULL;
 
-  /* We can only use RSA for now.  There is a with pkcs-10 on how to
-     use ElGamal because it is expected that a PK algorithm can always
-     be used for signing. */
+  /* Check that we have all required parameters; */
+  assert (get_parameter (para, pKEYTYPE, 0));
+
+  /* We can only use RSA for now.  There is a problem with pkcs-10 on
+     how to use ElGamal because it is expected that a PK algorithm can
+     always be used for signing. Another problem is that on-card
+     generated encryption keys may not be used for signing.  */
   i = get_parameter_algo (para, pKEYTYPE);
-  if (i < 1 || i != GCRY_PK_RSA )
+  if (!i && (s = get_parameter_value (para, pKEYTYPE, 0)) && *s)
     {
-      r = get_parameter (para, pKEYTYPE);
-      log_error ("line %d: invalid algorithm\n", r->lnr);
+      /* Hack to allow creation of certificates directly from a smart
+         card.  For example: "Key-Type: card:OPENPGP.3".  */
+      if (!strncmp (s, "card:", 5) && s[5])
+        cardkeyid = xtrystrdup (s+5);
+    }
+  if ( (i < 1 || i != GCRY_PK_RSA) && !cardkeyid )
+    {
+      r = get_parameter (para, pKEYTYPE, 0);
+      log_error (_("line %d: invalid algorithm\n"), r->lnr);
       return gpg_error (GPG_ERR_INV_PARAMETER);
     }
   
-  /* check the keylength */
-  if (!get_parameter (para, pKEYLENGTH))
+  /* Check the keylength. */
+  if (!get_parameter (para, pKEYLENGTH, 0))
     nbits = 1024;
   else
     nbits = get_parameter_uint (para, pKEYLENGTH);
-  if (nbits < 512 || nbits > 4096)
+  if ((nbits < 1024 || nbits > 4096) && !cardkeyid)
     {
-      r = get_parameter (para, pKEYTYPE);
-      log_error ("line %d: invalid key length %u (valid are 512 to 4096)\n",
-                 r->lnr, nbits);
+      /* The BSI specs dated 2002-11-25 don't allow lengths below 1024. */
+      r = get_parameter (para, pKEYLENGTH, 0);
+      log_error (_("line %d: invalid key length %u (valid are %d to %d)\n"),
+                 r->lnr, nbits, 1024, 4096);
+      xfree (cardkeyid);
       return gpg_error (GPG_ERR_INV_PARAMETER);
     }
     
-  /* check the usage */
+  /* Check the usage. */
   if (parse_parameter_usage (para, pKEYUSAGE))
-    return gpg_error (GPG_ERR_INV_PARAMETER);
+    {
+      xfree (cardkeyid);
+      return gpg_error (GPG_ERR_INV_PARAMETER);
+    }
 
-  /* check that there is a subject name and that this DN fits our
-     requirements */
-  if (!(s=get_parameter_value (para, pNAMEDN)))
+  /* Check that there is a subject name and that this DN fits our
+     requirements. */
+  if (!(s=get_parameter_value (para, pNAMEDN, 0)))
     {
-      r = get_parameter (para, pKEYTYPE);
-      log_error ("line %d: no subject name given\n", r->lnr);
+      r = get_parameter (para, pNAMEDN, 0);
+      log_error (_("line %d: no subject name given\n"), r->lnr);
+      xfree (cardkeyid);
+      return gpg_error (GPG_ERR_INV_PARAMETER);
+    }
+  err = ksba_dn_teststr (s, 0, &erroff, &errlen);
+  if (err)
+    {
+      r = get_parameter (para, pNAMEDN, 0);
+      if (gpg_err_code (err) == GPG_ERR_UNKNOWN_NAME)
+        log_error (_("line %d: invalid subject name label `%.*s'\n"),
+                   r->lnr, (int)errlen, s+erroff);
+      else
+        log_error (_("line %d: invalid subject name `%s' at pos %d\n"),
+                   r->lnr, s, (int)erroff);
+
+      xfree (cardkeyid);
       return gpg_error (GPG_ERR_INV_PARAMETER);
     }
-  /* fixme check s */
 
-  /* check that the optional email address is okay */
-  if ((s=get_parameter_value (para, pNAMEEMAIL)))
+  /* Check that the optional email address is okay. */
+  for (seq=0; (s=get_parameter_value (para, pNAMEEMAIL, seq)); seq++)
     { 
       if (has_invalid_email_chars (s)
           || *s == '@'
@@ -484,26 +530,57 @@ proc_parameters (ctrl_t ctrl,
           || s[strlen(s)-1] == '.'
           || strstr(s, ".."))
         {
-          r = get_parameter (para, pKEYTYPE);
-          log_error ("line %d: not a valid email address\n", r->lnr);
+          r = get_parameter (para, pNAMEEMAIL, seq);
+          log_error (_("line %d: not a valid email address\n"), r->lnr);
+          xfree (cardkeyid);
           return gpg_error (GPG_ERR_INV_PARAMETER);
         }
     }
 
-  sprintf (numbuf, "%u", nbits);
-  snprintf (keyparms, DIM (keyparms)-1, 
-            "(6:genkey(3:rsa(5:nbits%d:%s)))", strlen (numbuf), numbuf);
-  rc = gpgsm_agent_genkey (ctrl, keyparms, &public);
-  if (rc)
+  if (cardkeyid) /* Take the key from the current smart card. */
+    {
+      rc = gpgsm_agent_readkey (ctrl, 1, cardkeyid, &public);
+      if (rc)
+        {
+          r = get_parameter (para, pKEYTYPE, 0);
+          log_error (_("line %d: error reading key `%s' from card: %s\n"),
+                     r->lnr, cardkeyid, gpg_strerror (rc));
+          xfree (cardkeyid);
+          return rc;
+        }
+    }
+  else if ((s=get_parameter_value (para, pKEYGRIP, 0))) /* Use existing key.*/
     {
-      r = get_parameter (para, pKEYTYPE);
-      log_error ("line %d: key generation failed: %s\n",
-                 r->lnr, gpg_strerror (rc));
-      return rc;
+      rc = gpgsm_agent_readkey (ctrl, 0, s, &public);
+      if (rc)
+        {
+          r = get_parameter (para, pKEYTYPE, 0);
+          log_error (_("line %d: error getting key by keygrip `%s': %s\n"),
+                     r->lnr, s, gpg_strerror (rc));
+          xfree (cardkeyid);
+          return rc;
+        }
+    }
+  else /* Generate new key.  */
+    {
+      sprintf (numbuf, "%u", nbits);
+      snprintf ((char*)keyparms, DIM (keyparms)-1, 
+                "(6:genkey(3:rsa(5:nbits%d:%s)))",
+                (int)strlen (numbuf), numbuf);
+      rc = gpgsm_agent_genkey (ctrl, keyparms, &public);
+      if (rc)
+        {
+          r = get_parameter (para, pKEYTYPE, 0);
+          log_error (_("line %d: key generation failed: %s <%s>\n"),
+                     r->lnr, gpg_strerror (rc), gpg_strsource (rc));
+          xfree (cardkeyid);
+          return rc;
+        }
     }
 
-  rc = create_request (ctrl, para, public, outctrl);
+  rc = create_request (ctrl, para, cardkeyid, public, outctrl);
   xfree (public);
+  xfree (cardkeyid);
 
   return rc;
 }
@@ -512,8 +589,10 @@ proc_parameters (ctrl_t ctrl,
 /* Parameters are checked, the key pair has been created.  Now
    generate the request and write it out */
 static int
-create_request (ctrl_t ctrl,
-                struct para_data_s *para, ksba_const_sexp_t public,
+create_request (ctrl_t ctrl, 
+                struct para_data_s *para, 
+                const char *carddirect,
+                ksba_const_sexp_t public,
                 struct reqgen_ctrl_s *outctrl)
 {
   ksba_certreq_t cr;
@@ -523,6 +602,10 @@ create_request (ctrl_t ctrl,
   int rc = 0;
   const char *s;
   unsigned int use;
+  int seq;
+  char *buf, *p;
+  size_t len;
+  char numbuf[30];
 
   err = ksba_certreq_new (&cr);
   if (err)
@@ -540,7 +623,7 @@ create_request (ctrl_t ctrl,
   ksba_certreq_set_hash_function (cr, HASH_FNC, md);
   ksba_certreq_set_writer (cr, outctrl->writer);
   
-  err = ksba_certreq_add_subject (cr, get_parameter_value (para, pNAMEDN));
+  err = ksba_certreq_add_subject (cr, get_parameter_value (para, pNAMEDN, 0));
   if (err)
     {
       log_error ("error setting the subject's name: %s\n",
@@ -549,15 +632,12 @@ create_request (ctrl_t ctrl,
       goto leave;
     }
 
-  s = get_parameter_value (para, pNAMEEMAIL);
-  if (s)
+  for (seq=0; (s = get_parameter_value (para, pNAMEEMAIL, seq)); seq++)
     {
-      char *buf;
-
       buf = xtrymalloc (strlen (s) + 3);
       if (!buf)
         {
-          rc = OUT_OF_CORE (errno);
+          rc = out_of_core ();
           goto leave;
         }
       *buf = '<';
@@ -574,6 +654,60 @@ create_request (ctrl_t ctrl,
         }
     }
 
+  for (seq=0; (s = get_parameter_value (para, pNAMEDNS, seq)); seq++)
+    {
+      len = strlen (s);
+      assert (len);
+      snprintf (numbuf, DIM(numbuf), "%u:", (unsigned int)len);
+      buf = p = xtrymalloc (11 + strlen (numbuf) + len + 3);
+      if (!buf)
+        {
+          rc = out_of_core ();
+          goto leave;
+        }
+      p = stpcpy (p, "(8:dns-name");
+      p = stpcpy (p, numbuf);
+      p = stpcpy (p, s);
+      strcpy (p, ")");
+
+      err = ksba_certreq_add_subject (cr, buf);
+      xfree (buf);
+      if (err)
+        {
+          log_error ("error setting the subject's alternate name: %s\n",
+                     gpg_strerror (err));
+          rc = err;
+          goto leave;
+        }
+    }
+
+  for (seq=0; (s = get_parameter_value (para, pNAMEURI, seq)); seq++)
+    {
+      len = strlen (s);
+      assert (len);
+      snprintf (numbuf, DIM(numbuf), "%u:", (unsigned int)len);
+      buf = p = xtrymalloc (6 + strlen (numbuf) + len + 3);
+      if (!buf)
+        {
+          rc = out_of_core ();
+          goto leave;
+        }
+      p = stpcpy (p, "(3:uri");
+      p = stpcpy (p, numbuf);
+      p = stpcpy (p, s);
+      strcpy (p, ")");
+
+      err = ksba_certreq_add_subject (cr, buf);
+      xfree (buf);
+      if (err)
+        {
+          log_error ("error setting the subject's alternate name: %s\n",
+                     gpg_strerror (err));
+          rc = err;
+          goto leave;
+        }
+    }
+
 
   err = ksba_certreq_set_public_key (cr, public);
   if (err)
@@ -626,8 +760,9 @@ create_request (ctrl_t ctrl,
         {
           gcry_sexp_t s_pkey;
           size_t n;
-          unsigned char grip[20], hexgrip[41];
-          char *sigval;
+          unsigned char grip[20];
+          char hexgrip[41];
+          unsigned char *sigval;
           size_t siglen;
 
           n = gcry_sexp_canon_len (public, 0, NULL, NULL);
@@ -637,7 +772,7 @@ create_request (ctrl_t ctrl,
               err = gpg_error (GPG_ERR_BUG);
               goto leave;
             }
-          rc = gcry_sexp_sscan (&s_pkey, NULL, public, n);
+          rc = gcry_sexp_sscan (&s_pkey, NULL, (const char*)public, n);
           if (rc)
             {
               log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
@@ -651,14 +786,20 @@ create_request (ctrl_t ctrl,
               goto leave;
             }
           gcry_sexp_release (s_pkey);
-          for (n=0; n < 20; n++)
-            sprintf (hexgrip+n*2, "%02X", grip[n]);
-
-          rc = gpgsm_agent_pksign (ctrl, hexgrip, NULL,
-                                   gcry_md_read(md, GCRY_MD_SHA1), 
-                                   gcry_md_get_algo_dlen (GCRY_MD_SHA1),
-                                   GCRY_MD_SHA1,
-                                   &sigval, &siglen);
+          bin2hex (grip, 20, hexgrip);
+
+          if (carddirect)
+            rc = gpgsm_scd_pksign (ctrl, carddirect, NULL,
+                                     gcry_md_read(md, GCRY_MD_SHA1), 
+                                     gcry_md_get_algo_dlen (GCRY_MD_SHA1),
+                                     GCRY_MD_SHA1,
+                                     &sigval, &siglen);
+          else
+            rc = gpgsm_agent_pksign (ctrl, hexgrip, NULL,
+                                     gcry_md_read(md, GCRY_MD_SHA1), 
+                                     gcry_md_get_algo_dlen (GCRY_MD_SHA1),
+                                     GCRY_MD_SHA1,
+                                     &sigval, &siglen);
           if (rc)
             {
               log_error ("signing failed: %s\n", gpg_strerror (rc));
@@ -687,37 +828,28 @@ create_request (ctrl_t ctrl,
 
 
 \f
-/* Create a new key by reading the parameters from in_fd.  Multiple
+/* Create a new key by reading the parameters from IN_FP.  Multiple
    keys may be created */
 int
-gpgsm_genkey (ctrl_t ctrl, int in_fd, FILE *out_fp)
+gpgsm_genkey (ctrl_t ctrl, estream_t in_stream, FILE *out_fp)
 {
   int rc;
-  FILE *in_fp;
   Base64Context b64writer = NULL;
   ksba_writer_t writer;
 
-  in_fp = fdopen (dup (in_fd), "rb");
-  if (!in_fp)
-    {
-      gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
-      log_error ("fdopen() failed: %s\n", strerror (errno));
-      return tmperr;
-    }
-
-  ctrl->pem_name = "NEW CERTIFICATE REQUEST";
-  rc = gpgsm_create_writer (&b64writer, ctrl, out_fp, &writer);
+  ctrl->pem_name = "CERTIFICATE REQUEST";
+  rc = gpgsm_create_writer (&b64writer, ctrl, out_fp, NULL, &writer);
   if (rc)
     {
       log_error ("can't create writer: %s\n", gpg_strerror (rc));
       goto leave;
     }
 
-  rc = read_parameters (ctrl, in_fp, writer);
+  rc = read_parameters (ctrl, in_stream, writer);
   if (rc)
     {
-      log_error ("error creating certificate request: %s\n",
-                 gpg_strerror (rc));
+      log_error ("error creating certificate request: %s <%s>\n",
+                 gpg_strerror (rc), gpg_strsource (rc));
       goto leave;
     }
 
@@ -733,7 +865,6 @@ gpgsm_genkey (ctrl_t ctrl, int in_fd, FILE *out_fp)
 
  leave:
   gpgsm_destroy_writer (b64writer);
-  fclose (in_fp);
   return rc;
 }