* keyserver.c (keyserver_spawn): Use the full 64-bit keyid in the INFO
[gnupg.git] / g10 / card-util.c
index 901ce92..de445b7 100644 (file)
  */
 
 #include <config.h>
-#ifdef ENABLE_CARD_SUPPORT
-/* 
-   Note, that most card related code has been taken from 1.9.x branch
-   and is maintained over there if at all possible.  Thus, if you make
-   changes here, please check that a similar change has been commited
-   to the 1.9.x branch.
-*/
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 #include <assert.h>
 
+#if GNUPG_MAJOR_VERSION != 1
+#include "gpg.h"
+#endif
 #include "util.h"
 #include "i18n.h"
 #include "ttyio.h"
 #include "status.h"
 #include "options.h"
 #include "main.h"
-
+#if GNUPG_MAJOR_VERSION == 1
 #include "cardglue.h"
+#else
+#include "call-agent.h"
+#endif
 
 #define CONTROL_D ('D' - 'A' + 1)
 
@@ -51,7 +50,6 @@ change_pin (int chvno)
 {
   struct agent_card_info_s info;
   int rc;
-  int reset_mode = 0;
 
   rc = agent_learn (&info);
   if (rc)
@@ -77,17 +75,11 @@ change_pin (int chvno)
       char *answer;
 
       tty_printf ("\n");
-      tty_printf ("1 - change signature PIN\n"
-                  "2 - change decryption and authentication PIN\n"
-                  "3 - change Admin's PIN\n"
-                  "R - toggle reset retry counter mode\n"
+      tty_printf ("1 - change PIN\n"
+                  "2 - unblock PIN\n"
+                  "3 - change Admin PIN\n"
                   "Q - quit\n");
       tty_printf ("\n");
-      if (reset_mode)
-        {
-          tty_printf ("Reset Retry Counter mode active\n");
-          tty_printf ("\n");
-        }
 
       answer = cpr_get("cardutil.change_pin.menu",_("Your selection? "));
       cpr_kill_prompt();
@@ -95,30 +87,35 @@ change_pin (int chvno)
         continue;
 
       rc = 0;
-      if (reset_mode && *answer == '3')
+      if (*answer == '1')
         {
-          tty_printf ("Sorry, reset of the Admin PIN's retry counter "
-                      "is not possible.\n");
+          rc = agent_scd_change_pin (1);
+          if (rc)
+            tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
+          else
+            tty_printf ("PIN changed.\n");
         }
-      else if (*answer == '1'  || *answer == '2' || *answer == '3')
+      else if (*answer == '2')
         {
-          rc = agent_scd_change_pin (*answer - '0' + (reset_mode?100:0));
+          rc = agent_scd_change_pin (101);
           if (rc)
-            tty_printf ("Error changing/resetting the PIN: %s\n",
-                        gpg_strerror (rc));
+            tty_printf ("Error unblocking the PIN: %s\n", gpg_strerror (rc));
           else
-            tty_printf ("New PIN successfully set.\n");
+            tty_printf ("PIN unblocked and new PIN set.\n");
         }
-      else if (*answer == 'r' || *answer == 'R')
+      else if (*answer == '3')
         {
-          reset_mode = !reset_mode;
+          rc = agent_scd_change_pin (3);
+          if (rc)
+            tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
+          else
+            tty_printf ("PIN changed.\n");
         }
       else if (*answer == 'q' || *answer == 'Q')
         {
           break;
         }
     }
-
 }
 
 static const char *
@@ -182,7 +179,7 @@ print_name (FILE *fp, const char *text, const char *name)
       if (fp)
         print_utf8_string2 (fp, name, strlen (name), '\n');
       else
-        tty_print_utf8_string2 (name, strlen (name), '\n');
+        tty_print_utf8_string2 (name, strlen (name), 0);
     }
   else
     tty_fprintf (fp, _("[not set]"));
@@ -214,7 +211,7 @@ print_isoname (FILE *fp, const char *text, const char *tag, const char *name)
           else if (fp)
             print_utf8_string2 (fp, given, strlen (given), '\n');
           else
