* plaintext.c (handle_plaintext): Accept 'u' as a plaintext mode that
[gnupg.git] / g10 / cardglue.c
index 91637a7..55729bf 100644 (file)
@@ -20,7 +20,7 @@
 
 #include <config.h>
 #ifndef ENABLE_CARD_SUPPORT
-#error  no configured for card support.
+#error  not configured for card support.
 #endif
 #include <stdio.h>
 #include <stdlib.h>
@@ -36,6 +36,7 @@
 #include "util.h"
 #include "main.h"
 #include "status.h"
+#include "ttyio.h"
 #include "i18n.h"
 
 #include "cardglue.h"
@@ -249,7 +250,9 @@ open_card (void)
   int rc;
   APP app;
 
-  current_app = NULL;/* FIXME: Release it first.*/
+  card_close ();
+
+ retry:
   slot = apdu_open_reader (default_reader_port);
   if (slot == -1)
     {
@@ -260,19 +263,107 @@ open_card (void)
   app = xcalloc (1, sizeof *app);
   app->slot = slot;
   rc = app_select_openpgp (app, &app->serialno, &app->serialnolen);
+  if (rc && !opt.batch)
+    {
+      write_status_text (STATUS_CARDCTRL, "1");
+      
+      if ( cpr_get_answer_okay_cancel ("cardctrl.insert_card.okay",
+           _("Please insert the card and hit return or enter 'c' to cancel: "),
+                                       1) )
+        {
+          apdu_close_reader (slot);
+          xfree (app);
+          goto retry;
+        }
+    }
   if (rc)
     {
-/*        apdu_close_reader (slot); */
       log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc));
+      apdu_close_reader (slot);
       xfree (app);
       return NULL;
     }
 
   app->initialized = 1;
   current_app = app;
+  if (is_status_enabled () )
+    {
+      int i;
+      char *p, *buf;
+
+      buf = xmalloc (5 + app->serialnolen * 2 + 1);
+      p = stpcpy (buf, "3 ");
+      for (i=0; i < app->serialnolen; p +=2, i++)
+        sprintf (p, "%02X", app->serialno[i]);
+      write_status_text (STATUS_CARDCTRL, buf);
+      xfree (buf);
+    }
+
   return app;
 }
 
