mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
Make PIN changing code work for v2 cards.
This commit is contained in:
parent
41ee7a14cf
commit
e095815c4d
@ -1,3 +1,10 @@
|
||||
2009-05-20 Werner Koch <wk@g10code.com>
|
||||
|
||||
* app-openpgp.c (verify_chv2): Add case for v2 cards.
|
||||
(verify_chv3): Factor some code out to ..
|
||||
(build_enter_admin_pin_prompt): .. new.
|
||||
(do_change_pin): Properly handle v2 cards.
|
||||
|
||||
2009-05-19 Werner Koch <wk@g10code.com>
|
||||
|
||||
* scdaemon.c (create_server_socket): Use SUN_LEN.
|
||||
|
@ -1869,7 +1869,7 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
|
||||
return err;
|
||||
|
||||
if (DBG_CARD_IO)
|
||||
log_printhex (" APDU_data:", apdu, apdulen);
|
||||
log_printhex (" raw apdu:", apdu, apdulen);
|
||||
|
||||
maxbuflen = *buflen;
|
||||
if (pininfo)
|
||||
@ -2847,7 +2847,7 @@ send_le (int slot, int class, int ins, int p0, int p1,
|
||||
return SW_HOST_NO_DRIVER;
|
||||
|
||||
if (DBG_CARD_IO)
|
||||
log_debug ("send apdu: c=%02X i=%02X p0=%02X p1=%02X lc=%d le=%d em=%d\n",
|
||||
log_debug ("send apdu: c=%02X i=%02X p1=%02X p2=%02X lc=%d le=%d em=%d\n",
|
||||
class, ins, p0, p1, lc, le, extended_mode);
|
||||
|
||||
if (lc != -1 && (lc > 255 || lc < 0))
|
||||
@ -3036,7 +3036,7 @@ send_le (int slot, int class, int ins, int p0, int p1,
|
||||
log_debug (" response: sw=%04X datalen=%d\n",
|
||||
sw, (unsigned int)resultlen);
|
||||
if ( !retbuf && (sw == SW_SUCCESS || (sw & 0xff00) == SW_MORE_DATA))
|
||||
log_printhex (" dump: ", result, resultlen);
|
||||
log_printhex (" dump: ", result, resultlen);
|
||||
}
|
||||
|
||||
if (sw == SW_SUCCESS || sw == SW_EOF_REACHED)
|
||||
|
@ -1420,7 +1420,7 @@ do_readcert (app_t app, const char *certid,
|
||||
for the pinentry. CHVNO must be either 1 or 2. SIGCOUNT is only
|
||||
used with CHV1. PINVALUE is the address of a pointer which will
|
||||
receive a newly allocated block with the actual PIN (this is useful
|
||||
in case that PIN shall be used for another verifiy operation). The
|
||||
in case that PIN shall be used for another verify operation). The
|
||||
caller needs to free this value. If the function returns with
|
||||
success and NULL is stored at PINVALUE, the caller should take this
|
||||
as an indication that the keypad has been used.
|
||||
@ -1553,30 +1553,42 @@ verify_chv2 (app_t app,
|
||||
char *pinvalue;
|
||||
|
||||
if (app->did_chv2)
|
||||
return 0; /* We already verified CHV2. */
|
||||
return 0; /* We already verified CHV2 (PW1 for v2 cards). */
|
||||
|
||||
rc = verify_a_chv (app, pincb, pincb_arg, 2, 0, &pinvalue);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
app->did_chv2 = 1;
|
||||
|
||||
if (!app->did_chv1 && !app->force_chv1 && pinvalue)
|
||||
if (app->app_local->extcap.is_v2)
|
||||
{
|
||||
/* For convenience we verify CHV1 here too. We do this only if
|
||||
the card is not configured to require a verification before
|
||||
each CHV1 controlled operation (force_chv1) and if we are not
|
||||
using the keypad (PINVALUE == NULL). */
|
||||
rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue));
|
||||
if (gpg_err_code (rc) == GPG_ERR_BAD_PIN)
|
||||
rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED);
|
||||
/* Version two cards don't have a CHV2 anymore. We need to
|
||||
verify CHV1 (now called PW1) instead. */
|
||||
rc = verify_a_chv (app, pincb, pincb_arg, 1, 0, &pinvalue);
|
||||
if (rc)
|
||||
return rc;
|
||||
app->did_chv2 = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Version 1 cards only. */
|
||||
rc = verify_a_chv (app, pincb, pincb_arg, 2, 0, &pinvalue);
|
||||
if (rc)
|
||||
return rc;
|
||||
app->did_chv2 = 1;
|
||||
|
||||
if (!app->did_chv1 && !app->force_chv1 && pinvalue)
|
||||
{
|
||||
log_error (_("verify CHV%d failed: %s\n"), 1, gpg_strerror (rc));
|
||||
flush_cache_after_error (app);
|
||||
/* For convenience we verify CHV1 here too. We do this only
|
||||
if the card is not configured to require a verification
|
||||
before each CHV1 controlled operation (force_chv1) and if
|
||||
we are not using the keypad (PINVALUE == NULL). */
|
||||
rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue));
|
||||
if (gpg_err_code (rc) == GPG_ERR_BAD_PIN)
|
||||
rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED);
|
||||
if (rc)
|
||||
{
|
||||
log_error (_("verify CHV%d failed: %s\n"), 1, gpg_strerror (rc));
|
||||
flush_cache_after_error (app);
|
||||
}
|
||||
else
|
||||
app->did_chv1 = 1;
|
||||
}
|
||||
else
|
||||
app->did_chv1 = 1;
|
||||
}
|
||||
xfree (pinvalue);
|
||||
|
||||
@ -1584,6 +1596,56 @@ verify_chv2 (app_t app,
|
||||
}
|
||||
|
||||
|
||||
/* Build the prompt to enter the Admin PIN. The prompt depends on the
|
||||
current sdtate of the card. */
|
||||
static gpg_error_t
|
||||
build_enter_admin_pin_prompt (app_t app, char **r_prompt)
|
||||
{
|
||||
void *relptr;
|
||||
unsigned char *value;
|
||||
size_t valuelen;
|
||||
int remaining;
|
||||
char *prompt;
|
||||
|
||||
*r_prompt = NULL;
|
||||
|
||||
relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
|
||||
if (!relptr || valuelen < 7)
|
||||
{
|
||||
log_error (_("error retrieving CHV status from card\n"));
|
||||
xfree (relptr);
|
||||
return gpg_error (GPG_ERR_CARD);
|
||||
}
|
||||
if (value[6] == 0)
|
||||
{
|
||||
log_info (_("card is permanently locked!\n"));
|
||||
xfree (relptr);
|
||||
return gpg_error (GPG_ERR_BAD_PIN);
|
||||
}
|
||||
remaining = value[6];
|
||||
xfree (relptr);
|
||||
|
||||
log_info(_("%d Admin PIN attempts remaining before card"
|
||||
" is permanently locked\n"), remaining);
|
||||
|
||||
if (remaining < 3)
|
||||
{
|
||||
/* TRANSLATORS: Do not translate the "|A|" prefix but keep it at
|
||||
the start of the string. Use %%0A to force a linefeed. */
|
||||
prompt = xtryasprintf (_("|A|Please enter the Admin PIN%%0A"
|
||||
"[remaining attempts: %d]"), remaining);
|
||||
}
|
||||
else
|
||||
prompt = xtrystrdup (_("|A|Please enter the Admin PIN"));
|
||||
|
||||
if (!prompt)
|
||||
return gpg_error_from_syserror ();
|
||||
|
||||
*r_prompt = prompt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Verify CHV3 if required. */
|
||||
static gpg_error_t
|
||||
verify_chv3 (app_t app,
|
||||
@ -1602,65 +1664,25 @@ verify_chv3 (app_t app,
|
||||
|
||||
if (!app->did_chv3)
|
||||
{
|
||||
void *relptr;
|
||||
unsigned char *value;
|
||||
size_t valuelen;
|
||||
iso7816_pininfo_t pininfo;
|
||||
int minlen = 8;
|
||||
int remaining;
|
||||
char *prompt_buffer = NULL;
|
||||
const char *prompt;
|
||||
char *prompt;
|
||||
|
||||
memset (&pininfo, 0, sizeof pininfo);
|
||||
pininfo.mode = 1;
|
||||
pininfo.minlen = minlen;
|
||||
|
||||
relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
|
||||
if (!relptr || valuelen < 7)
|
||||
{
|
||||
log_error (_("error retrieving CHV status from card\n"));
|
||||
xfree (relptr);
|
||||
return gpg_error (GPG_ERR_CARD);
|
||||
}
|
||||
if (value[6] == 0)
|
||||
{
|
||||
log_info (_("card is permanently locked!\n"));
|
||||
xfree (relptr);
|
||||
return gpg_error (GPG_ERR_BAD_PIN);
|
||||
}
|
||||
remaining = value[6];
|
||||
xfree (relptr);
|
||||
|
||||
log_info(_("%d Admin PIN attempts remaining before card"
|
||||
" is permanently locked\n"), remaining);
|
||||
|
||||
if (remaining < 3)
|
||||
{
|
||||
/* TRANSLATORS: Do not translate the "|A|" prefix but keep
|
||||
it at the start of the string. Use %%0A to force a
|
||||
lienfeed. */
|
||||
#define PROMPTSTRING _("|A|Please enter the Admin PIN%%0A" \
|
||||
"[remaining attempts: %d]")
|
||||
size_t promptsize = strlen (PROMPTSTRING) + 50;
|
||||
|
||||
prompt_buffer = xtrymalloc (promptsize);
|
||||
if (!prompt_buffer)
|
||||
return gpg_error_from_syserror ();
|
||||
snprintf (prompt_buffer, promptsize-1, PROMPTSTRING, remaining);
|
||||
prompt = prompt_buffer;
|
||||
#undef PROMPTSTRING
|
||||
}
|
||||
else
|
||||
prompt = _("|A|Please enter the Admin PIN");
|
||||
rc = build_enter_admin_pin_prompt (app, &prompt);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!opt.disable_keypad
|
||||
&& !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
|
||||
{
|
||||
/* The reader supports the verify command through the keypad. */
|
||||
rc = pincb (pincb_arg, prompt, NULL);
|
||||
xfree (prompt);
|
||||
prompt = NULL;
|
||||
xfree (prompt_buffer);
|
||||
prompt_buffer = NULL;
|
||||
if (rc)
|
||||
{
|
||||
log_info (_("PIN callback returned error: %s\n"),
|
||||
@ -1676,9 +1698,8 @@ verify_chv3 (app_t app,
|
||||
char *pinvalue;
|
||||
|
||||
rc = pincb (pincb_arg, prompt, &pinvalue);
|
||||
xfree (prompt);
|
||||
prompt = NULL;
|
||||
xfree (prompt_buffer);
|
||||
prompt_buffer = NULL;
|
||||
if (rc)
|
||||
{
|
||||
log_info (_("PIN callback returned error: %s\n"),
|
||||
@ -1821,7 +1842,21 @@ do_writecert (app_t app, ctrl_t ctrl,
|
||||
|
||||
|
||||
|
||||
/* Handle the PASSWD command. */
|
||||
/* Handle the PASSWD command. The following combinations are
|
||||
possible:
|
||||
|
||||
Flags CHVNO Vers. Description
|
||||
RESET 1 1 Verify CHV3 and set a new CHV1 and CHV2
|
||||
RESET 1 2 Verify PW3 and set a new PW1.
|
||||
RESET 2 1 Verify CHV3 and set a new CHV1 and CHV2.
|
||||
RESET 2 2 Verify PW3 and set a new Reset Code.
|
||||
RESET 3 any Returns GPG_ERR_INV_ID.
|
||||
- 1 1 Verify CHV2 and set a new CHV1 and CHV2.
|
||||
- 1 2 Verify PW1 and set a new PW1.
|
||||
- 2 1 Verify CHV2 and set a new CHV1 and CHV2.
|
||||
- 2 2 Verify Reset Code and set a new PW1.
|
||||
- 3 any Verify CHV3/PW3 and set a new CHV3/PW3.
|
||||
*/
|
||||
static gpg_error_t
|
||||
do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
|
||||
unsigned int flags,
|
||||
@ -1831,6 +1866,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
|
||||
int rc = 0;
|
||||
int chvno = atoi (chvnostr);
|
||||
char *resetcode = NULL;
|
||||
char *oldpinvalue = NULL;
|
||||
char *pinvalue;
|
||||
int reset_mode = !!(flags & APP_CHANGE_FLAG_RESET);
|
||||
int set_resetcode = 0;
|
||||
@ -1842,75 +1878,136 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
|
||||
rc = gpg_error (GPG_ERR_INV_ID);
|
||||
goto leave;
|
||||
}
|
||||
else if (reset_mode || chvno == 3)
|
||||
{
|
||||
/* We always require that the PIN is entered. */
|
||||
app->did_chv3 = 0;
|
||||
rc = verify_chv3 (app, pincb, pincb_arg);
|
||||
if (rc)
|
||||
goto leave;
|
||||
|
||||
if (chvno == 2 && app->app_local->extcap.is_v2)
|
||||
set_resetcode = 1;
|
||||
}
|
||||
else if (chvno == 2 && app->app_local->extcap.is_v2)
|
||||
{
|
||||
/* There is no PW2 for v2 cards. We use this condition to allow
|
||||
a PW reset using the Reset Code. */
|
||||
void *relptr;
|
||||
unsigned char *value;
|
||||
size_t valuelen;
|
||||
int remaining;
|
||||
|
||||
relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
|
||||
if (!relptr || valuelen < 7)
|
||||
if (!app->app_local->extcap.is_v2)
|
||||
{
|
||||
/* Version 1 cards. */
|
||||
|
||||
if (reset_mode || chvno == 3)
|
||||
{
|
||||
log_error (_("error retrieving CHV status from card\n"));
|
||||
xfree (relptr);
|
||||
rc = gpg_error (GPG_ERR_CARD);
|
||||
/* We always require that the PIN is entered. */
|
||||
app->did_chv3 = 0;
|
||||
rc = verify_chv3 (app, pincb, pincb_arg);
|
||||
if (rc)
|
||||
goto leave;
|
||||
}
|
||||
else if (chvno == 1 || chvno == 2)
|
||||
{
|
||||
/* On a v1.x card CHV1 and CVH2 should always have the same
|
||||
value, thus we enforce it here. */
|
||||
int save_force = app->force_chv1;
|
||||
|
||||
app->force_chv1 = 0;
|
||||
app->did_chv1 = 0;
|
||||
app->did_chv2 = 0;
|
||||
rc = verify_chv2 (app, pincb, pincb_arg);
|
||||
app->force_chv1 = save_force;
|
||||
if (rc)
|
||||
goto leave;
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = gpg_error (GPG_ERR_INV_ID);
|
||||
goto leave;
|
||||
}
|
||||
remaining = value[5];
|
||||
xfree (relptr);
|
||||
if (!remaining)
|
||||
{
|
||||
log_error (_("Reset Code not or not anymore available\n"));
|
||||
rc = gpg_error (GPG_ERR_BAD_PIN);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
rc = pincb (pincb_arg, _("||Please enter the Reset Code for the card"),
|
||||
&resetcode);
|
||||
if (rc)
|
||||
{
|
||||
log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc));
|
||||
goto leave;
|
||||
}
|
||||
if (strlen (resetcode) < 8)
|
||||
{
|
||||
log_error (_("Reset Code is too short; minimum length is %d\n"), 8);
|
||||
rc = gpg_error (GPG_ERR_BAD_PIN);
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
else if (chvno == 1 || chvno == 2)
|
||||
{
|
||||
/* CHV1 and CVH2 should always have the same value, thus we
|
||||
enforce it here. */
|
||||
int save_force = app->force_chv1;
|
||||
|
||||
app->force_chv1 = 0;
|
||||
app->did_chv1 = 0;
|
||||
app->did_chv2 = 0;
|
||||
rc = verify_chv2 (app, pincb, pincb_arg);
|
||||
app->force_chv1 = save_force;
|
||||
if (rc)
|
||||
goto leave;
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = gpg_error (GPG_ERR_INV_ID);
|
||||
goto leave;
|
||||
/* Version 2 cards. */
|
||||
|
||||
if (reset_mode)
|
||||
{
|
||||
/* To reset a PIN the Admin PIN is required. */
|
||||
app->did_chv3 = 0;
|
||||
rc = verify_chv3 (app, pincb, pincb_arg);
|
||||
if (rc)
|
||||
goto leave;
|
||||
|
||||
if (chvno == 2)
|
||||
set_resetcode = 1;
|
||||
}
|
||||
else if (chvno == 1 || chvno == 3)
|
||||
{
|
||||
int minlen = (chvno ==3)? 8 : 6;
|
||||
char *promptbuf = NULL;
|
||||
const char *prompt;
|
||||
|
||||
if (chvno == 3)
|
||||
{
|
||||
rc = build_enter_admin_pin_prompt (app, &promptbuf);
|
||||
if (rc)
|
||||
goto leave;
|
||||
prompt = promptbuf;
|
||||
}
|
||||
else
|
||||
prompt = _("||Please enter the PIN");
|
||||
rc = pincb (pincb_arg, prompt, &oldpinvalue);
|
||||
xfree (promptbuf);
|
||||
promptbuf = NULL;
|
||||
if (rc)
|
||||
{
|
||||
log_info (_("PIN callback returned error: %s\n"),
|
||||
gpg_strerror (rc));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (strlen (oldpinvalue) < minlen)
|
||||
{
|
||||
log_info (_("PIN for CHV%d is too short;"
|
||||
" minimum length is %d\n"), chvno, minlen);
|
||||
rc = gpg_error (GPG_ERR_BAD_PIN);
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
else if (chvno == 2)
|
||||
{
|
||||
/* There is no PW2 for v2 cards. We use this condition to
|
||||
allow a PW reset using the Reset Code. */
|
||||
void *relptr;
|
||||
unsigned char *value;
|
||||
size_t valuelen;
|
||||
int remaining;
|
||||
int minlen = 8;
|
||||
|
||||
relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
|
||||
if (!relptr || valuelen < 7)
|
||||
{
|
||||
log_error (_("error retrieving CHV status from card\n"));
|
||||
xfree (relptr);
|
||||
rc = gpg_error (GPG_ERR_CARD);
|
||||
goto leave;
|
||||
}
|
||||
remaining = value[5];
|
||||
xfree (relptr);
|
||||
if (!remaining)
|
||||
{
|
||||
log_error (_("Reset Code not or not anymore available\n"));
|
||||
rc = gpg_error (GPG_ERR_BAD_PIN);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
rc = pincb (pincb_arg,
|
||||
_("||Please enter the Reset Code for the card"),
|
||||
&resetcode);
|
||||
if (rc)
|
||||
{
|
||||
log_info (_("PIN callback returned error: %s\n"),
|
||||
gpg_strerror (rc));
|
||||
goto leave;
|
||||
}
|
||||
if (strlen (resetcode) < minlen)
|
||||
{
|
||||
log_info (_("Reset Code is too short; minimum length is %d\n"),
|
||||
minlen);
|
||||
rc = gpg_error (GPG_ERR_BAD_PIN);
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = gpg_error (GPG_ERR_INV_ID);
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
|
||||
if (chvno == 3)
|
||||
@ -1931,6 +2028,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
|
||||
goto leave;
|
||||
}
|
||||
|
||||
|
||||
if (resetcode)
|
||||
{
|
||||
char *buffer;
|
||||
@ -1966,20 +2064,33 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
|
||||
rc = iso7816_reset_retry_counter (app->slot, 0x82,
|
||||
pinvalue, strlen (pinvalue));
|
||||
}
|
||||
else
|
||||
else if (!app->app_local->extcap.is_v2)
|
||||
{
|
||||
/* Version 1 cards. */
|
||||
if (chvno == 1 || chvno == 2)
|
||||
{
|
||||
rc = iso7816_change_reference_data (app->slot, 0x81, NULL, 0,
|
||||
pinvalue, strlen (pinvalue));
|
||||
if (!rc && !app->app_local->extcap.is_v2)
|
||||
if (!rc)
|
||||
rc = iso7816_change_reference_data (app->slot, 0x82, NULL, 0,
|
||||
pinvalue, strlen (pinvalue));
|
||||
}
|
||||
else
|
||||
rc = iso7816_change_reference_data (app->slot, 0x80 + chvno, NULL, 0,
|
||||
pinvalue, strlen (pinvalue));
|
||||
else /* CHVNO == 3 */
|
||||
{
|
||||
rc = iso7816_change_reference_data (app->slot, 0x80 + chvno, NULL, 0,
|
||||
pinvalue, strlen (pinvalue));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Version 2 cards. */
|
||||
assert (chvno == 1 || chvno == 3);
|
||||
|
||||
rc = iso7816_change_reference_data (app->slot, 0x80 + chvno,
|
||||
oldpinvalue, strlen (oldpinvalue),
|
||||
pinvalue, strlen (pinvalue));
|
||||
}
|
||||
|
||||
if (pinvalue)
|
||||
{
|
||||
wipememory (pinvalue, strlen (pinvalue));
|
||||
@ -1994,6 +2105,11 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
|
||||
wipememory (resetcode, strlen (resetcode));
|
||||
xfree (resetcode);
|
||||
}
|
||||
if (oldpinvalue)
|
||||
{
|
||||
wipememory (oldpinvalue, strlen (oldpinvalue));
|
||||
xfree (oldpinvalue);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user