diff options
Diffstat (limited to '')
-rw-r--r-- | scd/app-openpgp.c | 333 |
1 files changed, 229 insertions, 104 deletions
diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index c42167fc9..b5906f887 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -1,6 +1,7 @@ /* app-openpgp.c - The OpenPGP card application. - * Copyright (C) 2003, 2004, 2005, 2007, 2008, - * 2009, 2013, 2014, 2015 Free Software Foundation, Inc. + * Copyright (C) 2003-2005, 2007-2009, + * 2013-2015 Free Software Foundation, Inc. + * Copyright (C) 2003-2005, 2007-2009, 2013-2015, 2020 g10 Code GmbH * * This file is part of GnuPG. * @@ -291,6 +292,35 @@ do_deinit (app_t app) } +/* This is a helper to do a wipememory followed by a free. In general + * we do not need this if the buffer has been allocated in secure + * memory. However at some places we can't make that sure and thus we + * better to an extra wipe here. */ +static void +wipe_and_free (void *p, size_t len) +{ + if (p) + { + if (len) + wipememory (p, len); + xfree (p); + } +} + + +/* Similar to wipe_and_free but assumes P is eitehr NULL or a proper + * string. */ +static void +wipe_and_free_string (char *p) +{ + if (p) + { + wipememory (p, strlen (p)); + xfree (p); + } +} + + /* Wrapper around iso7816_get_data which first tries to get the data from the cache. With GET_IMMEDIATE passed as true, the cache is bypassed. With TRY_EXTLEN extended lengths APDUs are use if @@ -2074,25 +2104,39 @@ get_prompt_info (app_t app, int chvno, unsigned long sigcount, int remaining) #define KDF_DATA_LENGTH_MAX 110 /* Compute hash if KDF-DO is available. CHVNO must be 0 for reset - code, 1 or 2 for user pin and 3 for admin pin. - */ + * code, 1 or 2 for user pin and 3 for admin pin. PIN is the original + * PIN as entered by the user. R_PINVALUE and r_PINLEN will receive a + * newly allocated buffer with a possible modified pin. */ static gpg_error_t -pin2hash_if_kdf (app_t app, int chvno, char *pinvalue, int *r_pinlen) +pin2hash_if_kdf (app_t app, int chvno, const char *pin, + char **r_pinvalue, size_t *r_pinlen) { gpg_error_t err = 0; void *relptr = NULL; unsigned char *buffer; - size_t buflen; + size_t pinlen, buflen; + char *dek = NULL; + size_t deklen = 32; + *r_pinvalue = NULL; + *r_pinlen = 0; + + pinlen = strlen (pin); if (app->app_local->extcap.kdf_do && (relptr = get_one_do (app, 0x00F9, &buffer, &buflen, NULL)) && buflen >= KDF_DATA_LENGTH_MIN && (buffer[2] == 0x03)) { const char *salt; unsigned long s2k_count; - char dek[32]; int salt_index; + dek = xtrymalloc (deklen); + if (!dek) + { + err = gpg_error_from_syserror (); + goto leave; + } + s2k_count = (((unsigned int)buffer[8] << 24) | (buffer[9] << 16) | (buffer[10] << 8) | buffer[11]); @@ -2107,33 +2151,41 @@ pin2hash_if_kdf (app_t app, int chvno, char *pinvalue, int *r_pinlen) } salt = &buffer[salt_index]; - err = gcry_kdf_derive (pinvalue, strlen (pinvalue), + err = gcry_kdf_derive (pin, pinlen, GCRY_KDF_ITERSALTED_S2K, DIGEST_ALGO_SHA256, salt, 8, - s2k_count, sizeof (dek), dek); + s2k_count, deklen, dek); if (!err) { - /* pinvalue has a buffer of MAXLEN_PIN+1, 32 is OK. */ - *r_pinlen = 32; - memcpy (pinvalue, dek, *r_pinlen); - wipememory (dek, *r_pinlen); + *r_pinlen = deklen; + *r_pinvalue = dek; + dek = NULL; } - } + } else - *r_pinlen = strlen (pinvalue); + { + /* Just copy the PIN to a malloced buffer. */ + *r_pinvalue = xtrymalloc_secure (pinlen + 1); + if (!*r_pinvalue) + { + err = gpg_error_from_syserror (); + goto leave; + } + strcpy (*r_pinvalue, pin); + *r_pinlen = pinlen; + } leave: + xfree (dek); xfree (relptr); return err; } -/* 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) +static const char * +chvno_to_keyref (int chvno) { const char *keyref; - switch (chvno) { case 1: keyref = "1"; break; @@ -2141,12 +2193,54 @@ cache_pin (app_t app, ctrl_t ctrl, int chvno, const char *pin) case 3: keyref = "3"; break; default: keyref = NULL; break; } + return keyref; +} + + +/* 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 = chvno_to_keyref (chvno); - if (app->card->cardtype == CARDTYPE_YUBIKEY && keyref) - pincache_put (ctrl, app_get_slot (app), "openpgp", keyref, pin); + if (!keyref) + return; + switch (app->card->cardtype) + { + case CARDTYPE_YUBIKEY: break; + default: return; + } + + pincache_put (ctrl, app_get_slot (app), "openpgp", keyref, pin); } +/* If the PIN cache is expected and really has a valid PIN return that + * pin at R_PIN. Returns true if that is the case; otherwise + * false. */ +static int +pin_from_cache (app_t app, ctrl_t ctrl, int chvno, char **r_pin) +{ + const char *keyref = chvno_to_keyref (chvno); + + *r_pin = NULL; + + if (!keyref) + return 0; + switch (app->card->cardtype) + { + case CARDTYPE_YUBIKEY: break; + default: return 0; + } + + if (pincache_get (ctrl, app_get_slot (app), "openpgp", keyref, r_pin)) + return 0; + + return 1; +} + + + /* 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 @@ -2158,10 +2252,10 @@ cache_pin (app_t app, ctrl_t ctrl, int chvno, const char *pin) as an indication that the pinpad has been used. */ static gpg_error_t -verify_a_chv (app_t app, +verify_a_chv (app_t app, ctrl_t ctrl, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, int chvno, unsigned long sigcount, - char **pinvalue, int *pinlen) + char **r_pinvalue, size_t *r_pinlen) { int rc = 0; char *prompt_buffer = NULL; @@ -2169,11 +2263,12 @@ verify_a_chv (app_t app, pininfo_t pininfo; int minlen = 6; int remaining; + char *pin = NULL; log_assert (chvno == 1 || chvno == 2); - *pinvalue = NULL; - *pinlen = 0; + *r_pinvalue = NULL; + *r_pinlen = 0; remaining = get_remaining_tries (app, 0); if (remaining == -1) @@ -2221,8 +2316,10 @@ verify_a_chv (app_t app, && !check_pinpad_request (app, &pininfo, 0)) { /* The reader supports the verify command through the pinpad. - Note that the pincb appends a text to the prompt telling the - user to use the pinpad. */ + * In this case we do not utilize the PIN cache because by using + * a pinpad the PIN can't have been cached. + * Note that the pincb appends a text to the prompt telling the + * user to use the pinpad. */ rc = pincb (pincb_arg, prompt, NULL); prompt = NULL; xfree (prompt_buffer); @@ -2236,13 +2333,18 @@ verify_a_chv (app_t app, rc = iso7816_verify_kp (app_get_slot (app), 0x80+chvno, &pininfo); /* Dismiss the prompt. */ pincb (pincb_arg, NULL, NULL); - - log_assert (!*pinvalue); } else { - /* The reader has no pinpad or we don't want to use it. */ - rc = pincb (pincb_arg, prompt, pinvalue); + /* The reader has no pinpad or we don't want to use it. If we + * have at least the standard 3 remaining tries we first try to + * get the PIN from the cache. With less remaining tries it is + * better to let the user know about failed attempts (which + * might be due to a bug in the PIN cache handling). */ + if (remaining >= 3 && pin_from_cache (app, ctrl, chvno, &pin)) + rc = 0; + else + rc = pincb (pincb_arg, prompt, &pin); prompt = NULL; xfree (prompt_buffer); prompt_buffer = NULL; @@ -2253,26 +2355,29 @@ verify_a_chv (app_t app, return rc; } - if (strlen (*pinvalue) < minlen) + if (strlen (pin) < minlen) { log_error (_("PIN for CHV%d is too short;" " minimum length is %d\n"), chvno, minlen); - xfree (*pinvalue); - *pinvalue = NULL; + wipe_and_free_string (pin); return gpg_error (GPG_ERR_BAD_PIN); } - rc = pin2hash_if_kdf (app, chvno, *pinvalue, pinlen); + rc = pin2hash_if_kdf (app, chvno, pin, r_pinvalue, r_pinlen); if (!rc) rc = iso7816_verify (app_get_slot (app), - 0x80 + chvno, *pinvalue, *pinlen); + 0x80 + chvno, *r_pinvalue, *r_pinlen); + if (!rc) + cache_pin (app, ctrl, chvno, pin); } + wipe_and_free_string (pin); if (rc) { log_error (_("verify CHV%d failed: %s\n"), chvno, gpg_strerror (rc)); - xfree (*pinvalue); - *pinvalue = NULL; + xfree (*r_pinvalue); + *r_pinvalue = NULL; + *r_pinlen = 0; flush_cache_after_error (app); } @@ -2289,16 +2394,15 @@ verify_chv2 (app_t app, ctrl_t ctrl, { int rc; char *pinvalue; - int pinlen; + size_t pinlen; if (app->did_chv2) return 0; /* We already verified CHV2. */ - rc = verify_a_chv (app, pincb, pincb_arg, 2, 0, &pinvalue, &pinlen); + rc = verify_a_chv (app, ctrl, pincb, pincb_arg, 2, 0, &pinvalue, &pinlen); if (rc) return rc; app->did_chv2 = 1; - cache_pin (app, ctrl, 2, pinvalue); if (!app->did_chv1 && !app->force_chv1 && pinvalue) { @@ -2317,26 +2421,31 @@ verify_chv2 (app_t app, ctrl_t ctrl, else { app->did_chv1 = 1; - cache_pin (app, ctrl, 1, pinvalue); + /* Note that we are not able to cache the CHV 1 here because + * it is possible that due to the use of a KDF-DO PINVALUE + * has the hashed binary PIN of length PINLEN. */ } } - xfree (pinvalue); + wipe_and_free (pinvalue, pinlen); return rc; } /* Build the prompt to enter the Admin PIN. The prompt depends on the - current sdtate of the card. */ + * current state of the card. If R_REMAINING is not NULL the + * remaining tries are stored there. */ static gpg_error_t -build_enter_admin_pin_prompt (app_t app, char **r_prompt) +build_enter_admin_pin_prompt (app_t app, char **r_prompt, int *r_remaining) { int remaining; char *prompt; char *infoblock; *r_prompt = NULL; + if (r_remaining) + *r_remaining = 0; remaining = get_remaining_tries (app, 1); if (remaining == -1) @@ -2364,6 +2473,8 @@ build_enter_admin_pin_prompt (app_t app, char **r_prompt) return gpg_error_from_syserror (); *r_prompt = prompt; + if (r_remaining) + *r_remaining = remaining; return 0; } @@ -2387,12 +2498,13 @@ verify_chv3 (app_t app, ctrl_t ctrl, pininfo_t pininfo; int minlen = 8; char *prompt; + int remaining; memset (&pininfo, 0, sizeof pininfo); pininfo.fixedlen = -1; pininfo.minlen = minlen; - rc = build_enter_admin_pin_prompt (app, &prompt); + rc = build_enter_admin_pin_prompt (app, &prompt, &remaining); if (rc) return rc; @@ -2417,10 +2529,14 @@ verify_chv3 (app_t app, ctrl_t ctrl, } else { + char *pin; char *pinvalue; - int pinlen; + size_t pinlen; - rc = pincb (pincb_arg, prompt, &pinvalue); + if (remaining >= 3 && pin_from_cache (app, ctrl, 3, &pin)) + rc = 0; + else + rc = pincb (pincb_arg, prompt, &pin); xfree (prompt); prompt = NULL; if (rc) @@ -2430,20 +2546,21 @@ verify_chv3 (app_t app, ctrl_t ctrl, return rc; } - if (strlen (pinvalue) < minlen) + if (strlen (pin) < minlen) { log_error (_("PIN for CHV%d is too short;" " minimum length is %d\n"), 3, minlen); - xfree (pinvalue); + wipe_and_free_string (pin); return gpg_error (GPG_ERR_BAD_PIN); } - rc = pin2hash_if_kdf (app, 3, pinvalue, &pinlen); + rc = pin2hash_if_kdf (app, 3, pin, &pinvalue, &pinlen); if (!rc) rc = iso7816_verify (app_get_slot (app), 0x83, pinvalue, pinlen); if (!rc) - cache_pin (app, ctrl, 3, pinvalue); - xfree (pinvalue); + cache_pin (app, ctrl, 3, pin); + wipe_and_free_string (pin); + wipe_and_free (pinvalue, pinlen); } if (rc) @@ -2660,8 +2777,6 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, pininfo_t pininfo; int use_pinpad = 0; int minlen = 6; - int pinlen0 = 0; - int pinlen = 0; if (digitp (chvnostr)) chvno = atoi (chvnostr); @@ -2678,6 +2793,11 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, pininfo.fixedlen = -1; pininfo.minlen = minlen; + /* Better clear all the PIN caches first. */ + cache_pin (app, ctrl, 1, NULL); + cache_pin (app, ctrl, 2, NULL); + cache_pin (app, ctrl, 3, NULL); + if ((flags & APP_CHANGE_FLAG_CLEAR)) return clear_chv_status (app, ctrl, chvno); @@ -2694,7 +2814,6 @@ 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, ctrl, pincb, pincb_arg); if (rc) @@ -2706,8 +2825,6 @@ 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; @@ -2754,7 +2871,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, if (chvno == 3) { minlen = 8; - rc = build_enter_admin_pin_prompt (app, &promptbuf); + rc = build_enter_admin_pin_prompt (app, &promptbuf, NULL); if (rc) goto leave; prompt = promptbuf; @@ -2855,29 +2972,38 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, if (resetcode) { - char *buffer; + char *result1 = NULL; + char *result2 = NULL; + char *buffer = NULL; + size_t resultlen1, resultlen2, bufferlen=0; - buffer = xtrymalloc (strlen (resetcode) + strlen (pinvalue) + 1); - if (!buffer) - rc = gpg_error_from_syserror (); - else + rc = pin2hash_if_kdf (app, 0, resetcode, &result1, &resultlen1); + if (!rc) + rc = pin2hash_if_kdf (app, 0, pinvalue, &result2, &resultlen2); + if (!rc) { - strcpy (buffer, resetcode); - rc = pin2hash_if_kdf (app, 0, buffer, &pinlen0); - if (!rc) + bufferlen = resultlen1 + resultlen2; + buffer = xtrymalloc (bufferlen); + if (!buffer) + rc = gpg_error_from_syserror (); + else { - strcpy (buffer+pinlen0, pinvalue); - rc = pin2hash_if_kdf (app, 0, buffer+pinlen0, &pinlen); + memcpy (buffer, result1, resultlen1); + memcpy (buffer+resultlen1, result2, resultlen2); } - if (!rc) - rc = iso7816_reset_retry_counter_with_rc (app_get_slot (app), 0x81, - buffer, pinlen0+pinlen); - wipememory (buffer, pinlen0 + pinlen); - xfree (buffer); } + if (!rc) + rc = iso7816_reset_retry_counter_with_rc (app_get_slot (app), 0x81, + buffer, bufferlen); + wipe_and_free (result1, resultlen1); + wipe_and_free (result2, resultlen2); + wipe_and_free (buffer, bufferlen); } else if (set_resetcode) { + char *buffer = NULL; + size_t bufferlen; + if (strlen (pinvalue) < 8) { log_error (_("Reset Code is too short; minimum length is %d\n"), 8); @@ -2885,21 +3011,28 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, } else { - rc = pin2hash_if_kdf (app, 0, pinvalue, &pinlen); + rc = pin2hash_if_kdf (app, 0, pinvalue, &buffer, &bufferlen); if (!rc) rc = iso7816_put_data (app_get_slot (app), - 0, 0xD3, pinvalue, pinlen); + 0, 0xD3, buffer, bufferlen); } + + wipe_and_free (buffer, bufferlen); } else if (reset_mode) { - rc = pin2hash_if_kdf (app, 1, pinvalue, &pinlen); + char *buffer = NULL; + size_t bufferlen; + + rc = pin2hash_if_kdf (app, 1, pinvalue, &buffer, &bufferlen); if (!rc) rc = iso7816_reset_retry_counter (app_get_slot (app), - 0x81, pinvalue, pinlen); + 0x81, buffer, bufferlen); if (!rc && !app->app_local->extcap.is_v2) rc = iso7816_reset_retry_counter (app_get_slot (app), - 0x82, pinvalue, pinlen); + 0x82, buffer, bufferlen); + + wipe_and_free (buffer, bufferlen); } else if (!app->app_local->extcap.is_v2) { @@ -2945,36 +3078,30 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, } else { - rc = pin2hash_if_kdf (app, chvno, oldpinvalue, &pinlen0); + char *buffer1 = NULL; + char *buffer2 = NULL; + size_t bufferlen1, bufferlen2; + + rc = pin2hash_if_kdf (app, chvno, oldpinvalue, &buffer1, &bufferlen1); if (!rc) - rc = pin2hash_if_kdf (app, chvno, pinvalue, &pinlen); + rc = pin2hash_if_kdf (app, chvno, pinvalue, &buffer2, &bufferlen2); if (!rc) rc = iso7816_change_reference_data (app_get_slot (app), 0x80 + chvno, - oldpinvalue, pinlen0, - pinvalue, pinlen); + buffer1, bufferlen1, + buffer2, bufferlen2); + wipe_and_free (buffer1, bufferlen1); + wipe_and_free (buffer2, bufferlen2); } } - if (pinvalue) - { - wipememory (pinvalue, pinlen); - xfree (pinvalue); - } + wipe_and_free_string (pinvalue); if (rc) flush_cache_after_error (app); leave: - if (resetcode) - { - wipememory (resetcode, strlen (resetcode)); - xfree (resetcode); - } - if (oldpinvalue) - { - wipememory (oldpinvalue, pinlen0); - xfree (oldpinvalue); - } + wipe_and_free_string (resetcode); + wipe_and_free_string (oldpinvalue); return rc; } @@ -4533,16 +4660,14 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, if (!app->did_chv1 || app->force_chv1) { char *pinvalue; - int pinlen; + size_t pinlen; - rc = verify_a_chv (app, pincb, pincb_arg, 1, sigcount, + rc = verify_a_chv (app, ctrl, pincb, pincb_arg, 1, sigcount, &pinvalue, &pinlen); if (rc) 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 @@ -4557,14 +4682,14 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, if (rc) { log_error (_("verify CHV%d failed: %s\n"), 2, gpg_strerror (rc)); - xfree (pinvalue); + wipe_and_free (pinvalue, pinlen); flush_cache_after_error (app); return rc; } app->did_chv2 = 1; cache_pin (app, ctrl, 2, pinvalue); } - xfree (pinvalue); + wipe_and_free (pinvalue, pinlen); } |