-            tty_print_utf8_string2 (given, strlen (given), '\n');
+            tty_print_utf8_string2 (given, strlen (given), 0);
 
           if (opt.with_colons)
             putc (':', fp);
@@ -227,7 +224,7 @@ print_isoname (FILE *fp, const char *text, const char *tag, const char *name)
       else if (fp)
         print_utf8_string2 (fp, buf, strlen (buf), '\n');
       else
-        tty_print_utf8_string2 (buf, strlen (buf), '\n');
+        tty_print_utf8_string2 (buf, strlen (buf), 0);
       xfree (buf);
     }
   else
@@ -244,16 +241,30 @@ print_isoname (FILE *fp, const char *text, const char *tag, const char *name)
     tty_fprintf (fp, "\n");
 }
 
+/* Return true if the SHA1 fingerprint FPR consists only of zeroes. */
+static int
+fpr_is_zero (const char *fpr)
+{
+  int i;
+
+  for (i=0; i < 20 && !fpr[i]; i++)
+    ;
+  return (i == 20);
+}
+
 
 /* Print all available information about the current card. */
 void
-card_status (FILE *fp)
+card_status (FILE *fp, char *serialno, size_t serialnobuflen)
 {
   struct agent_card_info_s info;
   PKT_public_key *pk = xcalloc (1, sizeof *pk);
   int rc;
   unsigned int uval;
 
+  if (serialno && serialnobuflen)
+    *serialno = 0;
+
   rc = agent_learn (&info);
   if (rc)
     {
@@ -281,6 +292,13 @@ card_status (FILE *fp)
       return;
     }
 
+  if (!serialno)
+    ;
+  else if (strlen (serialno)+1 > serialnobuflen)
+    log_error ("serial number longer than expected\n");
+  else 
+    strcpy (serialno, info.serialno);
+
   if (opt.with_colons)
     fputs ("openpgp-card:\n", fp);
 
@@ -346,7 +364,7 @@ card_status (FILE *fp)
       print_name (fp, "URL of public key : ", info.pubkey_url);
       print_name (fp, "Login data .......: ", info.login_data);
       tty_fprintf (fp,    "Signature PIN ....: %s\n",
-                   info.chv1_cached? _("cached"): _("not cached"));
+                   info.chv1_cached? _("not forced"): _("forced"));
       tty_fprintf (fp,    "Max. PIN lengths .: %d %d %d\n",
                    info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]);
       tty_fprintf (fp,    "PIN retry counter : %d %d %d\n",
@@ -358,11 +376,11 @@ card_status (FILE *fp)
       print_sha1_fpr (fp, info.fpr2valid? info.fpr2:NULL);
       tty_fprintf (fp, "Authentication key:");
       print_sha1_fpr (fp, info.fpr3valid? info.fpr3:NULL);
-/*       tty_fprintf (fp, "General key info..: ");  */
-/*       if (info.fpr1valid && !get_pubkey_byfprint (pk, info.fpr1, 20)) */
-/*         print_pubkey_info (fp, pk); */
-/*       else */
-/*         tty_fprintf (fp, "[none]\n"); */
+      tty_fprintf (fp, "General key info..: "); 
+      if (info.fpr1valid && !get_pubkey_byfprint (pk, info.fpr1, 20))
+        print_pubkey_info (fp, pk);
+      else
+        tty_fprintf (fp, "[none]\n");
     }
       
   free_public_key (pk);
@@ -549,6 +567,100 @@ change_sex (void)
 }
 
 
