scd: First changes to implement a PIN cache.

* scd/command.c (pincache_put): New.  Uses a dummy key for now.
(pincache_get): New.
* scd/app.c (select_application): Flush the PIN cache.
(scd_update_reader_status_file): Ditto.
(maybe_switch_app): Call the new prep_reselect function.
(app_write_learn_status): Ditto.
* scd/app-openpgp.c (cache_pin): New helper to cache a PIN.
(verify_chv2): Call it.
(verify_chv3): Call it.
(clear_chv_status): Call it.
(do_change_pin): Call it.

* scd/app-common.h (struct app_ctx_s): Add function 'prep_select'.
* scd/app-openpgp.c (do_prep_reselect): New stub function.
(app_select_openpgp): Set new stub function.
* scd/app-piv.c (do_prep_reselect): New stub function.
(app_select_piv): Set new stub function.

* scd/app-common.h (struct app_ctx_s): Add parameter ctrl to setattr,
sign, auth, decipher, and check_pin.  Change all implementations and
callers to pass such a parameter.
--

This is work in progress.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2020-01-07 18:45:33 +01:00
parent d5c00354bb
commit fbf97a7856
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
11 changed files with 418 additions and 85 deletions

View File

@ -136,6 +136,7 @@ struct app_ctx_s {
struct app_local_s *app_local; /* Local to the application. */ struct app_local_s *app_local; /* Local to the application. */
struct { struct {
void (*deinit) (app_t app); void (*deinit) (app_t app);
gpg_error_t (*prep_reselect) (app_t app, ctrl_t ctrl);
gpg_error_t (*reselect) (app_t app, ctrl_t ctrl); gpg_error_t (*reselect) (app_t app, ctrl_t ctrl);
gpg_error_t (*learn_status) (app_t app, ctrl_t ctrl, unsigned int flags); gpg_error_t (*learn_status) (app_t app, ctrl_t ctrl, unsigned int flags);
gpg_error_t (*readcert) (app_t app, const char *certid, gpg_error_t (*readcert) (app_t app, const char *certid,
@ -144,22 +145,22 @@ struct app_ctx_s {
const char *certid, unsigned int flags, const char *certid, unsigned int flags,
unsigned char **pk, size_t *pklen); unsigned char **pk, size_t *pklen);
gpg_error_t (*getattr) (app_t app, ctrl_t ctrl, const char *name); gpg_error_t (*getattr) (app_t app, ctrl_t ctrl, const char *name);
gpg_error_t (*setattr) (app_t app, const char *name, gpg_error_t (*setattr) (app_t app, ctrl_t ctrl, const char *name,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const unsigned char *value, size_t valuelen); const unsigned char *value, size_t valuelen);
gpg_error_t (*sign) (app_t app, gpg_error_t (*sign) (app_t app, ctrl_t ctrl,
const char *keyidstr, int hashalgo, const char *keyidstr, int hashalgo,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen ); unsigned char **outdata, size_t *outdatalen );
gpg_error_t (*auth) (app_t app, const char *keyidstr, gpg_error_t (*auth) (app_t app, ctrl_t ctrl, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen); unsigned char **outdata, size_t *outdatalen);
gpg_error_t (*decipher) (app_t app, const char *keyidstr, gpg_error_t (*decipher) (app_t app, ctrl_t ctrl, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
@ -184,7 +185,7 @@ struct app_ctx_s {
const char *chvnostr, unsigned int flags, const char *chvnostr, unsigned int flags,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg); void *pincb_arg);
gpg_error_t (*check_pin) (app_t app, const char *keyidstr, gpg_error_t (*check_pin) (app_t app, ctrl_t ctrl, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg); void *pincb_arg);
gpg_error_t (*with_keygrip) (app_t app, ctrl_t ctrl, int action, gpg_error_t (*with_keygrip) (app_t app, ctrl_t ctrl, int action,

View File

@ -389,7 +389,7 @@ verify_pin (app_t app,
that callback should return the PIN in an allocated buffer and that callback should return the PIN in an allocated buffer and
store that in the 3rd argument. */ store that in the 3rd argument. */
static gpg_error_t static gpg_error_t
do_sign (app_t app, const char *keyidstr, int hashalgo, do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
@ -411,6 +411,8 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
+ the largest OID _prefix above. */ + the largest OID _prefix above. */
int datalen; int datalen;
(void)ctrl;
if (!keyidstr || !*keyidstr) if (!keyidstr || !*keyidstr)
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
if (indatalen != 20 && indatalen != 16 && indatalen != 32 if (indatalen != 20 && indatalen != 16 && indatalen != 32
@ -557,6 +559,7 @@ app_select_dinsig (app_t app)
{ {
app->apptype = APPTYPE_DINSIG; app->apptype = APPTYPE_DINSIG;
app->fnc.prep_reselect = NULL;
app->fnc.reselect = NULL; app->fnc.reselect = NULL;
app->fnc.learn_status = do_learn_status; app->fnc.learn_status = do_learn_status;
app->fnc.readcert = do_readcert; app->fnc.readcert = do_readcert;

View File

@ -313,6 +313,7 @@ app_select_geldkarte (app_t app)
app->apptype = APPTYPE_GELDKARTE; app->apptype = APPTYPE_GELDKARTE;
app->fnc.deinit = do_deinit; app->fnc.deinit = do_deinit;
app->fnc.prep_reselect = NULL;
app->fnc.reselect = NULL; app->fnc.reselect = NULL;
/* If we don't have a serialno yet construct it from the EF_ID. */ /* If we don't have a serialno yet construct it from the EF_ID. */

View File

@ -887,7 +887,7 @@ verify_pin (app_t app, int pwid, const char *desc,
that callback should return the PIN in an allocated buffer and that callback should return the PIN in an allocated buffer and
store that in the 3rd argument. */ store that in the 3rd argument. */
static gpg_error_t static gpg_error_t
do_sign (app_t app, const char *keyidstr, int hashalgo, do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
@ -907,6 +907,8 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
+ the largest OID prefix. */ + the largest OID prefix. */
size_t datalen; size_t datalen;
(void)ctrl;
if (!keyidstr || !*keyidstr) if (!keyidstr || !*keyidstr)
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
switch (indatalen) switch (indatalen)
@ -1022,7 +1024,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
If a PIN is required the PINCB will be used to ask for the PIN; it If a PIN is required the PINCB will be used to ask for the PIN; it
should return the PIN in an allocated buffer and put it into PIN. */ should return the PIN in an allocated buffer and put it into PIN. */
static gpg_error_t static gpg_error_t
do_decipher (app_t app, const char *keyidstr, do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
@ -1034,6 +1036,7 @@ do_decipher (app_t app, const char *keyidstr,
int fid; int fid;
int kid; int kid;
(void)ctrl;
(void)r_info; (void)r_info;
if (!keyidstr || !*keyidstr || !indatalen) if (!keyidstr || !*keyidstr || !indatalen)
@ -1310,7 +1313,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr,
/* Perform a simple verify operation. KEYIDSTR should be NULL or empty. */ /* Perform a simple verify operation. KEYIDSTR should be NULL or empty. */
static gpg_error_t static gpg_error_t
do_check_pin (app_t app, const char *pwidstr, do_check_pin (app_t app, ctrl_t ctrl, const char *pwidstr,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg) void *pincb_arg)
{ {
@ -1319,6 +1322,8 @@ do_check_pin (app_t app, const char *pwidstr,
int is_sigg; int is_sigg;
const char *desc; const char *desc;
(void)ctrl;
desc = parse_pwidstr (pwidstr, 0, &is_sigg, &pwid); desc = parse_pwidstr (pwidstr, 0, &is_sigg, &pwid);
if (!desc) if (!desc)
return gpg_error (GPG_ERR_INV_ID); return gpg_error (GPG_ERR_INV_ID);
@ -1451,6 +1456,7 @@ app_select_nks (app_t app)
log_info ("Detected NKS version: %d\n", app->app_local->nks_version); log_info ("Detected NKS version: %d\n", app->app_local->nks_version);
app->fnc.deinit = do_deinit; app->fnc.deinit = do_deinit;
app->fnc.prep_reselect = NULL;
app->fnc.reselect = NULL; app->fnc.reselect = NULL;
app->fnc.learn_status = do_learn_status; app->fnc.learn_status = do_learn_status;
app->fnc.readcert = do_readcert; app->fnc.readcert = do_readcert;

View File

@ -249,14 +249,14 @@ struct app_local_s {
static unsigned long convert_sig_counter_value (const unsigned char *value, static unsigned long convert_sig_counter_value (const unsigned char *value,
size_t valuelen); size_t valuelen);
static unsigned long get_sig_counter (app_t app); static unsigned long get_sig_counter (app_t app);
static gpg_error_t do_auth (app_t app, const char *keyidstr, static gpg_error_t do_auth (app_t app, ctrl_t ctrl, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen); unsigned char **outdata, size_t *outdatalen);
static void parse_algorithm_attribute (app_t app, int keyno); static void parse_algorithm_attribute (app_t app, int keyno);
static gpg_error_t change_keyattr_from_string static gpg_error_t change_keyattr_from_string
(app_t app, (app_t app, ctrl_t ctrl,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *value, size_t valuelen); const void *value, size_t valuelen);
@ -2128,6 +2128,25 @@ pin2hash_if_kdf (app_t app, int chvno, char *pinvalue, int *r_pinlen)
} }
/* Helper to cache a PIN. If PIN is NULL the cache is cleared. */
static void
cache_pin (app_t app, ctrl_t ctrl, int chvno, const char *pin)
{
const char *keyref;
switch (chvno)
{
case 1: keyref = "1"; break;
case 2: keyref = "2"; break;
case 3: keyref = "3"; break;
default: keyref = NULL; break;
}
if (app->card->cardtype == CARDTYPE_YUBIKEY && keyref)
pincache_put (ctrl, app_get_slot (app), "openpgp", keyref, pin);
}
/* Verify a CHV either using the pinentry or if possible by /* Verify a CHV either using the pinentry or if possible by
using a pinpad. PINCB and PINCB_ARG describe the usual callback using a pinpad. PINCB and PINCB_ARG describe the usual callback
for the pinentry. CHVNO must be either 1 or 2. SIGCOUNT is only for the pinentry. CHVNO must be either 1 or 2. SIGCOUNT is only
@ -2264,7 +2283,7 @@ verify_a_chv (app_t app,
/* Verify CHV2 if required. Depending on the configuration of the /* Verify CHV2 if required. Depending on the configuration of the
card CHV1 will also be verified. */ card CHV1 will also be verified. */
static gpg_error_t static gpg_error_t
verify_chv2 (app_t app, verify_chv2 (app_t app, ctrl_t ctrl,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg) void *pincb_arg)
{ {
@ -2279,6 +2298,7 @@ verify_chv2 (app_t app,
if (rc) if (rc)
return rc; return rc;
app->did_chv2 = 1; app->did_chv2 = 1;
cache_pin (app, ctrl, 2, pinvalue);
if (!app->did_chv1 && !app->force_chv1 && pinvalue) if (!app->did_chv1 && !app->force_chv1 && pinvalue)
{ {
@ -2295,7 +2315,10 @@ verify_chv2 (app_t app,
flush_cache_after_error (app); flush_cache_after_error (app);
} }
else else
app->did_chv1 = 1; {
app->did_chv1 = 1;
cache_pin (app, ctrl, 1, pinvalue);
}
} }
xfree (pinvalue); xfree (pinvalue);
@ -2347,7 +2370,7 @@ build_enter_admin_pin_prompt (app_t app, char **r_prompt)
/* Verify CHV3 if required. */ /* Verify CHV3 if required. */
static gpg_error_t static gpg_error_t
verify_chv3 (app_t app, verify_chv3 (app_t app, ctrl_t ctrl,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg) void *pincb_arg)
{ {
@ -2418,6 +2441,8 @@ verify_chv3 (app_t app,
rc = pin2hash_if_kdf (app, 3, pinvalue, &pinlen); rc = pin2hash_if_kdf (app, 3, pinvalue, &pinlen);
if (!rc) if (!rc)
rc = iso7816_verify (app_get_slot (app), 0x83, pinvalue, pinlen); rc = iso7816_verify (app_get_slot (app), 0x83, pinvalue, pinlen);
if (!rc)
cache_pin (app, ctrl, 3, pinvalue);
xfree (pinvalue); xfree (pinvalue);
} }
@ -2429,6 +2454,7 @@ verify_chv3 (app_t app,
} }
app->did_chv3 = 1; app->did_chv3 = 1;
} }
return rc; return rc;
} }
@ -2436,7 +2462,7 @@ verify_chv3 (app_t app,
/* Handle the SETATTR operation. All arguments are already basically /* Handle the SETATTR operation. All arguments are already basically
checked. */ checked. */
static gpg_error_t static gpg_error_t
do_setattr (app_t app, const char *name, do_setattr (app_t app, ctrl_t ctrl, const char *name,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const unsigned char *value, size_t valuelen) const unsigned char *value, size_t valuelen)
@ -2488,15 +2514,16 @@ do_setattr (app_t app, const char *name,
return gpg_error (GPG_ERR_INV_OBJ); return gpg_error (GPG_ERR_INV_OBJ);
if (table[idx].special == 3) if (table[idx].special == 3)
return change_keyattr_from_string (app, pincb, pincb_arg, value, valuelen); return change_keyattr_from_string (app, ctrl, pincb, pincb_arg,
value, valuelen);
switch (table[idx].need_chv) switch (table[idx].need_chv)
{ {
case 2: case 2:
rc = verify_chv2 (app, pincb, pincb_arg); rc = verify_chv2 (app, ctrl, pincb, pincb_arg);
break; break;
case 3: case 3:
rc = verify_chv3 (app, pincb, pincb_arg); rc = verify_chv3 (app, ctrl, pincb, pincb_arg);
break; break;
default: default:
rc = 0; rc = 0;
@ -2547,7 +2574,6 @@ do_writecert (app_t app, ctrl_t ctrl,
void *pincb_arg, void *pincb_arg,
const unsigned char *certdata, size_t certdatalen) const unsigned char *certdata, size_t certdatalen)
{ {
(void)ctrl;
if (strcmp (certidstr, "OPENPGP.3")) if (strcmp (certidstr, "OPENPGP.3"))
return gpg_error (GPG_ERR_INV_ID); return gpg_error (GPG_ERR_INV_ID);
if (!certdata || !certdatalen) if (!certdata || !certdatalen)
@ -2556,16 +2582,19 @@ do_writecert (app_t app, ctrl_t ctrl,
return gpg_error (GPG_ERR_NOT_SUPPORTED); return gpg_error (GPG_ERR_NOT_SUPPORTED);
if (certdatalen > app->app_local->extcap.max_certlen_3) if (certdatalen > app->app_local->extcap.max_certlen_3)
return gpg_error (GPG_ERR_TOO_LARGE); return gpg_error (GPG_ERR_TOO_LARGE);
return do_setattr (app, "CERT-3", pincb, pincb_arg, certdata, certdatalen); return do_setattr (app, ctrl, "CERT-3", pincb, pincb_arg,
certdata, certdatalen);
} }
static gpg_error_t static gpg_error_t
clear_chv_status (app_t app, int chvno) clear_chv_status (app_t app, ctrl_t ctrl, int chvno)
{ {
unsigned char apdu[4]; unsigned char apdu[4];
gpg_error_t err; gpg_error_t err;
cache_pin (app, ctrl, chvno, NULL);
if (!app->app_local->extcap.is_v2) if (!app->app_local->extcap.is_v2)
return GPG_ERR_UNSUPPORTED_OPERATION; return GPG_ERR_UNSUPPORTED_OPERATION;
@ -2634,8 +2663,6 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
int pinlen0 = 0; int pinlen0 = 0;
int pinlen = 0; int pinlen = 0;
(void)ctrl;
if (digitp (chvnostr)) if (digitp (chvnostr))
chvno = atoi (chvnostr); chvno = atoi (chvnostr);
else if (!ascii_strcasecmp (chvnostr, "OPENPGP.1")) else if (!ascii_strcasecmp (chvnostr, "OPENPGP.1"))
@ -2652,7 +2679,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
pininfo.minlen = minlen; pininfo.minlen = minlen;
if ((flags & APP_CHANGE_FLAG_CLEAR)) if ((flags & APP_CHANGE_FLAG_CLEAR))
return clear_chv_status (app, chvno); return clear_chv_status (app, ctrl, chvno);
if (reset_mode && chvno == 3) if (reset_mode && chvno == 3)
{ {
@ -2667,8 +2694,9 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
if (reset_mode || chvno == 3) if (reset_mode || chvno == 3)
{ {
/* We always require that the PIN is entered. */ /* We always require that the PIN is entered. */
cache_pin (app, ctrl, 3, NULL);
app->did_chv3 = 0; app->did_chv3 = 0;
rc = verify_chv3 (app, pincb, pincb_arg); rc = verify_chv3 (app, ctrl, pincb, pincb_arg);
if (rc) if (rc)
goto leave; goto leave;
} }
@ -2678,10 +2706,12 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
value, thus we enforce it here. */ value, thus we enforce it here. */
int save_force = app->force_chv1; int save_force = app->force_chv1;
cache_pin (app, ctrl, 1, NULL);
cache_pin (app, ctrl, 2, NULL);
app->force_chv1 = 0; app->force_chv1 = 0;
app->did_chv1 = 0; app->did_chv1 = 0;
app->did_chv2 = 0; app->did_chv2 = 0;
rc = verify_chv2 (app, pincb, pincb_arg); rc = verify_chv2 (app, ctrl, pincb, pincb_arg);
app->force_chv1 = save_force; app->force_chv1 = save_force;
if (rc) if (rc)
goto leave; goto leave;
@ -2707,7 +2737,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
/* To reset a PIN the Admin PIN is required. */ /* To reset a PIN the Admin PIN is required. */
use_pinpad = 0; use_pinpad = 0;
app->did_chv3 = 0; app->did_chv3 = 0;
rc = verify_chv3 (app, pincb, pincb_arg); rc = verify_chv3 (app, ctrl, pincb, pincb_arg);
if (rc) if (rc)
goto leave; goto leave;
@ -3263,7 +3293,8 @@ build_ecc_privkey_template (app_t app, int keyno,
/* Helper for do_writekley to change the size of a key. Not ethat /* Helper for do_writekley to change the size of a key. Not ethat
this deletes the entire key without asking. */ this deletes the entire key without asking. */
static gpg_error_t static gpg_error_t
change_keyattr (app_t app, int keyno, const unsigned char *buf, size_t buflen, change_keyattr (app_t app, ctrl_t ctrl,
int keyno, const unsigned char *buf, size_t buflen,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg) void *pincb_arg)
{ {
@ -3272,7 +3303,7 @@ change_keyattr (app_t app, int keyno, const unsigned char *buf, size_t buflen,
assert (keyno >=0 && keyno <= 2); assert (keyno >=0 && keyno <= 2);
/* Prepare for storing the key. */ /* Prepare for storing the key. */
err = verify_chv3 (app, pincb, pincb_arg); err = verify_chv3 (app, ctrl, pincb, pincb_arg);
if (err) if (err)
return err; return err;
@ -3292,7 +3323,7 @@ change_keyattr (app_t app, int keyno, const unsigned char *buf, size_t buflen,
static gpg_error_t static gpg_error_t
change_rsa_keyattr (app_t app, int keyno, unsigned int nbits, change_rsa_keyattr (app_t app, ctrl_t ctrl, int keyno, unsigned int nbits,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg) void *pincb_arg)
{ {
@ -3332,7 +3363,7 @@ change_rsa_keyattr (app_t app, int keyno, unsigned int nbits,
buflen = 6; buflen = 6;
} }
err = change_keyattr (app, keyno, buf, buflen, pincb, pincb_arg); err = change_keyattr (app, ctrl, keyno, buf, buflen, pincb, pincb_arg);
xfree (relptr); xfree (relptr);
} }
@ -3346,7 +3377,7 @@ change_rsa_keyattr (app_t app, int keyno, unsigned int nbits,
ECC: "--force <key> <algo> <curvename>" ECC: "--force <key> <algo> <curvename>"
*/ */
static gpg_error_t static gpg_error_t
change_keyattr_from_string (app_t app, change_keyattr_from_string (app_t app, ctrl_t ctrl,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *value, size_t valuelen) const void *value, size_t valuelen)
@ -3390,7 +3421,7 @@ change_keyattr_from_string (app_t app,
else if (nbits > 4096) else if (nbits > 4096)
err = gpg_error (GPG_ERR_TOO_LARGE); err = gpg_error (GPG_ERR_TOO_LARGE);
else else
err = change_rsa_keyattr (app, keyno, nbits, pincb, pincb_arg); err = change_rsa_keyattr (app, ctrl, keyno, nbits, pincb, pincb_arg);
} }
else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA
|| algo == PUBKEY_ALGO_EDDSA) || algo == PUBKEY_ALGO_EDDSA)
@ -3417,7 +3448,7 @@ change_keyattr_from_string (app_t app,
/* We have enough room at STRING. */ /* We have enough room at STRING. */
string[0] = algo; string[0] = algo;
memcpy (string+1, oidbuf+1, oid_len-1); memcpy (string+1, oidbuf+1, oid_len-1);
err = change_keyattr (app, keyno, string, oid_len, pincb, pincb_arg); err = change_keyattr (app, ctrl,keyno, string, oid_len, pincb, pincb_arg);
gcry_mpi_release (oid); gcry_mpi_release (oid);
} }
else else
@ -3430,7 +3461,8 @@ change_keyattr_from_string (app_t app,
static gpg_error_t static gpg_error_t
rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), rsa_writekey (app_t app, ctrl_t ctrl,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, int keyno, void *pincb_arg, int keyno,
const unsigned char *buf, size_t buflen, int depth) const unsigned char *buf, size_t buflen, int depth)
{ {
@ -3556,7 +3588,7 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
&& app->app_local->extcap.algo_attr_change) && app->app_local->extcap.algo_attr_change)
{ {
/* Try to switch the key to a new length. */ /* Try to switch the key to a new length. */
err = change_rsa_keyattr (app, keyno, nbits, pincb, pincb_arg); err = change_rsa_keyattr (app, ctrl, keyno, nbits, pincb, pincb_arg);
if (!err) if (!err)
maxbits = app->app_local->keyattr[keyno].rsa.n_bits; maxbits = app->app_local->keyattr[keyno].rsa.n_bits;
} }
@ -3658,7 +3690,7 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
goto leave; goto leave;
/* Prepare for storing the key. */ /* Prepare for storing the key. */
err = verify_chv3 (app, pincb, pincb_arg); err = verify_chv3 (app, ctrl, pincb, pincb_arg);
if (err) if (err)
goto leave; goto leave;
@ -3714,7 +3746,7 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
assert (tp - template == template_len); assert (tp - template == template_len);
/* Prepare for storing the key. */ /* Prepare for storing the key. */
err = verify_chv3 (app, pincb, pincb_arg); err = verify_chv3 (app, ctrl, pincb, pincb_arg);
if (err) if (err)
goto leave; goto leave;
@ -3742,7 +3774,8 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
static gpg_error_t static gpg_error_t
ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), ecc_writekey (app_t app, ctrl_t ctrl,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, int keyno, void *pincb_arg, int keyno,
const unsigned char *buf, size_t buflen, int depth) const unsigned char *buf, size_t buflen, int depth)
{ {
@ -3939,7 +3972,8 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
} }
keyattr[0] = algo; keyattr[0] = algo;
memcpy (keyattr+1, oidbuf+1, oid_len-1); memcpy (keyattr+1, oidbuf+1, oid_len-1);
err = change_keyattr (app, keyno, keyattr, oid_len, pincb, pincb_arg); err = change_keyattr (app, ctrl, keyno,
keyattr, oid_len, pincb, pincb_arg);
xfree (keyattr); xfree (keyattr);
if (err) if (err)
goto leave; goto leave;
@ -3977,7 +4011,7 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
goto leave; goto leave;
/* Prepare for storing the key. */ /* Prepare for storing the key. */
err = verify_chv3 (app, pincb, pincb_arg); err = verify_chv3 (app, ctrl, pincb, pincb_arg);
if (err) if (err)
{ {
xfree (template); xfree (template);
@ -4012,6 +4046,7 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
return err; return err;
} }
/* Handle the WRITEKEY command for OpenPGP. This function expects a /* Handle the WRITEKEY command for OpenPGP. This function expects a
canonical encoded S-expression with the secret key in KEYDATA and canonical encoded S-expression with the secret key in KEYDATA and
its length (for assertions) in KEYDATALEN. KEYID needs to be the its length (for assertions) in KEYDATALEN. KEYID needs to be the
@ -4075,9 +4110,9 @@ do_writekey (app_t app, ctrl_t ctrl,
if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
goto leave; goto leave;
if (tok && toklen == 3 && memcmp ("rsa", tok, toklen) == 0) if (tok && toklen == 3 && memcmp ("rsa", tok, toklen) == 0)
err = rsa_writekey (app, pincb, pincb_arg, keyno, buf, buflen, depth); err = rsa_writekey (app, ctrl, pincb, pincb_arg, keyno, buf, buflen, depth);
else if (tok && toklen == 3 && memcmp ("ecc", tok, toklen) == 0) else if (tok && toklen == 3 && memcmp ("ecc", tok, toklen) == 0)
err = ecc_writekey (app, pincb, pincb_arg, keyno, buf, buflen, depth); err = ecc_writekey (app, ctrl, pincb, pincb_arg, keyno, buf, buflen, depth);
else else
{ {
err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO);
@ -4152,7 +4187,7 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, const char *keytype,
} }
/* Prepare for key generation by verifying the Admin PIN. */ /* Prepare for key generation by verifying the Admin PIN. */
err = verify_chv3 (app, pincb, pincb_arg); err = verify_chv3 (app, ctrl, pincb, pincb_arg);
if (err) if (err)
return err; return err;
@ -4371,7 +4406,7 @@ check_keyidstr (app_t app, const char *keyidstr, int keyno)
operation to the auth command. operation to the auth command.
*/ */
static gpg_error_t static gpg_error_t
do_sign (app_t app, const char *keyidstr, int hashalgo, do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
@ -4485,7 +4520,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
/* Redirect to the AUTH command if asked to. */ /* Redirect to the AUTH command if asked to. */
if (use_auth) if (use_auth)
{ {
return do_auth (app, "OPENPGP.3", pincb, pincb_arg, return do_auth (app, ctrl, "OPENPGP.3", pincb, pincb_arg,
data, datalen, data, datalen,
outdata, outdatalen); outdata, outdatalen);
} }
@ -4506,6 +4541,8 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
return rc; return rc;
app->did_chv1 = 1; app->did_chv1 = 1;
if (!app->force_chv1)
cache_pin (app, ctrl, 1, pinvalue);
/* For cards with versions < 2 we want to keep CHV1 and CHV2 in /* For cards with versions < 2 we want to keep CHV1 and CHV2 in
sync, thus we verify CHV2 here using the given PIN. Cards sync, thus we verify CHV2 here using the given PIN. Cards
@ -4525,6 +4562,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
return rc; return rc;
} }
app->did_chv2 = 1; app->did_chv2 = 1;
cache_pin (app, ctrl, 2, pinvalue);
} }
xfree (pinvalue); xfree (pinvalue);
} }
@ -4545,9 +4583,12 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
rc = iso7816_compute_ds (app_get_slot (app), exmode, data, datalen, le_value, rc = iso7816_compute_ds (app_get_slot (app), exmode, data, datalen, le_value,
outdata, outdatalen); outdata, outdatalen);
if (gpg_err_code (rc) == GPG_ERR_TIMEOUT) if (gpg_err_code (rc) == GPG_ERR_TIMEOUT)
clear_chv_status (app, 1); clear_chv_status (app, ctrl, 1);
else if (!rc && app->force_chv1) else if (!rc && app->force_chv1)
app->did_chv1 = 0; {
app->did_chv1 = 0;
cache_pin (app, ctrl, 1, NULL);
}
return rc; return rc;
} }
@ -4563,7 +4604,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
not match the one required for the requested action (e.g. the not match the one required for the requested action (e.g. the
serial number does not match). */ serial number does not match). */
static gpg_error_t static gpg_error_t
do_auth (app_t app, const char *keyidstr, do_auth (app_t app, ctrl_t ctrl, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
@ -4604,7 +4645,7 @@ do_auth (app_t app, const char *keyidstr,
return rc; return rc;
} }
rc = verify_chv2 (app, pincb, pincb_arg); rc = verify_chv2 (app, ctrl, pincb, pincb_arg);
if (!rc) if (!rc)
{ {
int exmode, le_value; int exmode, le_value;
@ -4625,14 +4666,14 @@ do_auth (app_t app, const char *keyidstr,
indata, indatalen, le_value, indata, indatalen, le_value,
outdata, outdatalen); outdata, outdatalen);
if (gpg_err_code (rc) == GPG_ERR_TIMEOUT) if (gpg_err_code (rc) == GPG_ERR_TIMEOUT)
clear_chv_status (app, 1); clear_chv_status (app, ctrl, 1);
} }
return rc; return rc;
} }
static gpg_error_t static gpg_error_t
do_decipher (app_t app, const char *keyidstr, do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
@ -4659,7 +4700,7 @@ do_decipher (app_t app, const char *keyidstr,
return rc; return rc;
} }
rc = verify_chv2 (app, pincb, pincb_arg); rc = verify_chv2 (app, ctrl, pincb, pincb_arg);
if (rc) if (rc)
return rc; return rc;
@ -4845,7 +4886,7 @@ do_decipher (app_t app, const char *keyidstr,
} }
} }
if (gpg_err_code (rc) == GPG_ERR_TIMEOUT) if (gpg_err_code (rc) == GPG_ERR_TIMEOUT)
clear_chv_status (app, 1); clear_chv_status (app, ctrl, 1);
if (gpg_err_code (rc) == GPG_ERR_CARD /* actual SW is 0x640a */ if (gpg_err_code (rc) == GPG_ERR_CARD /* actual SW is 0x640a */
&& app->app_local->manufacturer == 5 && app->app_local->manufacturer == 5
@ -4870,7 +4911,7 @@ do_decipher (app_t app, const char *keyidstr,
the "[CHV3]" being a literal string: The Admin Pin is checked if the "[CHV3]" being a literal string: The Admin Pin is checked if
and only if the retry counter is still at 3. */ and only if the retry counter is still at 3. */
static gpg_error_t static gpg_error_t
do_check_pin (app_t app, const char *keyidstr, do_check_pin (app_t app, ctrl_t ctrl, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg) void *pincb_arg)
{ {
@ -4923,12 +4964,13 @@ do_check_pin (app_t app, const char *keyidstr,
} }
app->did_chv3 = 0; /* Force verification. */ app->did_chv3 = 0; /* Force verification. */
return verify_chv3 (app, pincb, pincb_arg); return verify_chv3 (app, ctrl, pincb, pincb_arg);
} }
else else
return verify_chv2 (app, pincb, pincb_arg); return verify_chv2 (app, ctrl, pincb, pincb_arg);
} }
static void static void
send_keyinfo_if_available (app_t app, ctrl_t ctrl, char *serial, send_keyinfo_if_available (app_t app, ctrl_t ctrl, char *serial,
int data, int i) int data, int i)
@ -5225,6 +5267,23 @@ parse_algorithm_attribute (app_t app, int keyno)
} }
/* Prepare a reselect of another application. This is used by cards
* which support on-the-fly switching between applications. The
* function is called to give us a chance to save state for a future
* reselect of us again. */
static gpg_error_t
do_prep_reselect (app_t app, ctrl_t ctrl)
{
gpg_error_t err;
(void)app;
(void)ctrl;
err = 0;
return err;
}
/* Reselect the application. This is used by cards which support /* Reselect the application. This is used by cards which support
* on-the-fly switching between applications. */ * on-the-fly switching between applications. */
static gpg_error_t static gpg_error_t
@ -5236,7 +5295,7 @@ do_reselect (app_t app, ctrl_t ctrl)
/* An extra check which should not be necessary because the caller /* An extra check which should not be necessary because the caller
* should have made sure that a re-select is only called for * should have made sure that a re-select is only called for
* approriate cards. */ * appropriate cards. */
if (app->card->cardtype != CARDTYPE_YUBIKEY) if (app->card->cardtype != CARDTYPE_YUBIKEY)
return gpg_error (GPG_ERR_NOT_SUPPORTED); return gpg_error (GPG_ERR_NOT_SUPPORTED);
@ -5403,6 +5462,7 @@ app_select_openpgp (app_t app)
dump_all_do (slot); dump_all_do (slot);
app->fnc.deinit = do_deinit; app->fnc.deinit = do_deinit;
app->fnc.prep_reselect = do_prep_reselect;
app->fnc.reselect = do_reselect; app->fnc.reselect = do_reselect;
app->fnc.learn_status = do_learn_status; app->fnc.learn_status = do_learn_status;
app->fnc.readcert = do_readcert; app->fnc.readcert = do_readcert;

View File

@ -2853,7 +2853,7 @@ micardo_mse (app_t app, unsigned short fid)
that callback should return the PIN in an allocated buffer and that callback should return the PIN in an allocated buffer and
store that as the 3rd argument. */ store that as the 3rd argument. */
static gpg_error_t static gpg_error_t
do_sign (app_t app, const char *keyidstr, int hashalgo, do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
@ -2876,6 +2876,8 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
int no_data_padding = 0; /* True if the card want the data without padding.*/ int no_data_padding = 0; /* True if the card want the data without padding.*/
int mse_done = 0; /* Set to true if the MSE has been done. */ int mse_done = 0; /* Set to true if the MSE has been done. */
(void)ctrl;
if (!keyidstr || !*keyidstr) if (!keyidstr || !*keyidstr)
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
if (indatalen != 20 && indatalen != 16 && indatalen != 35 && indatalen != 36) if (indatalen != 20 && indatalen != 16 && indatalen != 35 && indatalen != 36)
@ -3208,7 +3210,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
must match the criteria used for the attribute $AUTHKEYID. See must match the criteria used for the attribute $AUTHKEYID. See
do_sign for calling conventions; there is no HASHALGO, though. */ do_sign for calling conventions; there is no HASHALGO, though. */
static gpg_error_t static gpg_error_t
do_auth (app_t app, const char *keyidstr, do_auth (app_t app, ctrl_t ctrl, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
@ -3231,7 +3233,7 @@ do_auth (app_t app, const char *keyidstr,
} }
algo = indatalen == 36? MD_USER_TLS_MD5SHA1 : GCRY_MD_SHA1; algo = indatalen == 36? MD_USER_TLS_MD5SHA1 : GCRY_MD_SHA1;
return do_sign (app, keyidstr, algo, pincb, pincb_arg, return do_sign (app, ctrl, keyidstr, algo, pincb, pincb_arg,
indata, indatalen, outdata, outdatalen); indata, indatalen, outdata, outdatalen);
} }
@ -3415,6 +3417,7 @@ app_select_p15 (app_t app)
} }
app->fnc.deinit = do_deinit; app->fnc.deinit = do_deinit;
app->fnc.prep_reselect = NULL;
app->fnc.reselect = NULL; app->fnc.reselect = NULL;
app->fnc.learn_status = do_learn_status; app->fnc.learn_status = do_learn_status;
app->fnc.readcert = do_readcert; app->fnc.readcert = do_readcert;

View File

@ -1068,7 +1068,7 @@ set_adm_key (app_t app, const unsigned char *value, size_t valuelen)
/* Handle the SETATTR operation. All arguments are already basically /* Handle the SETATTR operation. All arguments are already basically
* checked. */ * checked. */
static gpg_error_t static gpg_error_t
do_setattr (app_t app, const char *name, do_setattr (app_t app, ctrl_t ctrl, const char *name,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const unsigned char *value, size_t valuelen) const unsigned char *value, size_t valuelen)
@ -1089,6 +1089,7 @@ do_setattr (app_t app, const char *name,
}; };
int idx; int idx;
(void)ctrl;
(void)pincb; (void)pincb;
(void)pincb_arg; (void)pincb_arg;
@ -2075,12 +2076,14 @@ do_change_chv (app_t app, ctrl_t ctrl, const char *pwidstr,
/* Perform a simple verify operation for the PIN specified by PWIDSTR. /* Perform a simple verify operation for the PIN specified by PWIDSTR.
* For valid values see do_change_chv. */ * For valid values see do_change_chv. */
static gpg_error_t static gpg_error_t
do_check_chv (app_t app, const char *pwidstr, do_check_chv (app_t app, ctrl_t ctrl, const char *pwidstr,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg) void *pincb_arg)
{ {
int keyref; int keyref;
(void)ctrl;
keyref = parse_chv_keyref (pwidstr); keyref = parse_chv_keyref (pwidstr);
if (keyref == -1) if (keyref == -1)
return gpg_error (GPG_ERR_INV_ID); return gpg_error (GPG_ERR_INV_ID);
@ -2100,7 +2103,7 @@ do_check_chv (app_t app, const char *pwidstr,
* OID to the indata or checks that it is consistent. * OID to the indata or checks that it is consistent.
*/ */
static gpg_error_t static gpg_error_t
do_sign (app_t app, const char *keyidstr, int hashalgo, do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *indata_arg, size_t indatalen, const void *indata_arg, size_t indatalen,
@ -2121,6 +2124,8 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
size_t apdudatalen; size_t apdudatalen;
int force_verify; int force_verify;
(void)ctrl;
if (!keyidstr || !*keyidstr) if (!keyidstr || !*keyidstr)
{ {
err = gpg_error (GPG_ERR_INV_VALUE); err = gpg_error (GPG_ERR_INV_VALUE);
@ -2384,13 +2389,13 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
* whereas SIGN may accept a plain digest and does the padding if * whereas SIGN may accept a plain digest and does the padding if
* needed. This is also the reason why SIGN takes a hashalgo. */ * needed. This is also the reason why SIGN takes a hashalgo. */
static gpg_error_t static gpg_error_t
do_auth (app_t app, const char *keyidstr, do_auth (app_t app, ctrl_t ctrl, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
unsigned char **r_outdata, size_t *r_outdatalen) unsigned char **r_outdata, size_t *r_outdatalen)
{ {
return do_sign (app, keyidstr, 0, pincb, pincb_arg, indata, indatalen, return do_sign (app, ctrl, keyidstr, 0, pincb, pincb_arg, indata, indatalen,
r_outdata, r_outdatalen); r_outdata, r_outdatalen);
} }
@ -2398,7 +2403,7 @@ do_auth (app_t app, const char *keyidstr,
/* Decrypt the data in (INDATA,INDATALEN) and on success store the /* Decrypt the data in (INDATA,INDATALEN) and on success store the
* mallocated result at (R_OUTDATA,R_OUTDATALEN). */ * mallocated result at (R_OUTDATA,R_OUTDATALEN). */
static gpg_error_t static gpg_error_t
do_decipher (app_t app, const char *keyidstr, do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *indata_arg, size_t indatalen, const void *indata_arg, size_t indatalen,
@ -2418,6 +2423,8 @@ do_decipher (app_t app, const char *keyidstr,
unsigned char *apdudata = NULL; unsigned char *apdudata = NULL;
size_t apdudatalen; size_t apdudatalen;
(void)ctrl;
if (!keyidstr || !*keyidstr) if (!keyidstr || !*keyidstr)
{ {
err = gpg_error (GPG_ERR_INV_VALUE); err = gpg_error (GPG_ERR_INV_VALUE);
@ -3431,6 +3438,23 @@ do_with_keygrip (app_t app, ctrl_t ctrl, int action,
} }
/* Prepare a reselect of another application. This is used by cards
* which support on-the-fly switching between applications. The
* function is called to give us a chance to save state for a future
* reselect of us again. */
static gpg_error_t
do_prep_reselect (app_t app, ctrl_t ctrl)
{
gpg_error_t err;
(void)app;
(void)ctrl;
err = 0;
return err;
}
/* Reselect the application. This is used by cards which support /* Reselect the application. This is used by cards which support
* on-the-fly switching between applications. */ * on-the-fly switching between applications. */
static gpg_error_t static gpg_error_t
@ -3442,7 +3466,7 @@ do_reselect (app_t app, ctrl_t ctrl)
/* An extra check which should not be necessary because the caller /* An extra check which should not be necessary because the caller
* should have made sure that a re-select is only called for * should have made sure that a re-select is only called for
* approriate cards. */ * appropriate cards. */
if (!app->app_local->flags.yubikey) if (!app->app_local->flags.yubikey)
return gpg_error (GPG_ERR_NOT_SUPPORTED); return gpg_error (GPG_ERR_NOT_SUPPORTED);
@ -3535,6 +3559,7 @@ app_select_piv (app_t app)
dump_all_do (slot); dump_all_do (slot);
app->fnc.deinit = do_deinit; app->fnc.deinit = do_deinit;
app->fnc.prep_reselect = do_prep_reselect;
app->fnc.reselect = do_reselect; app->fnc.reselect = do_reselect;
app->fnc.learn_status = do_learn_status; app->fnc.learn_status = do_learn_status;
app->fnc.readcert = do_readcert; app->fnc.readcert = do_readcert;

View File

@ -1773,7 +1773,7 @@ verify_pin (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
the ECDSA signature in X9.62 format (SEQ/INT(r)/INT(s)) the ECDSA signature in X9.62 format (SEQ/INT(r)/INT(s))
*/ */
static gpg_error_t static gpg_error_t
do_sign (app_t app, const char *keyidstr, int hashalgo, do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
@ -1810,6 +1810,8 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
unsigned char algoid; unsigned char algoid;
int sw; int sw;
(void)ctrl;
if (!keyidstr || !*keyidstr) if (!keyidstr || !*keyidstr)
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
@ -1901,7 +1903,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
must match the criteria used for the attribute $AUTHKEYID. See must match the criteria used for the attribute $AUTHKEYID. See
do_sign for calling conventions; there is no HASHALGO, though. */ do_sign for calling conventions; there is no HASHALGO, though. */
static gpg_error_t static gpg_error_t
do_auth (app_t app, const char *keyidstr, do_auth (app_t app, ctrl_t ctrl, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
@ -1924,7 +1926,7 @@ do_auth (app_t app, const char *keyidstr,
} }
algo = indatalen == 36? MD_USER_TLS_MD5SHA1 : GCRY_MD_SHA1; algo = indatalen == 36? MD_USER_TLS_MD5SHA1 : GCRY_MD_SHA1;
return do_sign (app, keyidstr, algo, pincb, pincb_arg, return do_sign (app, ctrl, keyidstr, algo, pincb, pincb_arg,
indata, indatalen, outdata, outdatalen); indata, indatalen, outdata, outdatalen);
} }
@ -1973,7 +1975,7 @@ strip_PKCS15_padding(unsigned char *src, int srclen, unsigned char **dst,
/* Decrypt a PKCS#1 V1.5 formatted cryptogram using the referenced /* Decrypt a PKCS#1 V1.5 formatted cryptogram using the referenced
key. */ key. */
static gpg_error_t static gpg_error_t
do_decipher (app_t app, const char *keyidstr, do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
@ -1988,6 +1990,8 @@ do_decipher (app_t app, const char *keyidstr,
size_t p1blklen; size_t p1blklen;
int sw; int sw;
(void)ctrl;
if (!keyidstr || !*keyidstr || !indatalen) if (!keyidstr || !*keyidstr || !indatalen)
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
@ -2069,6 +2073,7 @@ app_select_sc_hsm (app_t app)
goto leave; goto leave;
app->fnc.deinit = do_deinit; app->fnc.deinit = do_deinit;
app->fnc.prep_reselect = NULL;
app->fnc.reselect = NULL; app->fnc.reselect = NULL;
app->fnc.learn_status = do_learn_status; app->fnc.learn_status = do_learn_status;
app->fnc.readcert = do_readcert; app->fnc.readcert = do_readcert;

View File

@ -663,7 +663,10 @@ select_application (ctrl_t ctrl, const char *name, card_t *r_card,
} }
if (err) if (err)
apdu_close_reader (slot); {
pincache_put (ctrl, slot, NULL, NULL, NULL);
apdu_close_reader (slot);
}
} }
apdu_dev_list_finish (l); apdu_dev_list_finish (l);
@ -1094,7 +1097,7 @@ app_get_serialno (app_t app)
/* Check that the card has been initialized and whether we need to /* Check that the card has been initialized and whether we need to
* switch to another application on the same card. Switching means * switch to another application on the same card. Switching means
* that the new active app will be moved to the head of the list at * that the new active app will be moved to the head of the list at
* CARD->app. Thus function must be called with the card lock held. */ * CARD->app. This function must be called with the card lock held. */
static gpg_error_t static gpg_error_t
maybe_switch_app (ctrl_t ctrl, card_t card, const char *keyref) maybe_switch_app (ctrl_t ctrl, card_t card, const char *keyref)
{ {
@ -1165,6 +1168,20 @@ maybe_switch_app (ctrl_t ctrl, card_t card, const char *keyref)
strapptype (app->apptype)); strapptype (app->apptype));
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
} }
/* Give the current app a chance to save some state before another
* app is selected. We ignore errors here because that state saving
* (e.g. putting PINs into a cache) is a convenience feature and not
* required to always work. */
if (app_prev && app_prev->fnc.prep_reselect)
{
err = app_prev->fnc.prep_reselect (app_prev, ctrl);
if (err)
log_info ("card %d: preparing re-select failed for '%s': %s\n",
card->slot, xstrapptype (app_prev), gpg_strerror (err));
err = 0;
}
err = app->fnc.reselect (app, ctrl); err = app->fnc.reselect (app, ctrl);
if (err) if (err)
{ {
@ -1220,8 +1237,8 @@ write_learn_status_core (card_t card, app_t app, ctrl_t ctrl,
gpg_error_t gpg_error_t
app_write_learn_status (card_t card, ctrl_t ctrl, unsigned int flags) app_write_learn_status (card_t card, ctrl_t ctrl, unsigned int flags)
{ {
gpg_error_t err, err2; gpg_error_t err, err2, tmperr;
app_t app; app_t app, last_app;
int any_reselect = 0; int any_reselect = 0;
if (!card) if (!card)
@ -1246,18 +1263,37 @@ app_write_learn_status (card_t card, ctrl_t ctrl, unsigned int flags)
* loop over all other apps which are capable of a reselect * loop over all other apps which are capable of a reselect
* and finally reselect the first app again. Note that we * and finally reselect the first app again. Note that we
* did the learn for the currently selected card above. */ * did the learn for the currently selected card above. */
app = card->app; app = last_app = card->app;
for (app = app->next; app && !err; app = app->next) for (app = app->next; app && !err; app = app->next)
if (app->fnc.reselect) if (app->fnc.reselect)
{ {
if (last_app && last_app->fnc.prep_reselect)
{
tmperr = last_app->fnc.prep_reselect (last_app, ctrl);
if (tmperr)
log_info ("card %d: preparing re-select failed for '%s'"
": %s\n", card->slot, xstrapptype (last_app),
gpg_strerror (tmperr));
}
any_reselect = 1; any_reselect = 1;
err = app->fnc.reselect (app, ctrl); err = app->fnc.reselect (app, ctrl);
if (!err) if (!err)
err = write_learn_status_core (NULL, app, ctrl, flags); {
last_app = app;
err = write_learn_status_core (NULL, app, ctrl, flags);
}
} }
app = card->app; app = card->app;
if (any_reselect) if (any_reselect)
{ {
if (last_app && last_app->fnc.prep_reselect)
{
tmperr = last_app->fnc.prep_reselect (last_app, ctrl);
if (tmperr)
log_info ("card %d: preparing re-select failed for '%s'"
": %s\n", card->slot, xstrapptype (last_app),
gpg_strerror (tmperr));
}
err2 = app->fnc.reselect (app, ctrl); err2 = app->fnc.reselect (app, ctrl);
if (err2) if (err2)
{ {
@ -1424,7 +1460,7 @@ app_setattr (card_t card, ctrl_t ctrl, const char *name,
if (DBG_APP) if (DBG_APP)
log_debug ("slot %d app %s: calling setattr(%s)\n", log_debug ("slot %d app %s: calling setattr(%s)\n",
card->slot, xstrapptype (card->app), name); card->slot, xstrapptype (card->app), name);
err = card->app->fnc.setattr (card->app, name, pincb, pincb_arg, err = card->app->fnc.setattr (card->app, ctrl, name, pincb, pincb_arg,
value, valuelen); value, valuelen);
} }
@ -1460,7 +1496,7 @@ app_sign (card_t card, ctrl_t ctrl, const char *keyidstr, int hashalgo,
if (DBG_APP) if (DBG_APP)
log_debug ("slot %d app %s: calling sign(%s)\n", log_debug ("slot %d app %s: calling sign(%s)\n",
card->slot, xstrapptype (card->app), keyidstr); card->slot, xstrapptype (card->app), keyidstr);
err = card->app->fnc.sign (card->app, keyidstr, hashalgo, err = card->app->fnc.sign (card->app, ctrl, keyidstr, hashalgo,
pincb, pincb_arg, pincb, pincb_arg,
indata, indatalen, indata, indatalen,
outdata, outdatalen); outdata, outdatalen);
@ -1501,7 +1537,7 @@ app_auth (card_t card, ctrl_t ctrl, const char *keyidstr,
if (DBG_APP) if (DBG_APP)
log_debug ("slot %d app %s: calling auth(%s)\n", log_debug ("slot %d app %s: calling auth(%s)\n",
card->slot, xstrapptype (card->app), keyidstr); card->slot, xstrapptype (card->app), keyidstr);
err = card->app->fnc.auth (card->app, keyidstr, err = card->app->fnc.auth (card->app, ctrl, keyidstr,
pincb, pincb_arg, pincb, pincb_arg,
indata, indatalen, indata, indatalen,
outdata, outdatalen); outdata, outdatalen);
@ -1544,7 +1580,7 @@ app_decipher (card_t card, ctrl_t ctrl, const char *keyidstr,
if (DBG_APP) if (DBG_APP)
log_debug ("slot %d app %s: calling decipher(%s)\n", log_debug ("slot %d app %s: calling decipher(%s)\n",
card->slot, xstrapptype (card->app), keyidstr); card->slot, xstrapptype (card->app), keyidstr);
err = card->app->fnc.decipher (card->app, keyidstr, err = card->app->fnc.decipher (card->app, ctrl, keyidstr,
pincb, pincb_arg, pincb, pincb_arg,
indata, indatalen, indata, indatalen,
outdata, outdatalen, outdata, outdatalen,
@ -1750,7 +1786,8 @@ app_check_pin (card_t card, ctrl_t ctrl, const char *keyidstr,
if (DBG_APP) if (DBG_APP)
log_debug ("slot %d app %s: calling check_pin(%s)\n", log_debug ("slot %d app %s: calling check_pin(%s)\n",
card->slot, xstrapptype (card->app), keyidstr); card->slot, xstrapptype (card->app), keyidstr);
err = card->app->fnc.check_pin (card->app, keyidstr, pincb, pincb_arg); err = card->app->fnc.check_pin (card->app, ctrl, keyidstr,
pincb, pincb_arg);
} }
unlock_card (card); unlock_card (card);
@ -1863,6 +1900,7 @@ scd_update_reader_status_file (void)
if (status == 0) if (status == 0)
{ {
log_debug ("Removal of a card: %d\n", card->slot); log_debug ("Removal of a card: %d\n", card->slot);
pincache_put (NULL, card->slot, NULL, NULL, NULL);
apdu_close_reader (card->slot); apdu_close_reader (card->slot);
deallocate_card (card); deallocate_card (card);
} }

View File

@ -42,7 +42,8 @@
#include "../common/asshelp.h" #include "../common/asshelp.h"
#include "../common/server-help.h" #include "../common/server-help.h"
/* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN */ /* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN. That
* length needs to small compared to the maximum Assuan line length. */
#define MAXLEN_PIN 100 #define MAXLEN_PIN 100
/* Maximum allowed size of key data as used in inquiries. */ /* Maximum allowed size of key data as used in inquiries. */
@ -281,7 +282,7 @@ static const char hlp_serialno[] =
"selected and an error is returned if no such card available.\n" "selected and an error is returned if no such card available.\n"
"\n" "\n"
"If --all is given, all possible other applications of the card are\n" "If --all is given, all possible other applications of the card are\n"
"will also be selected for on-the-fly swicthing.\n" "also selected to prepare for \"LEARN --force --multi\".\n"
"\n" "\n"
"If APPTYPE is given, an application of that type is selected and an\n" "If APPTYPE is given, an application of that type is selected and an\n"
"error is returned if the application is not supported or available.\n" "error is returned if the application is not supported or available.\n"
@ -2241,6 +2242,191 @@ send_status_printf (ctrl_t ctrl, const char *keyword, const char *format, ...)
} }
/* Store the PIN in the PIN cache. The key to identify the PIN
* consists of (SLOT,APPNAME,PINREF). If PIN is NULL the PIN stored
* under the given key is cleared. If APPNAME and PINREF are NULL the
* entire PIN cache for SLOT is cleared. If SLOT is -1 the entire PIN
* cache is cleared. We do no use an scdaemon internal cache but let
* gpg-agent cache because it is better suited for this. */
void
pincache_put (ctrl_t ctrl, int slot, const char *appname, const char *pinref,
const char *pin)
{
gpg_error_t err;
assuan_context_t ctx;
char line[950];
gcry_cipher_hd_t cipherhd = NULL;
char *pinbuf = NULL;
unsigned char *wrappedkey = NULL;
size_t pinlen, pinbuflen, wrappedkeylen;
if (!ctrl)
{
/* No CTRL object provided. We could pick an arbitrary
* connection and send the status to that one. However, such a
* connection is inlikley to wait for a respinse from use and
* thus it would at best be read as a response to the next
* command send to us. That is not good because it may clog up
* our connection. Thus we better don't do that. A better will
* be to queue this up and let the agent poll for general status
* messages. */
/* struct server_local_s *sl; */
/* for (sl=session_list; sl; sl = sl->next_session) */
/* if (sl->ctrl_backlink && sl->ctrl_backlink->server_local */
/* && sl->ctrl_backlink->server_local->assuan_ctx) */
/* { */
/* ctrl = sl->ctrl_backlink; */
/* break; */
/* } */
}
if (!ctrl || !ctrl->server_local || !(ctx=ctrl->server_local->assuan_ctx))
return;
if (pin && !*pin)
return; /* Ignore an empty PIN. */
snprintf (line, sizeof line, "%d/%s/%s ",
slot, appname? appname:"-", pinref? pinref:"-");
/* Without an APPNAME etc or without a PIN we clear the cache and
* thus there is no need to send the pin - even if the caller
* accidentially passed a pin. */
if (pin && slot != -1 && appname && pinref)
{
pinlen = strlen (pin);
if ((pinlen % 8))
{
/* Pad with zeroes (AESWRAP requires multiples of 64 bit but
* at least 128 bit data). */
pinbuflen = pinlen + 8 - (pinlen % 8);
if (pinbuflen < 16)
pinbuflen = 16;
pinbuf = xtrycalloc_secure (1, pinbuflen);
if (!pinbuf)
{
err = gpg_error_from_syserror ();
goto leave;
}
memcpy (pinbuf, pin, pinlen);
pinlen = pinbuflen;
pin = pinbuf;
}
err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128,
GCRY_CIPHER_MODE_AESWRAP, 0);
if (!err)
err = gcry_cipher_setkey (cipherhd, "1234567890123456", 16);
if (err)
goto leave;
wrappedkeylen = pinlen + 8;
wrappedkey = xtrymalloc (wrappedkeylen);
if (!wrappedkey)
{
err = gpg_error_from_syserror ();
goto leave;
}
err = gcry_cipher_encrypt (cipherhd, wrappedkey, wrappedkeylen,
pin, pinlen);
if (err)
goto leave;
gcry_cipher_close (cipherhd);
cipherhd = NULL;
if (strlen (line) + 2*wrappedkeylen + 1 >= sizeof line)
{
log_error ("%s: PIN or pinref string too long - ignored", __func__);
goto leave;
}
bin2hex (wrappedkey, wrappedkeylen, line + strlen (line));
}
send_status_direct (ctrl, "PINCACHE_PUT", line);
leave:
xfree (pinbuf);
xfree (wrappedkey);
gcry_cipher_close (cipherhd);
if (err)
log_error ("%s: error caching PIN: %s\n", __func__, gpg_strerror (err));
}
/* Ask the agent for a cached PIN for the tuple (SLOT,APPNAME,PINREF).
* Returns on success and stores the PIN at R_PIN; the caller needs to
* wipe(!) and then free that value. On error NULL is stored at
* R_PIN and an error code returned. Common error codes are:
* GPG_ERR_NOT_SUPPORTED - Client does not support the PIN cache
* GPG_ERR_NO_DATA - No PIN cached for the given key tuple
*/
gpg_error_t
pincache_get (ctrl_t ctrl, int slot, const char *appname, const char *pinref,
char **r_pin)
{
gpg_error_t err;
assuan_context_t ctx;
char command[512];
unsigned char *value = NULL;
size_t valuelen;
unsigned char *wrappedkey = NULL;
size_t wrappedkeylen;
gcry_cipher_hd_t cipherhd = NULL;
if (slot == -1 || !appname || !pinref || !r_pin)
{
err = gpg_error (GPG_ERR_INV_ARG);
goto leave;
}
if (!ctrl || !ctrl->server_local || !(ctx = ctrl->server_local->assuan_ctx))
{
err = set_error (GPG_ERR_USE_CONDITIONS, "called w/o assuan context");
goto leave;
}
snprintf (command, sizeof command, "PINCACHE_GET %d/%s/%s",
slot, appname? appname:"-", pinref? pinref:"-");
err = assuan_inquire (ctx, command, &wrappedkey, &wrappedkeylen,
MAXLEN_PIN+24);
if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
err = set_error (GPG_ERR_NOT_SUPPORTED,
"client does not feature a PIN cache");
else if (!err && (!wrappedkey || wrappedkeylen < 24))
err = set_error (GPG_ERR_INV_LENGTH, "received too short cryptogram");
else if (!err)
{
valuelen = wrappedkeylen - 8;
value = xtrymalloc_secure (valuelen);
if (!value)
{
err = gpg_error_from_syserror ();
goto leave;
}
err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128,
GCRY_CIPHER_MODE_AESWRAP, 0);
if (!err)
err = gcry_cipher_setkey (cipherhd, "1234567890123456", 16);
if (err)
goto leave;
err = gcry_cipher_decrypt (cipherhd, value, valuelen,
wrappedkey, wrappedkeylen);
if (err)
goto leave;
*r_pin = value;
value = NULL;
}
leave:
gcry_cipher_close (cipherhd);
xfree (wrappedkey);
xfree (value);
return err;
}
void void
popup_prompt (void *opaque, int on) popup_prompt (void *opaque, int on)
{ {

View File

@ -137,6 +137,11 @@ gpg_error_t send_status_printf (ctrl_t ctrl, const char *keyword,
void send_keyinfo (ctrl_t ctrl, int data, const char *keygrip_str, void send_keyinfo (ctrl_t ctrl, int data, const char *keygrip_str,
const char *serialno, const char *idstr); const char *serialno, const char *idstr);
void pincache_put (ctrl_t ctrl, int slot, const char *appname,
const char *pinref, const char *pin);
gpg_error_t pincache_get (ctrl_t ctrl, int slot, const char *appname,
const char *pinref, char **r_pin);
void popup_prompt (void *opaque, int on); void popup_prompt (void *opaque, int on);
/* Take care: this function assumes that CARD is locked. */ /* Take care: this function assumes that CARD is locked. */