diff --git a/doc/DETAILS b/doc/DETAILS index e88300b74..0010f549c 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -462,6 +462,14 @@ more arguments in future versions. 0x02 = this attribute packet is revoked 0x04 = this attribute packet is expired + STATUSCTRL [] + 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 ===================================== diff --git a/g10/ChangeLog b/g10/ChangeLog index 5896c5d88..2c6b1bc3e 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,13 @@ +2003-10-29 Werner Koch + + * 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 * keylist.c (list_keyblock_print): Denote secrets keys stored on a diff --git a/g10/cardglue.c b/g10/cardglue.c index 3173248cb..55729bf7d 100644 --- a/g10/cardglue.c +++ b/g10/cardglue.c @@ -20,7 +20,7 @@ #include #ifndef ENABLE_CARD_SUPPORT -#error no configured for card support. +#error not configured for card support. #endif #include #include @@ -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, diff --git a/g10/status.c b/g10/status.c index cde0c8d77..d39bc683b 100644 --- a/g10/status.c +++ b/g10/status.c @@ -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; + } + } +} + diff --git a/g10/status.h b/g10/status.h index 68da60d28..73cc3f1a9 100644 --- a/g10/status.h +++ b/g10/status.h @@ -100,6 +100,7 @@ #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*/ diff --git a/include/util.h b/include/util.h index 08ea7a78e..da2758a02 100644 --- a/include/util.h +++ b/include/util.h @@ -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 ); diff --git a/util/ChangeLog b/util/ChangeLog index 50e25370b..5501fd5c8 100644 --- a/util/ChangeLog +++ b/util/ChangeLog @@ -1,3 +1,7 @@ +2003-10-29 Werner Koch + + * miscutil.c (answer_is_okay_cancel): New. + 2003-10-25 Werner Koch * Makefile.am: Replaced INTLLIBS by LIBINTL. diff --git a/util/miscutil.c b/util/miscutil.c index b266d27d4..e0ea0e7d2 100644 --- a/util/miscutil.c +++ b/util/miscutil.c @@ -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; +}