agent, sm: Set CTX after start_agent.
[gnupg.git] / sm / certreqgen-ui.c
index 09a9456..3ccd048 100644 (file)
@@ -1,5 +1,5 @@
 /* certreqgen-ui.c - Simple user interface for certreqgen.c
- * Copyright (C) 2007 Free Software Foundation, Inc.
+ * Copyright (C) 2007, 2010, 2011 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -22,7 +22,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <unistd.h> 
+#include <unistd.h>
 #include <time.h>
 #include <assert.h>
 
@@ -40,7 +40,7 @@ ask_mb_lines (membuf_t *mb, const char *prefix)
 {
   char *answer = NULL;
 
-  do 
+  do
     {
       xfree (answer);
       answer = tty_get ("> ");
@@ -86,20 +86,58 @@ store_mb_lines (membuf_t *mb, membuf_t *lines)
 }
 
 
+/* Chech whether we have a key for the key with HEXGRIP.  Returns NULL
+   if not or a string describing the type of the key (RSA, ELG, DSA,
+   etc..).  */
+static const char *
+check_keygrip (ctrl_t ctrl, const char *hexgrip)
+{
+  gpg_error_t err;
+  ksba_sexp_t public;
+  size_t publiclen;
+  const char *algostr;
+
+  if (hexgrip[0] == '&')
+    hexgrip++;
+
+  err = gpgsm_agent_readkey (ctrl, 0, hexgrip, &public);
+  if (err)
+    return NULL;
+  publiclen = gcry_sexp_canon_len (public, 0, NULL, NULL);
+
+  get_pk_algo_from_canon_sexp (public, publiclen, &algostr);
+  xfree (public);
+
+  if (!algostr)
+    return NULL;
+  else if (!strcmp (algostr, "rsa"))
+    return "RSA";
+  else if (!strcmp (algostr, "dsa"))
+    return "DSA";
+  else if (!strcmp (algostr, "elg"))
+    return "ELG";
+  else if (!strcmp (algostr, "ecdsa"))
+    return "ECDSA";
+  else
+    return NULL;
+}
+
+
 /* This function is used to create a certificate request from the
    command line.  In the past the similar gpgsm-gencert.sh script has
    been used for it; however that scripts requires a full Unix shell
    and thus is not suitable for the Windows port.  So here is the
    re-implementation.  */
 void
