[svn] GnuPG - r5020 - trunk/scd
svn author wk
cvs at cvs.gnupg.org
Wed May 20 18:12:25 CEST 2009
Author: wk
Date: 2009-05-20 18:12:25 +0200 (Wed, 20 May 2009)
New Revision: 5020
Modified:
trunk/scd/ChangeLog
trunk/scd/apdu.c
trunk/scd/app-openpgp.c
Log:
Make PIN changing code work for v2 cards.
Modified: trunk/scd/ChangeLog
===================================================================
--- trunk/scd/ChangeLog 2009-05-20 10:25:58 UTC (rev 5019)
+++ trunk/scd/ChangeLog 2009-05-20 16:12:25 UTC (rev 5020)
@@ -1,3 +1,10 @@
+2009-05-20 Werner Koch <wk at 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 at g10code.com>
* scdaemon.c (create_server_socket): Use SUN_LEN.
Modified: trunk/scd/apdu.c
===================================================================
--- trunk/scd/apdu.c 2009-05-20 10:25:58 UTC (rev 5019)
+++ trunk/scd/apdu.c 2009-05-20 16:12:25 UTC (rev 5020)
@@ -1869,7 +1869,7 @@
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 @@
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 @@
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)
Modified: trunk/scd/app-openpgp.c
===================================================================
--- trunk/scd/app-openpgp.c 2009-05-20 10:25:58 UTC (rev 5019)
+++ trunk/scd/app-openpgp.c 2009-05-20 16:12:25 UTC (rev 5020)
@@ -1420,7 +1420,7 @@
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 @@
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 @@
}
+/* 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 @@
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);
+ rc = build_enter_admin_pin_prompt (app, &prompt);
+ if (rc)
+ return rc;
- 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");
-
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 @@
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 @@
-/* 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 @@
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,77 +1878,138 @@
rc = gpg_error (GPG_ERR_INV_ID);
goto leave;
}
- else if (reset_mode || chvno == 3)
+
+ if (!app->app_local->extcap.is_v2)
{
- /* 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;
+ /* Version 1 cards. */
+
+ 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;
+ }
+ 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;
+ }
}
- else if (chvno == 2 && app->app_local->extcap.is_v2)
+ else
{
- /* 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;
+ /* Version 2 cards. */
- relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
- if (!relptr || valuelen < 7)
+ if (reset_mode)
{
- log_error (_("error retrieving CHV status from card\n"));
- xfree (relptr);
- rc = gpg_error (GPG_ERR_CARD);
- goto leave;
+ /* 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;
}
- remaining = value[5];
- xfree (relptr);
- if (!remaining)
+ else if (chvno == 1 || chvno == 3)
{
- log_error (_("Reset Code not or not anymore available\n"));
- rc = gpg_error (GPG_ERR_BAD_PIN);
- goto leave;
- }
+ int minlen = (chvno ==3)? 8 : 6;
+ char *promptbuf = NULL;
+ const char *prompt;
- rc = pincb (pincb_arg, _("||Please enter the Reset Code for the card"),
- &resetcode);
- if (rc)
+ 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)
{
- log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc));
- goto leave;
+ /* 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;
+ }
}
- if (strlen (resetcode) < 8)
+ else
{
- log_error (_("Reset Code is too short; minimum length is %d\n"), 8);
- rc = gpg_error (GPG_ERR_BAD_PIN);
+ rc = gpg_error (GPG_ERR_INV_ID);
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;
- }
-
if (chvno == 3)
app->did_chv3 = 0;
else
@@ -1931,6 +2028,7 @@
goto leave;
}
+
if (resetcode)
{
char *buffer;
@@ -1966,20 +2064,33 @@
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 @@
wipememory (resetcode, strlen (resetcode));
xfree (resetcode);
}
+ if (oldpinvalue)
+ {
+ wipememory (oldpinvalue, strlen (oldpinvalue));
+ xfree (oldpinvalue);
+ }
return rc;
}
More information about the Gnupg-commits
mailing list