mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
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>
This commit is contained in:
parent
45398518fb
commit
fb10b6cba4
@ -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,
|
||||
|
220
tools/gpg-card.c
220
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;
|
||||
@ -774,10 +804,10 @@ 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, ".");
|
||||
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,8 +1262,44 @@ cmd_list (card_info_t info, char *argstr)
|
||||
else if (opt_info)
|
||||
print_card_list (fp, info, cards, 1);
|
||||
else
|
||||
{
|
||||
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:
|
||||
free_strlist (cards);
|
||||
@ -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,33 +2713,80 @@ 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)
|
||||
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)
|
||||
pinref = "PIV.80";
|
||||
@ -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)
|
||||
{
|
||||
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. */
|
||||
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;
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user