+static void
+toggle_forcesig (void)
+{
+  struct agent_card_info_s info;
+  int rc;
+  int newstate;
+
+  memset (&info, 0, sizeof info);
+  rc = agent_scd_getattr ("CHV-STATUS", &info);
+  if (rc)
+    {
+      log_error ("error getting current status: %s\n", gpg_strerror (rc));
+      return;
+    }
+  newstate = !info.chv1_cached;
+  agent_release_card_info (&info);
+
+  rc = agent_scd_setattr ("CHV-STATUS-1", newstate? "\x01":"", 1);
+  if (rc)
+    log_error ("error toggling signature PIN flag: %s\n", gpg_strerror (rc));
+}
+
+
+static void
+generate_card_keys (void)
+{
+  struct agent_card_info_s info;
+  int rc;
+  int forced_chv1;
+
+  memset (&info, 0, sizeof info);
+  rc = agent_scd_getattr ("KEY-FPR", &info);
+  if (!rc)
+    rc = agent_scd_getattr ("SERIALNO", &info);
+  if (!rc)
+    rc = agent_scd_getattr ("CHV-STATUS", &info);
+  if (!rc)
+    rc = agent_scd_getattr ("DISP-NAME", &info);
+  if (rc)
+    {
+      log_error ("error getting current key info: %s\n", gpg_strerror (rc));
+      return;
+    }
+  if ( (info.fpr1valid && !fpr_is_zero (info.fpr1))
+       || (info.fpr2valid && !fpr_is_zero (info.fpr2))
+       || (info.fpr3valid && !fpr_is_zero (info.fpr3)))
+    {
+      tty_printf ("\n");
+      log_info ("NOTE: keys are already stored on the card!\n");
+      tty_printf ("\n");
+      if ( !cpr_get_answer_is_yes( "cardedit.genkeys.replace_keys",
+                                  _("Replace existing keys? ")))
+        {
+          agent_release_card_info (&info);
+          return;
+        }
+    }
+  else if (!info.disp_name || !*info.disp_name)
+    {
+      tty_printf ("\n");
+      tty_printf (_("Please note that the factory settings of the PINs are\n"
+                    "   PIN = \"%s\"     Admin PIN = \"%s\"\n"
+                    "You should change them using the command --change-pin\n"),
+                  "123456", "12345678");
+      tty_printf ("\n");
+    }
+
+  forced_chv1 = !info.chv1_cached;
+  if (forced_chv1)
+    { /* Switch of the forced mode so that during key generation we
+         don't get bothered with PIN queries for each
+         self-signature. */
+      rc = agent_scd_setattr ("CHV-STATUS-1", "\x01", 1);
+      if (rc)
+        {
+          log_error ("error clearing forced signature PIN flag: %s\n",
+                     gpg_strerror (rc));
+          return;
+        }
+    }
+  generate_keypair (NULL, info.serialno);
+  agent_release_card_info (&info);
+  if (forced_chv1)
+    { /* Switch back to forced state. */
+      rc = agent_scd_setattr ("CHV-STATUS-1", "", 1);
+      if (rc)
+        {
+          log_error ("error setting forced signature PIN flag: %s\n",
+                     gpg_strerror (rc));
+          return;
+        }
+    }
+}
+
 /* Menu to edit all user changeable values on an OpenPGP card.  Only
    Key creation is not handled here. */
 void
@@ -558,34 +670,43 @@ card_edit (STRLIST commands)
     cmdNOP = 0,
     cmdQUIT, cmdHELP, cmdLIST, cmdDEBUG,
     cmdNAME, cmdURL, cmdLOGIN, cmdLANG, cmdSEX,
