summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2020-06-30 14:36:44 +0200
committerWerner Koch <wk@gnupg.org>2020-06-30 14:41:49 +0200
commitfb10b6cba43f4ed8675093ac25f461de4dacdce9 (patch)
tree5ab56b9fed34be42287665eb0b36dc6f0b8e0f23 /tools
parentecc: Support Ed448/X448 key generation. (diff)
downloadgnupg2-fb10b6cba43f4ed8675093ac25f461de4dacdce9.tar.xz
gnupg2-fb10b6cba43f4ed8675093ac25f461de4dacdce9.zip
card: Better detect removed cards. Add TCOS PIN menu.
* tools/card-call-scd.c (scd_change_pin): Add arg 'nullpin'. * tools/gpg-card.h (struct card_info_s): Add field 'card_removed'. * tools/gpg-card.c (fixup_scd_errors): New. (maybe_set_card_removed): New. (list_one_kinfo): Change type of first arg to get access to INFO. Set card_removed flag. (list_all_kinfo): Improve label alignment. (cmd_list): Check that the current card is still available. (cmd_passwd): Add option --nullpin and menu to chnage TCOS PINs. (dispatch_command): Handle card_removed flag. (interactive_loop): Ditto. -- Note that that I was not able to change the NullPIN of the standard PIN using a Signature V2 Brainpool test card. Changing the NullPIN of the QES PIN worked, though. I checked the commands send to scdaemon and they were correct - I used the very same command with gpg-connect-agent last week to set a Pin for a production Brainpool Signature card. Thus this might be a problem with this specific test card. Signed-off-by: Werner Koch <wk@gnupg.org>
Diffstat (limited to 'tools')
-rw-r--r--tools/card-call-scd.c15
-rw-r--r--tools/gpg-card.c234
-rw-r--r--tools/gpg-card.h3
3 files changed, 199 insertions, 53 deletions
diff --git a/tools/card-call-scd.c b/tools/card-call-scd.c
index 1116e072e..8e029a2bf 100644
--- a/tools/card-call-scd.c
+++ b/tools/card-call-scd.c
@@ -1586,17 +1586,10 @@ scd_applist (strlist_t *result, int all)
-/* Change the PIN of an OpenPGP card or reset the retry counter.
- * CHVNO 1: Change the PIN
- * 2: For v1 cards: Same as 1.
- * For v2 cards: Reset the PIN using the Reset Code.
- * 3: Change the admin PIN
- * 101: Set a new PIN and reset the retry counter
- * 102: For v1 cars: Same as 101.
- * For v2 cards: Set a new Reset Code.
- */
+/* Change the PIN of a card or reset the retry counter. If NULLPIN is
+ * set the TCOS specific NullPIN is changed. */
gpg_error_t
-scd_change_pin (const char *pinref, int reset_mode)
+scd_change_pin (const char *pinref, int reset_mode, int nullpin)
{
gpg_error_t err;
char line[ASSUAN_LINELENGTH];
@@ -1610,7 +1603,7 @@ scd_change_pin (const char *pinref, int reset_mode)
dfltparm.ctx = agent_ctx;
snprintf (line, sizeof line, "SCD PASSWD%s %s",
- reset_mode? " --reset":"", pinref);
+ nullpin? " --nullpin": reset_mode? " --reset":"", pinref);
err = assuan_transact (agent_ctx, line,
NULL, NULL,
default_inq_cb, &dfltparm,
diff --git a/tools/gpg-card.c b/tools/gpg-card.c
index 998c3f060..96b2bcf44 100644
--- a/tools/gpg-card.c
+++ b/tools/gpg-card.c
@@ -409,6 +409,34 @@ get_data_from_file (const char *fname, char **r_buffer, size_t *r_buflen)
}
+/* Fixup the ENODEV error from scdaemon which we may see after
+ * removing a card due to scdaemon scanning for readers with cards.
+ * We also map the CAERD REMOVED error to the more useful CARD_NOT
+ * PRESENT. */
+static gpg_error_t
+fixup_scd_errors (gpg_error_t err)
+{
+ if ((gpg_err_code (err) == GPG_ERR_ENODEV
+ || gpg_err_code (err) == GPG_ERR_CARD_REMOVED)
+ && gpg_err_source (err) == GPG_ERR_SOURCE_SCD)
+ err = gpg_error (GPG_ERR_CARD_NOT_PRESENT);
+ return err;
+}
+
+
+/* Set the card removed flag from INFO depending on ERR. This does
+ * not clear the flag. */
+static gpg_error_t
+maybe_set_card_removed (card_info_t info, gpg_error_t err)
+{
+ if ((gpg_err_code (err) == GPG_ERR_ENODEV
+ || gpg_err_code (err) == GPG_ERR_CARD_REMOVED)
+ && gpg_err_source (err) == GPG_ERR_SOURCE_SCD)
+ info->card_removed = 1;
+ return err;
+}
+
+
/* Write LENGTH bytes from BUFFER to file FNAME. Return 0 on
* success. */
static gpg_error_t
@@ -601,10 +629,11 @@ mem_is_zero (const char *mem, unsigned int memlen)
/* Helper to list a single keyref. LABEL_KEYREF is a fallback key
* reference if no info is available; it may be NULL. */
static void
-list_one_kinfo (key_info_t firstkinfo, key_info_t kinfo,
+list_one_kinfo (card_info_t info, key_info_t kinfo,
const char *label_keyref, estream_t fp, int no_key_lookup)
{
gpg_error_t err;
+ key_info_t firstkinfo = info->kinfo;
keyblock_t keyblock = NULL;
keyblock_t kb;
pubkey_t pubkey;
@@ -643,7 +672,7 @@ list_one_kinfo (key_info_t firstkinfo, key_info_t kinfo,
}
tty_fprintf (fp, "\n");
- if (!scd_readkey (kinfo->keyref, &s_pkey))
+ if (!(err = scd_readkey (kinfo->keyref, &s_pkey)))
{
char *tmp = pubkey_algo_string (s_pkey, NULL);
tty_fprintf (fp, " algorithm ..: %s\n", tmp);
@@ -653,6 +682,7 @@ list_one_kinfo (key_info_t firstkinfo, key_info_t kinfo,
}
else
{
+ maybe_set_card_removed (info, err);
tty_fprintf (fp, " algorithm ..: %s\n", kinfo->keyalgo);
}
@@ -751,7 +781,7 @@ list_all_kinfo (card_info_t info, keyinfolabel_t labels, estream_t fp,
int no_key_lookup)
{
key_info_t kinfo;
- int idx, i;
+ int idx, i, j;
/* Print the keyinfo. We first print those we known and then all
* remaining item. */
@@ -763,7 +793,7 @@ list_all_kinfo (card_info_t info, keyinfolabel_t labels, estream_t fp,
{
tty_fprintf (fp, "%s", labels[idx].label);
kinfo = find_kinfo (info, labels[idx].keyref);
- list_one_kinfo (info->kinfo, kinfo, labels[idx].keyref,
+ list_one_kinfo (info, kinfo, labels[idx].keyref,
fp, no_key_lookup);
if (kinfo)
kinfo->xflag = 1;
@@ -773,11 +803,11 @@ list_all_kinfo (card_info_t info, keyinfolabel_t labels, estream_t fp,
{
if (kinfo->xflag)
continue;
- tty_fprintf (fp, "Key %s ", kinfo->keyref);
- for (i=5+strlen (kinfo->keyref); i < 18; i++)
- tty_fprintf (fp, ".");
+ tty_fprintf (fp, "Key %s", kinfo->keyref);
+ for (i=4+strlen (kinfo->keyref), j=0; i < 18; i++, j=1)
+ tty_fprintf (fp, j? ".":" ");
tty_fprintf (fp, ":");
- list_one_kinfo (info->kinfo, kinfo, NULL, fp, no_key_lookup);
+ list_one_kinfo (info, kinfo, NULL, fp, no_key_lookup);
}
}
@@ -1206,7 +1236,7 @@ cmd_list (card_info_t info, char *argstr)
err = scd_switchcard (sl->d);
need_learn = 1;
}
- else /* --info with not args - show app list. */
+ else /* show app list. */
{
err = scd_applist (&cards, 1);
if (err)
@@ -1232,7 +1262,43 @@ cmd_list (card_info_t info, char *argstr)
else if (opt_info)
print_card_list (fp, info, cards, 1);
else
- list_card (info, opt_no_key_lookup);
+ {
+ size_t snlen;
+ const char *s;
+
+ /* First get the list of active cards and check whether the
+ * current card is still in the list. If not the card has
+ * been removed. Note that during the listing the card
+ * remove state might also be detected but only if an access
+ * to the scdaemon is required; it is anyway better to test
+ * that before starting a listing. */
+ free_strlist (cards);
+ err = scd_cardlist (&cards);
+ if (err)
+ goto leave;
+ for (sl = cards; sl; sl = sl->next)
+ {
+ if (info && info->serialno)
+ {
+ s = strchr (sl->d, ' ');
+ if (s)
+ snlen = s - sl->d;
+ else
+ snlen = strlen (sl->d);
+ if (strlen (info->serialno) == snlen
+ && !memcmp (info->serialno, sl->d, snlen))
+ break;
+ }
+ }
+ if (!sl)
+ {
+ info->need_sn_cmd = 1;
+ err = gpg_error (GPG_ERR_CARD_REMOVED);
+ goto leave;
+ }
+
+ list_card (info, opt_no_key_lookup);
+ }
}
leave:
@@ -2560,16 +2626,18 @@ cmd_passwd (card_info_t info, char *argstr)
char *answer = NULL;
const char *pinref = NULL;
int reset_mode = 0;
+ int nullpin = 0;
int menu_used = 0;
if (!info)
return print_help
- ("PASSWD [--reset] [PINREF]\n\n"
+ ("PASSWD [--reset|--nullpin] [PINREF]\n\n"
"Change or unblock the PINs. Note that in interactive mode\n"
"and without a PINREF a menu is presented for certain cards;\n"
"in non-interactive and without a PINREF a default value is\n"
"used for these cards. The option --reset is used with TCOS\n"
- "cards to reset the PIN using the PUK or vice versa.\n",
+ "cards to reset the PIN using the PUK or vice versa; --nullpin\n"
+ "is used for these cards to set the intial PIN.",
0);
if (opt.interactive || opt.verbose)
@@ -2580,10 +2648,12 @@ cmd_passwd (card_info_t info, char *argstr)
if (has_option (argstr, "--reset"))
reset_mode = 1;
+ else if (has_option (argstr, "--nullpin"))
+ nullpin = 1;
argstr = skip_options (argstr);
- /* If --reset has been given we force non-interactive mode. */
- if (*argstr || reset_mode)
+ /* If --reset or --nullpin has been given we force non-interactive mode. */
+ if (*argstr || reset_mode || nullpin)
{
pinref = argstr;
if (!*pinref)
@@ -2643,32 +2713,79 @@ cmd_passwd (card_info_t info, char *argstr)
}
else if (opt.interactive && info->apptype == APP_TYPE_NKS)
{
+ int for_qualified = 0;
+
menu_used = 1;
- while (!pinref)
+
+ for (;;)
{
xfree (answer);
- answer = get_selection (" 1 - change Global PIN 1\n"
- " 2 - change Global PUK\n"
- " 3 - change SigG PIN 1\n"
- " 4 - change SigG PUK\n"
- "11 - reset Global PIN 1\n"
- "12 - reset Global PUK\n"
- "13 - reset Sig PIN\n"
- "14 - reset Sig PUK\n"
+ answer = get_selection (" 1 - Standard PIN/PUK\n"
+ " 2 - PIN/PUK for qualified signature\n"
" Q - quit\n");
if (!ascii_strcasecmp (answer, "q"))
goto leave;
- else if (!strcmp (answer, "1") || !strcmp (answer, "11"))
- pinref = "PW1.CH";
- else if (!strcmp (answer, "2") || !strcmp (answer, "12"))
- pinref = "PW2.CH";
- else if (!strcmp (answer, "3") || !strcmp (answer, "13"))
- pinref = "PW1.CH.SIG";
- else if (!strcmp (answer, "4") || !strcmp (answer, "14"))
- pinref = "PW2.CH.SIG";
- /* Set reset mode for 11 to 14. */
- if (pinref && strlen (answer) == 2)
- reset_mode = 1;
+ else if (!strcmp (answer, "1"))
+ break;
+ else if (!strcmp (answer, "2"))
+ {
+ for_qualified = 1;
+ break;
+ }
+ }
+
+ log_assert (DIM (info->chvinfo) >= 4);
+ if (info->chvinfo[for_qualified? 2 : 0] == -4)
+ {
+ while (!pinref)
+ {
+ xfree (answer);
+ answer = get_selection
+ ("The NullPIN is still active on this card.\n"
+ "You need to choose and set a PIN first.\n"
+ "\n"
+ " 1 - Set your PIN\n"
+ " Q - quit\n");
+ if (!ascii_strcasecmp (answer, "q"))
+ goto leave;
+ else if (!strcmp (answer, "1"))
+ {
+ pinref = for_qualified? "PW1.CH.SIG" : "PW1.CH";
+ nullpin = 1;
+ }
+ }
+ }
+ else
+ {
+ while (!pinref)
+ {
+ xfree (answer);
+ answer = get_selection (" 1 - change PIN\n"
+ " 2 - reset PIN\n"
+ " 3 - change PUK\n"
+ " 4 - reset PUK\n"
+ " Q - quit\n");
+ if (!ascii_strcasecmp (answer, "q"))
+ goto leave;
+ else if (!strcmp (answer, "1"))
+ {
+ pinref = for_qualified? "PW1.CH.SIG" : "PW1.CH";
+ }
+ else if (!strcmp (answer, "2"))
+ {
+ pinref = for_qualified? "PW1.CH.SIG" : "PW1.CH";
+ reset_mode = 1;
+ }
+ else if (!strcmp (answer, "3"))
+ {
+ pinref = for_qualified? "PW2.CH.SIG" : "PW2.CH";
+ }
+ else if (!strcmp (answer, "4"))
+ {
+ pinref = for_qualified? "PW2.CH.SIG" : "PW2.CH";
+ reset_mode = 1;
+ }
+ }
}
}
else if (info->apptype == APP_TYPE_PIV)
@@ -2679,7 +2796,7 @@ cmd_passwd (card_info_t info, char *argstr)
goto leave;
}
- err = scd_change_pin (pinref, reset_mode);
+ err = scd_change_pin (pinref, reset_mode, nullpin);
if (err)
{
if (!opt.interactive && !menu_used && !opt.verbose)
@@ -2713,6 +2830,9 @@ cmd_passwd (card_info_t info, char *argstr)
log_info ("PIN resetted.\n");
else
log_info ("PIN changed.\n");
+
+ /* Update the CHV status. */
+ err = scd_getattr ("CHV-STATUS", info);
}
leave:
@@ -2753,7 +2873,7 @@ cmd_unblock (card_info_t info)
}
else
{
- err = scd_change_pin ("OPENPGP.2", 0);
+ err = scd_change_pin ("OPENPGP.2", 0, 0);
if (!err)
log_info ("PIN changed.\n");
}
@@ -2761,7 +2881,7 @@ cmd_unblock (card_info_t info)
else if (info->apptype == APP_TYPE_PIV)
{
/* Unblock the Application PIN. */
- err = scd_change_pin ("PIV.80", 1);
+ err = scd_change_pin ("PIV.80", 1, 0);
if (!err)
log_info ("PIN unblocked and changed.\n");
}
@@ -3477,11 +3597,15 @@ dispatch_command (card_info_t info, const char *orig_command)
err = scd_learn (info);
if (err)
{
+ err = fixup_scd_errors (err);
log_error ("Error reading card: %s\n", gpg_strerror (err));
goto leave;
}
}
+ if (info)
+ info->card_removed = 0;
+
switch (cmd)
{
case cmdNOP:
@@ -3568,8 +3692,17 @@ dispatch_command (card_info_t info, const char *orig_command)
es_fflush (es_stdout);
if (gpg_err_code (err) == GPG_ERR_EOF && cmd != cmdQUIT)
err = gpg_error (GPG_ERR_GENERAL);
+
+ if (!err && info && info->card_removed)
+ {
+ info->card_removed = 0;
+ info->need_sn_cmd = 1;
+ err = gpg_error (GPG_ERR_CARD_REMOVED);
+ }
+
if (err && gpg_err_code (err) != GPG_ERR_EOF)
{
+ err = fixup_scd_errors (err);
if (ignore_error)
{
log_info ("Command '%s' failed: %s\n", command, gpg_strerror (err));
@@ -3624,7 +3757,10 @@ interactive_loop (void)
{
err = cmd_list (info, "");
if (err)
- log_error ("Error reading card: %s\n", gpg_strerror (err));
+ {
+ err = fixup_scd_errors (err);
+ log_error ("Error reading card: %s\n", gpg_strerror (err));
+ }
else
{
tty_printf("\n");
@@ -3696,12 +3832,19 @@ interactive_loop (void)
{
/* Without a serial number most commands won't work.
* Catch it here. */
- tty_printf ("\n");
- tty_printf ("Serial number missing\n");
- continue;
+ if (cmd == cmdRESET || cmd == cmdLIST)
+ info->need_sn_cmd = 1;
+ else
+ {
+ tty_printf ("\n");
+ tty_printf ("Serial number missing\n");
+ continue;
+ }
}
}
+ if (info)
+ info->card_removed = 0;
err = 0;
switch (cmd)
{
@@ -3791,6 +3934,13 @@ interactive_loop (void)
break;
} /* End command switch. */
+ if (!err && info && info->card_removed)
+ {
+ info->card_removed = 0;
+ info->need_sn_cmd = 1;
+ err = gpg_error (GPG_ERR_CARD_REMOVED);
+ }
+
if (gpg_err_code (err) == GPG_ERR_CANCELED)
tty_fprintf (NULL, "\n");
else if (err)
@@ -3802,6 +3952,8 @@ interactive_loop (void)
s = cmds[i].name;
break;
}
+
+ err = fixup_scd_errors (err);
log_error ("Command '%s' failed: %s\n", s, gpg_strerror (err));
if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT)
info->need_sn_cmd = 1;
diff --git a/tools/gpg-card.h b/tools/gpg-card.h
index 82064999c..56e117bf3 100644
--- a/tools/gpg-card.h
+++ b/tools/gpg-card.h
@@ -139,6 +139,7 @@ struct card_info_s
{
int initialized; /* True if a learn command was successful. */
int need_sn_cmd; /* The SERIALNO command needs to be issued. */
+ int card_removed; /* Helper flag set by some listing functions. */
int error; /* private. */
char *reader; /* Reader information. */
char *cardtype; /* NULL or type of the card. */
@@ -232,7 +233,7 @@ gpg_error_t scd_readcert (const char *certidstr,
gpg_error_t scd_readkey (const char *keyrefstr, gcry_sexp_t *r_result);
gpg_error_t scd_cardlist (strlist_t *result);
gpg_error_t scd_applist (strlist_t *result, int all);
-gpg_error_t scd_change_pin (const char *pinref, int reset_mode);
+gpg_error_t scd_change_pin (const char *pinref, int reset_mode, int nullpin);
gpg_error_t scd_checkpin (const char *serialno);
unsigned long agent_get_s2k_count (void);