From efe7eb598fed5a92e097bc355cdec8239d86f151 Mon Sep 17 00:00:00 2001 From: Doug Engert Date: Thu, 8 Feb 2018 20:08:43 -0600 Subject: [PATCH] Some CAC / PIV cards do not support Discovery Object Some CAC card return '6A80` Incorrect parameters in APDU when trying to read the Discovery object. If it fails other then not found, then we can not use the Discovery object to test for the active AID. The test is done in piv_match_card just after doing a SELECT AID for the PIV. and set CI_DISCOVERY_USELESS if needed. piv_card_reader_lock_obtained will then not use the Discovery object. Some older PIV cards, prior to the introduction of the PIV Discovery and History objects, may get errors trying to read them. Ignore these errors too. Remove comment and remove code to check verify Lc=0 as requested in: https://github.com/OpenSC/OpenSC/pull/1256#pullrequestreview-96124443 They can easily be added back in. On branch piv-aid-discovery Changes to be committed: modified: src/libopensc/card-piv.c --- src/libopensc/card-piv.c | 59 +++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/src/libopensc/card-piv.c b/src/libopensc/card-piv.c index 7cdb93ff..63d80cca 100644 --- a/src/libopensc/card-piv.c +++ b/src/libopensc/card-piv.c @@ -213,6 +213,7 @@ static struct piv_aid piv_aids[] = { /* will also test after first PIN verify if protected object can be used instead */ #define CI_CANT_USE_GETDATA_FOR_STATE 0x00000008U /* No object to test verification inplace of VERIFY Lc=0 */ #define CI_LEAKS_FILE_NOT_FOUND 0x00000010U /* GET DATA of empty object returns 6A 82 even if PIN not verified */ +#define CI_DISCOVERY_USELESS 0x00000020U /* Discovery can not be used to query active AID */ #define CI_OTHER_AID_LOSE_STATE 0x00000100U /* Other drivers match routines may reset our security state and lose AID!!! */ #define CI_NFC_EXPOSE_TOO_MUCH 0x00000200U /* PIN, crypto and objects exposed over NFS in violation of 800-73-3 */ @@ -2662,7 +2663,6 @@ err: } -/* Do not use the cache value but read every time */ static int piv_find_discovery(sc_card_t *card) { int r = 0; @@ -2957,7 +2957,7 @@ piv_finish(sc_card_t *card) static int piv_match_card(sc_card_t *card) { - int i, k; + int i, i7e, k; size_t j; u8 *p, *pe; sc_file_t aidfile; @@ -3058,7 +3058,7 @@ static int piv_match_card(sc_card_t *card) * We may get interference on some cards by other drivers trying SELECT_AID before * we get to see if PIV application is still active. * putting PIV driver first might help. - * TODO could be cached too + * This may fail if the wrong AID is active */ i = piv_find_discovery(card); @@ -3067,6 +3067,22 @@ static int piv_match_card(sc_card_t *card) i = piv_find_aid(card, &aidfile); } + if (i >= 0) { + /* + * We now know PIV AID is active, test DISCOVERY object + * Some CAC cards with PIV don't support DISCOVERY and return + * SC_ERROR_INCORRECT_PARAMETERS. Any error other then + * SC_ERROR_FILE_NOT_FOUND means we cannot use discovery + * to test for active AID. + */ + i7e = piv_find_discovery(card); + if (i7e != 0 && i7e != SC_ERROR_FILE_NOT_FOUND) { + priv->card_issues |= CI_DISCOVERY_USELESS; + priv->obj_cache[PIV_OBJ_DISCOVERY].flags |= PIV_OBJ_CACHE_NOT_PRESENT; + } + } + + if (i < 0) { piv_finish(card); /* don't match. Does not have a PIV applet. */ @@ -3083,7 +3099,7 @@ static int piv_match_card(sc_card_t *card) static int piv_init(sc_card_t *card) { - int r; + int r = 0; piv_private_data_t * priv = PIV_DATA(card); sc_apdu_t apdu; unsigned long flags; @@ -3136,7 +3152,7 @@ static int piv_init(sc_card_t *card) switch(card->type) { case SC_CARD_TYPE_PIV_II_NEO: - priv->card_issues = CI_NO_EC384 + priv->card_issues |= CI_NO_EC384 | CI_VERIFY_630X | CI_OTHER_AID_LOSE_STATE | CI_LEAKS_FILE_NOT_FOUND @@ -3146,18 +3162,18 @@ static int piv_init(sc_card_t *card) break; case SC_CARD_TYPE_PIV_II_YUBIKEY4: - priv->card_issues = CI_OTHER_AID_LOSE_STATE + priv->card_issues |= CI_OTHER_AID_LOSE_STATE | CI_LEAKS_FILE_NOT_FOUND; if (priv->neo_version < 0x00040302) priv->card_issues |= CI_VERIFY_LC0_FAIL; break; case SC_CARD_TYPE_PIV_II_HIST: - priv->card_issues = 0; + priv->card_issues |= 0; break; case SC_CARD_TYPE_PIV_II_GENERIC: - priv->card_issues = CI_VERIFY_LC0_FAIL + priv->card_issues |= CI_VERIFY_LC0_FAIL | CI_OTHER_AID_LOSE_STATE; /* TODO may need more research */ break; @@ -3196,12 +3212,13 @@ static int piv_init(sc_card_t *card) * 800-73-3 cards may have a history object and/or a discovery object * We want to process them now as this has information on what * keys and certs the card has and how the pin might be used. + * If they fail, ignore it there are optional and introdced in + * NIST 800-73-3 and NIST 800-73-2 so some older cards may + * not handle the requets. */ piv_process_history(card); - r = piv_process_discovery(card); - if (r > 0) - r = 0; + piv_process_discovery(card); priv->pstate=PIV_STATE_NORMAL; sc_unlock(card) ; /* obtained in piv_match */ @@ -3477,7 +3494,6 @@ static int piv_card_reader_lock_obtained(sc_card_t *card, int was_reset) int r = 0; u8 temp[256]; size_t templen = sizeof(temp); - struct sc_pin_cmd_data data; piv_private_data_t * priv = PIV_DATA(card); /* may be null */ SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); @@ -3494,7 +3510,14 @@ static int piv_card_reader_lock_obtained(sc_card_t *card, int was_reset) /* first see if AID is active AID by reading discovery object '7E' */ /* If not try selecting AID */ - r = piv_find_discovery(card); + + /* but if x card does not support DISCOVERY object we can not use it */ + if (priv->card_issues & CI_DISCOVERY_USELESS) { + r = SC_ERROR_NO_CARD_SUPPORT; + } else { + r = piv_find_discovery(card); + } + if (r < 0) r = piv_select_aid(card, piv_aids[0].value, piv_aids[0].len_short, temp, &templen); @@ -3504,15 +3527,7 @@ static int piv_card_reader_lock_obtained(sc_card_t *card, int was_reset) if (was_reset > 0) priv->logged_in = SC_PIN_STATE_UNKNOWN; - /* See if VERIFY Lc=empty will tell us the state */ - memset(&data, 0, sizeof(data)); - data.cmd = SC_PIN_CMD_GET_INFO; - data.pin_type = SC_AC_CHV; - data.pin_reference = priv->pin_preference; - /* will try our best to see if logged_in or not */ - r = piv_pin_cmd(card, &data, NULL); - - r = 0; /* ignore return from piv_pin_cmd */ + r = 0; err: LOG_FUNC_RETURN(card->ctx, r);