From fbf97a7856bd2f80a1714f63417c59d6c604d333 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 7 Jan 2020 18:45:33 +0100 Subject: [PATCH] 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 --- scd/app-common.h | 11 +-- scd/app-dinsig.c | 5 +- scd/app-geldkarte.c | 1 + scd/app-nks.c | 12 ++- scd/app-openpgp.c | 158 ++++++++++++++++++++++++------------ scd/app-p15.c | 9 ++- scd/app-piv.c | 39 +++++++-- scd/app-sc-hsm.c | 13 ++- scd/app.c | 60 +++++++++++--- scd/command.c | 190 +++++++++++++++++++++++++++++++++++++++++++- scd/scdaemon.h | 5 ++ 11 files changed, 418 insertions(+), 85 deletions(-) diff --git a/scd/app-common.h b/scd/app-common.h index 99331b04e..72ad8e7cd 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -136,6 +136,7 @@ struct app_ctx_s { struct app_local_s *app_local; /* Local to the application. */ struct { 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 (*learn_status) (app_t app, ctrl_t ctrl, unsigned int flags); gpg_error_t (*readcert) (app_t app, const char *certid, @@ -144,22 +145,22 @@ struct app_ctx_s { const char *certid, unsigned int flags, unsigned char **pk, size_t *pklen); 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 **), void *pincb_arg, 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, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, 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 **), void *pincb_arg, const void *indata, size_t indatalen, 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 **), void *pincb_arg, const void *indata, size_t indatalen, @@ -184,7 +185,7 @@ struct app_ctx_s { const char *chvnostr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), 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 **), void *pincb_arg); gpg_error_t (*with_keygrip) (app_t app, ctrl_t ctrl, int action, diff --git a/scd/app-dinsig.c b/scd/app-dinsig.c index 9993b68ff..6f9d5e2f2 100644 --- a/scd/app-dinsig.c +++ b/scd/app-dinsig.c @@ -389,7 +389,7 @@ verify_pin (app_t app, that callback should return the PIN in an allocated buffer and store that in the 3rd argument. */ 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 **), void *pincb_arg, 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. */ int datalen; + (void)ctrl; + if (!keyidstr || !*keyidstr) return gpg_error (GPG_ERR_INV_VALUE); if (indatalen != 20 && indatalen != 16 && indatalen != 32 @@ -557,6 +559,7 @@ app_select_dinsig (app_t app) { app->apptype = APPTYPE_DINSIG; + app->fnc.prep_reselect = NULL; app->fnc.reselect = NULL; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; diff --git a/scd/app-geldkarte.c b/scd/app-geldkarte.c index 141985932..f76febe05 100644 --- a/scd/app-geldkarte.c +++ b/scd/app-geldkarte.c @@ -313,6 +313,7 @@ app_select_geldkarte (app_t app) app->apptype = APPTYPE_GELDKARTE; app->fnc.deinit = do_deinit; + app->fnc.prep_reselect = NULL; app->fnc.reselect = NULL; /* If we don't have a serialno yet construct it from the EF_ID. */ diff --git a/scd/app-nks.c b/scd/app-nks.c index bb5329bfe..bdf065145 100644 --- a/scd/app-nks.c +++ b/scd/app-nks.c @@ -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 store that in the 3rd argument. */ 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 **), void *pincb_arg, const void *indata, size_t indatalen, @@ -907,6 +907,8 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, + the largest OID prefix. */ size_t datalen; + (void)ctrl; + if (!keyidstr || !*keyidstr) return gpg_error (GPG_ERR_INV_VALUE); 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 should return the PIN in an allocated buffer and put it into PIN. */ 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 **), void *pincb_arg, const void *indata, size_t indatalen, @@ -1034,6 +1036,7 @@ do_decipher (app_t app, const char *keyidstr, int fid; int kid; + (void)ctrl; (void)r_info; 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. */ 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 **), void *pincb_arg) { @@ -1319,6 +1322,8 @@ do_check_pin (app_t app, const char *pwidstr, int is_sigg; const char *desc; + (void)ctrl; + desc = parse_pwidstr (pwidstr, 0, &is_sigg, &pwid); if (!desc) 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); app->fnc.deinit = do_deinit; + app->fnc.prep_reselect = NULL; app->fnc.reselect = NULL; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index cdd16fab2..c42167fc9 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -249,14 +249,14 @@ struct app_local_s { static unsigned long convert_sig_counter_value (const unsigned char *value, size_t valuelen); 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 **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen); static void parse_algorithm_attribute (app_t app, int keyno); 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 **), void *pincb_arg, 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 using a pinpad. PINCB and PINCB_ARG describe the usual callback 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 card CHV1 will also be verified. */ 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 **), void *pincb_arg) { @@ -2279,6 +2298,7 @@ verify_chv2 (app_t app, if (rc) return rc; app->did_chv2 = 1; + cache_pin (app, ctrl, 2, pinvalue); if (!app->did_chv1 && !app->force_chv1 && pinvalue) { @@ -2295,7 +2315,10 @@ verify_chv2 (app_t app, flush_cache_after_error (app); } else - app->did_chv1 = 1; + { + app->did_chv1 = 1; + cache_pin (app, ctrl, 1, pinvalue); + } } xfree (pinvalue); @@ -2347,7 +2370,7 @@ build_enter_admin_pin_prompt (app_t app, char **r_prompt) /* Verify CHV3 if required. */ 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 **), void *pincb_arg) { @@ -2418,6 +2441,8 @@ verify_chv3 (app_t app, rc = pin2hash_if_kdf (app, 3, pinvalue, &pinlen); if (!rc) rc = iso7816_verify (app_get_slot (app), 0x83, pinvalue, pinlen); + if (!rc) + cache_pin (app, ctrl, 3, pinvalue); xfree (pinvalue); } @@ -2429,6 +2454,7 @@ verify_chv3 (app_t app, } app->did_chv3 = 1; } + return rc; } @@ -2436,7 +2462,7 @@ verify_chv3 (app_t app, /* Handle the SETATTR operation. All arguments are already basically checked. */ 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 **), void *pincb_arg, 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); 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) { case 2: - rc = verify_chv2 (app, pincb, pincb_arg); + rc = verify_chv2 (app, ctrl, pincb, pincb_arg); break; case 3: - rc = verify_chv3 (app, pincb, pincb_arg); + rc = verify_chv3 (app, ctrl, pincb, pincb_arg); break; default: rc = 0; @@ -2547,7 +2574,6 @@ do_writecert (app_t app, ctrl_t ctrl, void *pincb_arg, const unsigned char *certdata, size_t certdatalen) { - (void)ctrl; if (strcmp (certidstr, "OPENPGP.3")) return gpg_error (GPG_ERR_INV_ID); if (!certdata || !certdatalen) @@ -2556,16 +2582,19 @@ do_writecert (app_t app, ctrl_t ctrl, return gpg_error (GPG_ERR_NOT_SUPPORTED); if (certdatalen > app->app_local->extcap.max_certlen_3) 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 -clear_chv_status (app_t app, int chvno) +clear_chv_status (app_t app, ctrl_t ctrl, int chvno) { unsigned char apdu[4]; gpg_error_t err; + cache_pin (app, ctrl, chvno, NULL); + if (!app->app_local->extcap.is_v2) 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 pinlen = 0; - (void)ctrl; - if (digitp (chvnostr)) chvno = atoi (chvnostr); 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; if ((flags & APP_CHANGE_FLAG_CLEAR)) - return clear_chv_status (app, chvno); + return clear_chv_status (app, ctrl, chvno); 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) { /* We always require that the PIN is entered. */ + cache_pin (app, ctrl, 3, NULL); app->did_chv3 = 0; - rc = verify_chv3 (app, pincb, pincb_arg); + rc = verify_chv3 (app, ctrl, pincb, pincb_arg); if (rc) goto leave; } @@ -2678,10 +2706,12 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, value, thus we enforce it here. */ int save_force = app->force_chv1; + cache_pin (app, ctrl, 1, NULL); + cache_pin (app, ctrl, 2, NULL); app->force_chv1 = 0; app->did_chv1 = 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; if (rc) 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. */ use_pinpad = 0; app->did_chv3 = 0; - rc = verify_chv3 (app, pincb, pincb_arg); + rc = verify_chv3 (app, ctrl, pincb, pincb_arg); if (rc) 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 this deletes the entire key without asking. */ 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 **), 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); /* Prepare for storing the key. */ - err = verify_chv3 (app, pincb, pincb_arg); + err = verify_chv3 (app, ctrl, pincb, pincb_arg); if (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 -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 **), void *pincb_arg) { @@ -3332,7 +3363,7 @@ change_rsa_keyattr (app_t app, int keyno, unsigned int nbits, 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); } @@ -3346,7 +3377,7 @@ change_rsa_keyattr (app_t app, int keyno, unsigned int nbits, ECC: "--force " */ 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 **), void *pincb_arg, const void *value, size_t valuelen) @@ -3390,7 +3421,7 @@ change_keyattr_from_string (app_t app, else if (nbits > 4096) err = gpg_error (GPG_ERR_TOO_LARGE); 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 || algo == PUBKEY_ALGO_EDDSA) @@ -3417,7 +3448,7 @@ change_keyattr_from_string (app_t app, /* We have enough room at STRING. */ string[0] = algo; 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); } else @@ -3430,7 +3461,8 @@ change_keyattr_from_string (app_t app, 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, 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) { /* 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) 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; /* Prepare for storing the key. */ - err = verify_chv3 (app, pincb, pincb_arg); + err = verify_chv3 (app, ctrl, pincb, pincb_arg); if (err) goto leave; @@ -3714,7 +3746,7 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), assert (tp - template == template_len); /* Prepare for storing the key. */ - err = verify_chv3 (app, pincb, pincb_arg); + err = verify_chv3 (app, ctrl, pincb, pincb_arg); if (err) goto leave; @@ -3742,7 +3774,8 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), 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, 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; 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); if (err) goto leave; @@ -3977,7 +4011,7 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), goto leave; /* Prepare for storing the key. */ - err = verify_chv3 (app, pincb, pincb_arg); + err = verify_chv3 (app, ctrl, pincb, pincb_arg); if (err) { xfree (template); @@ -4012,6 +4046,7 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), return err; } + /* Handle the WRITEKEY command for OpenPGP. This function expects a canonical encoded S-expression with the secret key in KEYDATA and 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))) goto leave; 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) - err = ecc_writekey (app, pincb, pincb_arg, keyno, buf, buflen, depth); + err = ecc_writekey (app, ctrl, pincb, pincb_arg, keyno, buf, buflen, depth); else { 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. */ - err = verify_chv3 (app, pincb, pincb_arg); + err = verify_chv3 (app, ctrl, pincb, pincb_arg); if (err) return err; @@ -4371,7 +4406,7 @@ check_keyidstr (app_t app, const char *keyidstr, int keyno) operation to the auth command. */ 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 **), void *pincb_arg, 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. */ if (use_auth) { - return do_auth (app, "OPENPGP.3", pincb, pincb_arg, + return do_auth (app, ctrl, "OPENPGP.3", pincb, pincb_arg, data, datalen, outdata, outdatalen); } @@ -4506,6 +4541,8 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, return rc; 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 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; } app->did_chv2 = 1; + cache_pin (app, ctrl, 2, 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, outdata, outdatalen); 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) - app->did_chv1 = 0; + { + app->did_chv1 = 0; + cache_pin (app, ctrl, 1, NULL); + } 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 serial number does not match). */ 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 **), void *pincb_arg, const void *indata, size_t indatalen, @@ -4604,7 +4645,7 @@ do_auth (app_t app, const char *keyidstr, return rc; } - rc = verify_chv2 (app, pincb, pincb_arg); + rc = verify_chv2 (app, ctrl, pincb, pincb_arg); if (!rc) { int exmode, le_value; @@ -4625,14 +4666,14 @@ do_auth (app_t app, const char *keyidstr, indata, indatalen, le_value, outdata, outdatalen); if (gpg_err_code (rc) == GPG_ERR_TIMEOUT) - clear_chv_status (app, 1); + clear_chv_status (app, ctrl, 1); } return rc; } 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 **), void *pincb_arg, const void *indata, size_t indatalen, @@ -4659,7 +4700,7 @@ do_decipher (app_t app, const char *keyidstr, return rc; } - rc = verify_chv2 (app, pincb, pincb_arg); + rc = verify_chv2 (app, ctrl, pincb, pincb_arg); if (rc) return rc; @@ -4845,7 +4886,7 @@ do_decipher (app_t app, const char *keyidstr, } } 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 */ && 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 and only if the retry counter is still at 3. */ 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 **), void *pincb_arg) { @@ -4923,12 +4964,13 @@ do_check_pin (app_t app, const char *keyidstr, } app->did_chv3 = 0; /* Force verification. */ - return verify_chv3 (app, pincb, pincb_arg); + return verify_chv3 (app, ctrl, pincb, pincb_arg); } else - return verify_chv2 (app, pincb, pincb_arg); + return verify_chv2 (app, ctrl, pincb, pincb_arg); } + static void send_keyinfo_if_available (app_t app, ctrl_t ctrl, char *serial, 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 * on-the-fly switching between applications. */ 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 * should have made sure that a re-select is only called for - * approriate cards. */ + * appropriate cards. */ if (app->card->cardtype != CARDTYPE_YUBIKEY) return gpg_error (GPG_ERR_NOT_SUPPORTED); @@ -5403,6 +5462,7 @@ app_select_openpgp (app_t app) dump_all_do (slot); app->fnc.deinit = do_deinit; + app->fnc.prep_reselect = do_prep_reselect; app->fnc.reselect = do_reselect; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; diff --git a/scd/app-p15.c b/scd/app-p15.c index 348242f4f..86902e90b 100644 --- a/scd/app-p15.c +++ b/scd/app-p15.c @@ -2853,7 +2853,7 @@ micardo_mse (app_t app, unsigned short fid) that callback should return the PIN in an allocated buffer and store that as the 3rd argument. */ 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 **), void *pincb_arg, 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 mse_done = 0; /* Set to true if the MSE has been done. */ + (void)ctrl; + if (!keyidstr || !*keyidstr) return gpg_error (GPG_ERR_INV_VALUE); 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 do_sign for calling conventions; there is no HASHALGO, though. */ 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 **), void *pincb_arg, 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; - return do_sign (app, keyidstr, algo, pincb, pincb_arg, + return do_sign (app, ctrl, keyidstr, algo, pincb, pincb_arg, indata, indatalen, outdata, outdatalen); } @@ -3415,6 +3417,7 @@ app_select_p15 (app_t app) } app->fnc.deinit = do_deinit; + app->fnc.prep_reselect = NULL; app->fnc.reselect = NULL; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; diff --git a/scd/app-piv.c b/scd/app-piv.c index 0b1cb8208..be61d562d 100644 --- a/scd/app-piv.c +++ b/scd/app-piv.c @@ -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 * checked. */ 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 **), void *pincb_arg, const unsigned char *value, size_t valuelen) @@ -1089,6 +1089,7 @@ do_setattr (app_t app, const char *name, }; int idx; + (void)ctrl; (void)pincb; (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. * For valid values see do_change_chv. */ 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 **), void *pincb_arg) { int keyref; + (void)ctrl; + keyref = parse_chv_keyref (pwidstr); if (keyref == -1) 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. */ 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 **), void *pincb_arg, const void *indata_arg, size_t indatalen, @@ -2121,6 +2124,8 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, size_t apdudatalen; int force_verify; + (void)ctrl; + if (!keyidstr || !*keyidstr) { 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 * needed. This is also the reason why SIGN takes a hashalgo. */ 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 **), void *pincb_arg, const void *indata, size_t indatalen, 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); } @@ -2398,7 +2403,7 @@ do_auth (app_t app, const char *keyidstr, /* Decrypt the data in (INDATA,INDATALEN) and on success store the * mallocated result at (R_OUTDATA,R_OUTDATALEN). */ 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 **), void *pincb_arg, const void *indata_arg, size_t indatalen, @@ -2418,6 +2423,8 @@ do_decipher (app_t app, const char *keyidstr, unsigned char *apdudata = NULL; size_t apdudatalen; + (void)ctrl; + if (!keyidstr || !*keyidstr) { 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 * on-the-fly switching between applications. */ 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 * should have made sure that a re-select is only called for - * approriate cards. */ + * appropriate cards. */ if (!app->app_local->flags.yubikey) return gpg_error (GPG_ERR_NOT_SUPPORTED); @@ -3535,6 +3559,7 @@ app_select_piv (app_t app) dump_all_do (slot); app->fnc.deinit = do_deinit; + app->fnc.prep_reselect = do_prep_reselect; app->fnc.reselect = do_reselect; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; diff --git a/scd/app-sc-hsm.c b/scd/app-sc-hsm.c index 2f1ab2074..3f376f2e5 100644 --- a/scd/app-sc-hsm.c +++ b/scd/app-sc-hsm.c @@ -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)) */ 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 **), void *pincb_arg, const void *indata, size_t indatalen, @@ -1810,6 +1810,8 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, unsigned char algoid; int sw; + (void)ctrl; + if (!keyidstr || !*keyidstr) 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 do_sign for calling conventions; there is no HASHALGO, though. */ 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 **), void *pincb_arg, 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; - return do_sign (app, keyidstr, algo, pincb, pincb_arg, + return do_sign (app, ctrl, keyidstr, algo, pincb, pincb_arg, 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 key. */ 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 **), void *pincb_arg, const void *indata, size_t indatalen, @@ -1988,6 +1990,8 @@ do_decipher (app_t app, const char *keyidstr, size_t p1blklen; int sw; + (void)ctrl; + if (!keyidstr || !*keyidstr || !indatalen) return gpg_error (GPG_ERR_INV_VALUE); @@ -2069,6 +2073,7 @@ app_select_sc_hsm (app_t app) goto leave; app->fnc.deinit = do_deinit; + app->fnc.prep_reselect = NULL; app->fnc.reselect = NULL; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; diff --git a/scd/app.c b/scd/app.c index 633070e9c..3b71f54dc 100644 --- a/scd/app.c +++ b/scd/app.c @@ -663,7 +663,10 @@ select_application (ctrl_t ctrl, const char *name, card_t *r_card, } if (err) - apdu_close_reader (slot); + { + pincache_put (ctrl, slot, NULL, NULL, NULL); + apdu_close_reader (slot); + } } 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 * 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 - * 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 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)); 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); if (err) { @@ -1220,8 +1237,8 @@ write_learn_status_core (card_t card, app_t app, ctrl_t ctrl, gpg_error_t app_write_learn_status (card_t card, ctrl_t ctrl, unsigned int flags) { - gpg_error_t err, err2; - app_t app; + gpg_error_t err, err2, tmperr; + app_t app, last_app; int any_reselect = 0; 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 * and finally reselect the first app again. Note that we * 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) 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; err = app->fnc.reselect (app, ctrl); 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; 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); if (err2) { @@ -1424,7 +1460,7 @@ app_setattr (card_t card, ctrl_t ctrl, const char *name, if (DBG_APP) log_debug ("slot %d app %s: calling setattr(%s)\n", 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); } @@ -1460,7 +1496,7 @@ app_sign (card_t card, ctrl_t ctrl, const char *keyidstr, int hashalgo, if (DBG_APP) log_debug ("slot %d app %s: calling sign(%s)\n", 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, indata, indatalen, outdata, outdatalen); @@ -1501,7 +1537,7 @@ app_auth (card_t card, ctrl_t ctrl, const char *keyidstr, if (DBG_APP) log_debug ("slot %d app %s: calling auth(%s)\n", 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, indata, indatalen, outdata, outdatalen); @@ -1544,7 +1580,7 @@ app_decipher (card_t card, ctrl_t ctrl, const char *keyidstr, if (DBG_APP) log_debug ("slot %d app %s: calling decipher(%s)\n", 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, indata, indatalen, outdata, outdatalen, @@ -1750,7 +1786,8 @@ app_check_pin (card_t card, ctrl_t ctrl, const char *keyidstr, if (DBG_APP) log_debug ("slot %d app %s: calling check_pin(%s)\n", 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); @@ -1863,6 +1900,7 @@ scd_update_reader_status_file (void) if (status == 0) { log_debug ("Removal of a card: %d\n", card->slot); + pincache_put (NULL, card->slot, NULL, NULL, NULL); apdu_close_reader (card->slot); deallocate_card (card); } diff --git a/scd/command.c b/scd/command.c index 3d82469af..36d46f084 100644 --- a/scd/command.c +++ b/scd/command.c @@ -42,7 +42,8 @@ #include "../common/asshelp.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 /* 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" "\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" "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" @@ -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 popup_prompt (void *opaque, int on) { diff --git a/scd/scdaemon.h b/scd/scdaemon.h index 3f2e3ed55..4a3ede37e 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -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, 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); /* Take care: this function assumes that CARD is locked. */