* cardglue.c (open_card): Ask for card insertion.
authorWerner Koch <wk@gnupg.org>
Wed, 29 Oct 2003 10:07:44 +0000 (10:07 +0000)
committerWerner Koch <wk@gnupg.org>
Wed, 29 Oct 2003 10:07:44 +0000 (10:07 +0000)
(check_card_serialno): New.
(agent_scd_pksign, agent_scd_pkdecrypt): Use it here.
* cardglue.c (open_card): Issue insertion status message.
* status.h, status.c (STATUS_CARDCTRL): New.

* status.c (cpr_get_answer_okay_cancel): New.

* miscutil.c (answer_is_okay_cancel): New.

doc/DETAILS
g10/ChangeLog
g10/cardglue.c
g10/status.c
g10/status.h
include/util.h
util/ChangeLog
util/miscutil.c

index e88300b..0010f54 100644 (file)
@@ -462,6 +462,14 @@ more arguments in future versions.
                0x02 = this attribute packet is revoked
                0x04 = this attribute packet is expired
 
+    STATUSCTRL <what> [<serialno>]
+        This is used to control smartcard operations.
+        Defined values for WHAT are:
+           1 = Request insertion of a card.  Serialnumber may be given
+               to request a specific card.
+           2 = Request removal of a card.
+           3 = Card with serialnumber detected
+
 
 Format of the "--attribute-fd" output
 =====================================
index 5896c5d..2c6b1bc 100644 (file)
@@ -1,3 +1,13 @@
+2003-10-29  Werner Koch  <wk@gnupg.org>
+
+       * cardglue.c (open_card): Ask for card insertion.
+       (check_card_serialno): New.
+       (agent_scd_pksign, agent_scd_pkdecrypt): Use it here.
+       * cardglue.c (open_card): Issue insertion status message.
+       * status.h, status.c (STATUS_CARDCTRL): New.
+
+       * status.c (cpr_get_answer_okay_cancel): New.
+
 2003-10-28  Werner Koch  <wk@gnupg.org>
 
        * keylist.c (list_keyblock_print): Denote secrets keys stored on a
index 3173248..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"
@@ -250,6 +251,8 @@ open_card (void)
   APP app;
 
   card_close ();
+
+ retry:
   slot = apdu_open_reader (default_reader_port);
   if (slot == -1)
     {
@@ -260,16 +263,42 @@ 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;
 }
 
@@ -287,6 +316,56 @@ card_close (void)
 }
 
 
+/* 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
    is percent escaping and '+'/space mapping.  A binary nul will
    silently be replaced by a 0xFF.  Function returns NULL to indicate
@@ -626,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,
@@ -649,13 +735,22 @@ agent_scd_pkdecrypt (const char *serialno,
                      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,
index cde0c8d..d39bc68 100644 (file)
@@ -150,6 +150,7 @@ get_status_string ( int no )
       case STATUS_EXPKEYSIG      : s = "EXPKEYSIG"; break;
       case STATUS_REVKEYSIG      : s = "REVKEYSIG"; break;
       case STATUS_ATTRIBUTE      : s = "ATTRIBUTE"; break;
+      case STATUS_CARDCTRL       : s = "CARDCTRL"; break;
       default: s = "?"; break;
     }
     return s;
@@ -692,3 +693,47 @@ cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt )
        }
     }
 }
+
+
+int
+cpr_get_answer_okay_cancel (const char *keyword,
+                            const char *prompt,
+                            int def_answer)
+{
+  int yes;
+  char *answer = NULL;
+  char *p;
+
+  if( opt.command_fd != -1 )
+    answer = do_get_from_fd ( keyword, 0, 0 );
+#ifdef USE_SHM_COPROCESSING
+  else if( opt.shm_coprocess )
+    answer = do_shm_get( keyword, 0, 0 );
+#endif
+
+  if (answer)
+    {
+      yes = answer_is_okay_cancel (answer, def_answer);
+      m_free (answer);
+      return yes;
+    }
+
+  for(;;)
+    {
+      p = tty_get( prompt );
+      trim_spaces(p); /* it is okay to do this here */
+      if (*p == '?' && !p[1])
+        {
+          m_free(p);
+          display_online_help (keyword);
+       }
+      else
+        {
+          tty_kill_prompt();
+          yes = answer_is_okay_cancel (p, def_answer);
+          m_free(p);
+          return yes;
+       }
+    }
+}
+
index 68da60d..73cc3f1 100644 (file)
 #define STATUS_IMPORT_OK       68
 #define STATUS_IMPORT_CHECK     69
 #define STATUS_REVKEYSIG        70
+#define STATUS_CARDCTRL         71
 
 /*-- status.c --*/
 void set_status_fd ( int fd );
@@ -123,6 +124,9 @@ char *cpr_get_hidden( const char *keyword, const char *prompt );
 void cpr_kill_prompt(void);
 int  cpr_get_answer_is_yes( const char *keyword, const char *prompt );
 int  cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt );
+int  cpr_get_answer_okay_cancel (const char *keyword,
+                                 const char *prompt,
+                                 int def_answer);
 
 
 #endif /*G10_STATUS_H*/
index 08ea7a7..da2758a 100644 (file)
@@ -175,6 +175,7 @@ char *make_printable_string( const byte *p, size_t n, int delim );
 int answer_is_yes_no_default( const char *s, int def_answer );
 int answer_is_yes( const char *s );
 int answer_is_yes_no_quit( const char *s );
+int answer_is_okay_cancel (const char *s, int def_answer);
 
 /*-- strgutil.c --*/
 void free_strlist( STRLIST sl );
index 50e2537..5501fd5 100644 (file)
@@ -1,3 +1,7 @@
+2003-10-29  Werner Koch  <wk@gnupg.org>
+
+       * miscutil.c (answer_is_okay_cancel): New.
+
 2003-10-25  Werner Koch  <wk@gnupg.org>
 
        * Makefile.am: Replaced INTLLIBS by LIBINTL.
index b266d27..e0ea0e7 100644 (file)
@@ -359,3 +359,38 @@ answer_is_yes_no_quit( const char *s )
        return -1;
     return 0;
 }
+
+
+/*
+   Return 1 for okay, 0 for for cancel or DEF_ANSWER for default. 
+ */
+int
+answer_is_okay_cancel (const char *s, int def_answer)
+{
+  const char *long_okay = _("okay");
+  const char *long_cancel = _("cancel");
+  const char *short_okay = _("oO");
+  const char *short_cancel = _("cC");
+  
+  /* Note: We have to use the locale dependent strcasecmp */
+  if ( !strcasecmp(s, long_okay ) )
+    return 1;
+  if ( !strcasecmp(s, long_cancel ) )
+    return 0;
+  if ( *s && strchr( short_okay, *s ) && !s[1] )
+    return 1;
+  if ( *s && strchr( short_cancel, *s ) && !s[1] )
+    return 0;
+  /* Always test for the English values (not locale here) */
+  if ( !ascii_strcasecmp(s, "okay" ) )
+    return 1;
+  if ( !ascii_strcasecmp(s, "ok" ) )
+    return 1;
+  if ( !ascii_strcasecmp(s, "cancel" ) )
+    return 0;
+  if ( *s && strchr( "oO", *s ) && !s[1] )
+    return 1;
+  if ( *s && strchr( "cC", *s ) && !s[1] )
+    return 0;
+  return def_answer;
+}