-gpgsm_gencertreq_tty (ctrl_t ctrl, FILE *output_fp)
+gpgsm_gencertreq_tty (ctrl_t ctrl, estream_t output_stream)
 {
   gpg_error_t err;
   char *answer;
   int selection;
-  FILE *fp = NULL;
+  estream_t fp = NULL;
   int method;
-  char *keytype;
+  char *keytype_buffer = NULL;
+  const char *keytype;
   char *keygrip = NULL;
   unsigned int nbits;
   int minbits = 1024;
@@ -109,14 +147,16 @@ gpgsm_gencertreq_tty (ctrl_t ctrl, FILE *output_fp)
   char *subject_name;
   membuf_t mb_email, mb_dns, mb_uri, mb_result;
   char *result = NULL;
-  int i;
   const char *s, *s2;
+  int selfsigned;
 
+  answer = NULL;
   init_membuf (&mb_email, 100);
   init_membuf (&mb_dns, 100);
   init_membuf (&mb_uri, 100);
   init_membuf (&mb_result, 512);
 
+ again:
   /* Get the type of the key.  */
   tty_printf (_("Please select what kind of key you want:\n"));
   tty_printf (_("   (%d) RSA\n"), 1 );
@@ -125,10 +165,10 @@ gpgsm_gencertreq_tty (ctrl_t ctrl, FILE *output_fp)
 
   do
     {
+      xfree (answer);
       answer = tty_get (_("Your selection? "));
       tty_kill_prompt ();
       selection = *answer? atoi (answer): 1;
-      xfree (answer);
     }
   while (!(selection >= 1 && selection <= 3));
   method = selection;
@@ -136,13 +176,14 @@ gpgsm_gencertreq_tty (ctrl_t ctrl, FILE *output_fp)
   /* Get  size of the key.  */
   if (method == 1)
     {
-      keytype = xstrdup ("RSA");
+      keytype = "RSA";
       for (;;)
         {
+          xfree (answer);
           answer = tty_getf (_("What keysize do you want? (%u) "), defbits);
           tty_kill_prompt ();
+          trim_spaces (answer);
           nbits = *answer? atoi (answer): defbits;
-          xfree (answer);
           if (nbits < minbits || nbits > maxbits)
             tty_printf(_("%s keysizes must be in the range %u-%u\n"),
                          "RSA", minbits, maxbits);
@@ -159,17 +200,80 @@ gpgsm_gencertreq_tty (ctrl_t ctrl, FILE *output_fp)
     }
   else if (method == 2)
     {
-      tty_printf ("Not yet supported; "
-                  "use the gpgsm-gencert.sh script instead\n");
-      keytype = xstrdup ("RSA"); 
-      nbits = defbits; /* We need a dummy value.  */
+      for (;;)
+        {
+          xfree (answer);
+          answer = tty_get (_("Enter the keygrip: "));
+          tty_kill_prompt ();
+          trim_spaces (answer);
+
+          if (!*answer)
+            goto again;
+          else if (strlen (answer) != 40 &&
+                   !(answer[0] == '&' && strlen (answer+1) == 40))
+            tty_printf (_("Not a valid keygrip (expecting 40 hex digits)\n"));
+          else if (!(keytype = check_keygrip (ctrl, answer)) )
+            tty_printf (_("No key with this keygrip\n"));
+          else
+            break; /* Okay.  */
+        }
+      xfree (keygrip);
+      keygrip = answer;
+      answer = NULL;
+      nbits = 1024; /* A dummy value is sufficient.  */
     }
   else /* method == 3 */
     {
-      tty_printf ("Not yet supported; "
-                  "use the gpgsm-gencert.sh script instead\n");
-      keytype = xstrdup ("card:foobar");
-      nbits = defbits; /* We need a dummy value.  */
+      char *serialno;
+      strlist_t keypairlist, sl;
+      int count;
+
+      err = gpgsm_agent_scd_serialno (ctrl, &serialno);
+      if (err)
+        {
+          tty_printf (_("error reading the card: %s\n"), gpg_strerror (err));
+          goto again;
+        }
+      tty_printf (_("Serial number of the card: %s\n"), serialno);
+      xfree (serialno);
+
+      err = gpgsm_agent_scd_keypairinfo (ctrl, &keypairlist);
+      if (err)
+        {
+          tty_printf (_("error reading the card: %s\n"), gpg_strerror (err));
+          goto again;
+        }
+
+      do
+        {
+          tty_printf (_("Available keys:\n"));
+          for (count=1,sl=keypairlist; sl; sl = sl->next, count++)
+            tty_printf ("   (%d) %s\n", count, sl->d);
+          xfree (answer);
+          answer = tty_get (_("Your selection? "));
+          tty_kill_prompt ();
+          trim_spaces (answer);
+          selection = atoi (answer);
+        }
+      while (!(selection > 0 && selection < count));
+
+      for (count=1,sl=keypairlist; sl; sl = sl->next, count++)
+        if (count == selection)
+          break;
+
+      s = sl->d;
+      while (*s && !spacep (s))
+        s++;
+      while (spacep (s))
+        s++;
+
+      xfree (keygrip);
+      keygrip = NULL;
+      xfree (keytype_buffer);
+      keytype_buffer = xasprintf ("card:%s", s);
+      free_strlist (keypairlist);
+      keytype = keytype_buffer;
+      nbits = 1024; /* A dummy value is sufficient.  */
     }
 
   /* Ask for the key usage.  */
@@ -179,10 +283,11 @@ gpgsm_gencertreq_tty (ctrl_t ctrl, FILE *output_fp)
   tty_printf (_("   (%d) encrypt\n"), 3 );
   do
     {
+      xfree (answer);
       answer = tty_get (_("Your selection? "));
       tty_kill_prompt ();
+      trim_spaces (answer);
       selection = *answer? atoi (answer): 1;
-      xfree (answer);
       switch (selection)
         {
         case 1: keyusage = "sign, encrypt"; break;
@@ -194,7 +299,6 @@ gpgsm_gencertreq_tty (ctrl_t ctrl, FILE *output_fp)
   while (!keyusage);
 
   /* Get the subject name.  */
-  answer = NULL;
   do
     {
       size_t erroff, errlen;
@@ -208,7 +312,7 @@ gpgsm_gencertreq_tty (ctrl_t ctrl, FILE *output_fp)
       else if ( (err = ksba_dn_teststr (answer, 0, &erroff, &errlen)) )
         {
           if (gpg_err_code (err) == GPG_ERR_UNKNOWN_NAME)
-            tty_printf (_("Invalid subject name label `%.*s'\n"),
+            tty_printf (_("Invalid subject name label '%.*s'\n"),
                         (int)errlen, answer+erroff);
           else
             {
@@ -217,7 +321,7 @@ gpgsm_gencertreq_tty (ctrl_t ctrl, FILE *output_fp)
                  adjust it do the length of your translation.  The
                  second string is merely passed to atoi so you can
                  drop everything after the number.  */
-              tty_printf (_("Invalid subject name `%s'\n"), answer);
+              tty_printf (_("Invalid subject name '%s'\n"), answer);
               tty_printf ("%*s^\n",
                           atoi (_("22 translator: see "
                                   "certreg-ui.c:gpgsm_gencertreq_tty"))
@@ -238,12 +342,17 @@ gpgsm_gencertreq_tty (ctrl_t ctrl, FILE *output_fp)
   /* DNS names.  */
   tty_printf (_("Enter DNS names"));
   tty_printf (_(" (optional; end with an empty line):\n"));
-  ask_mb_lines (&mb_email, "Name-DNS: ");
+  ask_mb_lines (&mb_dns, "Name-DNS: ");
 
   /* URIs.  */
   tty_printf (_("Enter URIs"));
   tty_printf (_(" (optional; end with an empty line):\n"));
-  ask_mb_lines (&mb_email, "Name-URI: ");
+  ask_mb_lines (&mb_uri, "Name-URI: ");
+
+
+  /* Want a self-signed certificate?  */
+  selfsigned = tty_get_answer_is_yes
+    (_("Create self-signed certificate? (y/N) "));
 
 
   /* Put it all together.  */
@@ -253,10 +362,12 @@ gpgsm_gencertreq_tty (ctrl_t ctrl, FILE *output_fp)
     snprintf (numbuf, sizeof numbuf, "%u", nbits);
     store_key_value_lf (&mb_result, "Key-Length: ", numbuf);
   }
-  store_key_value_lf (&mb_result, "Key-Usage: ", keyusage);
-  store_key_value_lf (&mb_result, "Name-DN: ", subject_name);
   if (keygrip)
     store_key_value_lf (&mb_result, "Key-Grip: ", keygrip);
+  store_key_value_lf (&mb_result, "Key-Usage: ", keyusage);
+  if (selfsigned)
+    store_key_value_lf (&mb_result, "Serial: ", "random");
+  store_key_value_lf (&mb_result, "Name-DN: ", subject_name);
   if (store_mb_lines (&mb_result, &mb_email))
     goto mem_error;
   if (store_mb_lines (&mb_result, &mb_dns))
@@ -268,44 +379,53 @@ gpgsm_gencertreq_tty (ctrl_t ctrl, FILE *output_fp)
   if (!result)
     goto mem_error;
 
-  tty_printf (_("Parameters to be used for the certificate request:\n"));
-  for (s=result; (s2 = strchr (s, '\n')); s = s2+1, i++)
+  tty_printf (_("These parameters are used:\n"));
+  for (s=result; (s2 = strchr (s, '\n')); s = s2+1)
     tty_printf ("    %.*s\n", (int)(s2-s), s);
   tty_printf ("\n");
 
-
-  if (!tty_get_answer_is_yes ("Really create request? (y/N) "))
-     goto leave;
+  if (!tty_get_answer_is_yes ("Proceed with creation? (y/N) "))
+    goto leave;
 
   /* Now create a parameter file and generate the key.  */
-  fp = tmpfile ();
+  fp = es_fopenmem (0, "w+");
   if (!fp)
     {
       log_error (_("error creating temporary file: %s\n"), strerror (errno));
       goto leave;
     }
-  fputs (result, fp);
-  rewind (fp);
-  tty_printf (_("Now creating certificate request.  "
-                "This may take a while ...\n"));
+  es_fputs (result, fp);
+  es_rewind (fp);
+  if (selfsigned)
+    tty_printf ("%s", _("Now creating self-signed certificate.  "));
+  else
+    tty_printf ("%s", _("Now creating certificate request.  "));
+  tty_printf ("%s", _("This may take a while ...\n"));
+
   {
     int save_pem = ctrl->create_pem;
     ctrl->create_pem = 1; /* Force creation of PEM. */
-    err = gpgsm_genkey (ctrl, -1, fp, output_fp);
+    err = gpgsm_genkey (ctrl, fp, output_stream);
     ctrl->create_pem = save_pem;
   }
   if (!err)
-    tty_printf (_("Ready.  You should now send this request to your CA.\n"));
+    {
+      if (selfsigned)
+        tty_printf (_("Ready.\n"));
+      else
+        tty_printf
+          (_("Ready.  You should now send this request to your CA.\n"));
+    }
 
 
   goto leave;
  mem_error:
-  log_error (_("resource problem: out or core\n"));
+  log_error (_("resource problem: out of core\n"));
  leave:
-  if (fp)
-    fclose (fp);
-  xfree (keytype);         
+  es_fclose (fp);
+  xfree (answer);
   xfree (subject_name);
+  xfree (keytype_buffer);
   xfree (keygrip);
   xfree (get_membuf (&mb_email, NULL));
   xfree (get_membuf (&mb_dns, NULL));