-
+    cmdFORCESIG, cmdGENERATE, cmdPASSWD,
     cmdINVCMD
   };
 
   static struct {
     const char *name;
     enum cmdids id;
+    int requires_pin;
     const char *desc;
   } cmds[] = {
-    { N_("quit")  , cmdQUIT  , N_("quit this menu") },
-    { N_("q")     , cmdQUIT  , NULL   },
-    { N_("help")  , cmdHELP  , N_("show this help") },
-    {    "?"      , cmdHELP  , NULL   },
-    { N_("list")  , cmdLIST  , N_("list all available data") },
-    { N_("l")     , cmdLIST  , NULL   },
-    { N_("debug") , cmdDEBUG , NULL },
-    { N_("name")  , cmdNAME  , N_("change card holder's name") },
-    { N_("url")   , cmdURL   , N_("change URL to retrieve key") },
-    { N_("login") , cmdLOGIN , N_("change the login name") },
-    { N_("lang")  , cmdLANG  , N_("change the language preferences") },
-    { N_("sex")   , cmdSEX   , N_("change card holder's sex") },
+    { N_("quit")  , cmdQUIT  , 0, N_("quit this menu") },
+    { N_("q")     , cmdQUIT  , 0, NULL   },
+    { N_("help")  , cmdHELP  , 0, N_("show this help") },
+    {    "?"      , cmdHELP  , 0, NULL   },
+    { N_("list")  , cmdLIST  , 0, N_("list all available data") },
+    { N_("l")     , cmdLIST  , 0, NULL   },
+    { N_("debug") , cmdDEBUG , 0, NULL },
+    { N_("name")  , cmdNAME  , 1, N_("change card holder's name") },
+    { N_("url")   , cmdURL   , 1, N_("change URL to retrieve key") },
+    { N_("login") , cmdLOGIN , 1, N_("change the login name") },
+    { N_("lang")  , cmdLANG  , 1, N_("change the language preferences") },
+    { N_("sex")   , cmdSEX   , 1, N_("change card holder's sex") },
+    { N_("forcesig"),
+                  cmdFORCESIG, 1, N_("toggle the signature force PIN flag") },
+    { N_("generate"),
+                  cmdGENERATE, 1, N_("generate new keys") },
+    { N_("passwd"), cmdPASSWD, 0, N_("menu to change or unblock the PIN") },
     { NULL, cmdINVCMD } 
   };
-
   enum cmdids cmd = cmdNOP;
   int have_commands = !!commands;
   int redisplay = 1;
   char *answer = NULL;
+  int did_checkpin = 0;
+  char serialnobuf[50];
+
 
   if (opt.command_fd != -1)
     ;
@@ -601,18 +722,19 @@ card_edit (STRLIST commands)
       const char *arg_string = "";
       char *p;
       int i;
-
+      int requires_pin;
+      
       tty_printf("\n");
       if (redisplay )
         {
           if (opt.with_colons)
             {
-              card_status (stdout);
+              card_status (stdout, serialnobuf, DIM (serialnobuf));
               fflush (stdout);
             }
           else
             {
-              card_status (NULL);
+              card_status (NULL, serialnobuf, DIM (serialnobuf));
               tty_printf("\n");
             }
           redisplay = 0;
@@ -646,6 +768,7 @@ card_edit (STRLIST commands)
       while( *answer == '#' );
 
       arg_number = 0; /* Yes, here is the init which egcc complains about */
+      requires_pin = 0;
       if (!*answer)
         cmd = cmdLIST; /* Default to the list command */
       else if (*answer == CONTROL_D)
@@ -665,7 +788,19 @@ card_edit (STRLIST commands)
             break;
 
         cmd = cmds[i].id;
+        requires_pin = cmds[i].requires_pin;
       }
+      
+      if (requires_pin && !did_checkpin)
+        {
+          int rc = agent_scd_checkpin (serialnobuf);
+          if (rc)
+            {
+              log_error ("error checking the PIN: %s\n", gpg_strerror (rc));
+              continue;
+            }
+          did_checkpin = 1;
+        }
 
       switch (cmd)
         {
@@ -699,6 +834,19 @@ card_edit (STRLIST commands)
           change_sex ();
           break;
 
+        case cmdFORCESIG:
+          toggle_forcesig ();
+          break;
+
+        case cmdGENERATE:
+          generate_card_keys ();
+          break;
+
+        case cmdPASSWD:
+          change_pin (0);
+          did_checkpin = 0; /* Need to reset it of course. */
+          break;
+
         case cmdQUIT:
           goto leave;
 
@@ -717,4 +865,3 @@ card_edit (STRLIST commands)
   xfree (answer);
 }
 
-#endif /*ENABLE_CARD_SUPPORT*/