+void
+card_close (void)
+{
+  if (current_app)
+    {
+      APP app = current_app;
+      current_app = NULL;
+
+      apdu_close_reader (app->slot);
+      xfree (app);
+    }
+}
+
+
+/* Check that the serial number of the current card (as described by
+   APP) matches SERIALNO.  If there is no match and we are not in
+   batch mode, present a prompt to insert the desired card.  The
+   function return 0 is the present card is okay, -1 if the user
+   selected to insert a new card or an error value.  Note that the
+   card context will be closed in all cases except for 0 as return
+   value. */
+static int
+check_card_serialno (APP app, const char *serialno)
+{
+  const char *s;
+  int ask = 0;
+  int n;
+  
+  for (s = serialno, n=0; *s != '/' && hexdigitp (s); s++, n++)
+    ;
+  if (n != 32)
+    {
+      log_error ("invalid serial number in keyring detected\n");
+      return gpg_error (GPG_ERR_INV_ID);
+    }
+  if (app->serialnolen != 16)
+    ask = 1;
+  for (s = serialno, n=0; !ask && n < 16; s += 2, n++)
+    if (app->serialno[n] != xtoi_2 (s))
+      ask = 1;
+  if (ask)
+    {
+      char buf[5+32+1];
+
+      card_close ();
+      tty_printf (_("Please remove the current card and "
+                    "insert the one with the serial number:\n"
+                    "   %.*s\n"), 32, serialno);
+
+      sprintf (buf, "1 %.32s", serialno);
+      write_status_text (STATUS_CARDCTRL, buf);
+
+      if ( cpr_get_answer_okay_cancel ("cardctrl.change_card.okay",
+                          _("Hit return when ready "
+                            "or enter 'c' to cancel: "),
+                                       1) )
+        return -1;
+      return gpg_error (GPG_ERR_INV_ID);
+    }
+  return 0;
+}
+
 
 
 /* Return a new malloced string by unescaping the string S.  Escaping
@@ -401,14 +492,14 @@ learn_status_cb (void *opaque, const char *line)
           while (spacep (p))
             p++;
           parm->chv1_cached = atoi (p);
-          while (!spacep (p))
+          while (*p && !spacep (p))
             p++;
           while (spacep (p))
             p++;
           for (i=0; *p && i < 3; i++)
             {
               parm->chvmaxlen[i] = atoi (p);
-              while (!spacep (p))
+              while (*p && !spacep (p))
                 p++;
               while (spacep (p))
                 p++;
@@ -416,7 +507,7 @@ learn_status_cb (void *opaque, const char *line)
           for (i=0; *p && i < 3; i++)
             {
               parm->chvretry[i] = atoi (p);
-              while (!spacep (p))
+              while (*p && !spacep (p))
                 p++;
               while (spacep (p))
                 p++;
@@ -427,7 +518,7 @@ learn_status_cb (void *opaque, const char *line)
   else if (keywordlen == 7 && !memcmp (keyword, "KEY-FPR", keywordlen))
     {
       int no = atoi (line);
-      while (!spacep (line))
+      while (* line && !spacep (line))
         line++;
       while (spacep (line))
         line++;
@@ -496,13 +587,16 @@ pin_cb (void *opaque, const char *info, char **retstr)
 {
   char *value;
   int canceled;
+  int isadmin = (info && strstr (info, "dmin"));
+
 
   *retstr = NULL;
   log_debug ("asking for PIN '%s'\n", info);
 
   value = ask_passphrase (info, 
-                          info && strstr (info, "dmin")?
-                           _("Enter Admin PIN: ") : _("Enter PIN: "),
+                          isadmin? "passphrase.adminpin.ask"
+                                 : "passphrase.pin.ask", 
+                          isadmin?  _("Enter Admin PIN: ") : _("Enter PIN: "),
                           &canceled);
   if (!value && canceled)
     return -1;
@@ -553,7 +647,7 @@ genkey_status_cb (void *opaque, const char *line)
       const char *name = line;
       char *buf;
 
-      while (!spacep (line))
+      while (*line && !spacep (line))
         line++;
       while (spacep (line))
         line++;
@@ -611,14 +705,21 @@ agent_scd_pksign (const char *serialno, int hashalgo,
                   unsigned char **r_buf, size_t *r_buflen)
 {
   APP app;
+  int rc;
 
   *r_buf = NULL;
   *r_buflen = 0;
+ retry:
   app = current_app? current_app : open_card ();
   if (!app)
     return gpg_error (GPG_ERR_CARD);
 
   /* Check that the card's serialnumber is as required.*/
+  rc = check_card_serialno (app, serialno);
+  if (rc == -1)
+    goto retry;
+  if (rc)
+    return rc;
 
   return app->fnc.sign (app, serialno, hashalgo,
                         pin_cb, NULL,
@@ -633,15 +734,23 @@ agent_scd_pkdecrypt (const char *serialno,
                      const unsigned char *indata, size_t indatalen,
                      unsigned char **r_buf, size_t *r_buflen)
 {
-
   APP app;
+  int rc;
 
   *r_buf = NULL;
   *r_buflen = 0;
+ retry:
   app = current_app? current_app : open_card ();
   if (!app)
     return gpg_error (GPG_ERR_CARD);
 
+  /* Check that the card's serialnumber is as required.*/
+  rc = check_card_serialno (app, serialno);
+  if (rc == -1)
+    goto retry;
+  if (rc)
+    return rc;
+
   return app->fnc.decipher (app, serialno, 
                             pin_cb, NULL,
                             indata, indatalen,
@@ -652,7 +761,34 @@ agent_scd_pkdecrypt (const char *serialno,
 int 
 agent_scd_change_pin (int chvno)
 {
+  APP app;
+  char chvnostr[20];
+  int reset = 0;
+
+  reset = (chvno >= 100);
+  chvno %= 100;
+
+  app = current_app? current_app : open_card ();
+  if (!app)
+    return gpg_error (GPG_ERR_CARD);
+
+  sprintf (chvnostr, "%d", chvno);
+  return app->fnc.change_pin (app, NULL, chvnostr, reset,
+                              pin_cb, NULL);
+}
+
+/* Perform a CHECKPIN operation.  SERIALNO should be the seriial
+   number of the card - optioanlly followed by the fingerprint;
+   however the fingerprint is ignored here. */
+int
+agent_scd_checkpin (const char *serialnobuf)
+{
+  APP app;
+
+  app = current_app? current_app : open_card ();
+  if (!app)
+    return gpg_error (GPG_ERR_CARD);
 
-  return gpg_error (GPG_ERR_CARD);
+  return app->fnc.check_pin (app, serialnobuf, pin_cb, NULL);
 }