From 27add2ee3c10269fba9465616a49231cfb68c65f Mon Sep 17 00:00:00 2001 From: Doug Engert Date: Thu, 1 Feb 2018 09:04:10 -0600 Subject: [PATCH] Inform pkcs15 and card drivers of PKCS#11 C_Login(CKU_CONTEXT_SPECIFIC)" Framework-pkcs15.c will now set pin_info->auth_method to SC_AC_CONTEXT_SPECIFIC iso7816.c iso7816_build_pin_apdu treats this the same as SC_AC_CHV card-piv.c piv_pin_cmd sets priv->xcontext_specific=1 and calls sc_lock before the verify command. If the verify fails sc_unlock is called. Later after the next card command returns, if priv->context_specific==1 piv_check_sw will call sc_unlock as the application may not have requested the crypto but some other command. Some additional calls to sc_lock and sc_unlock have been added to make sure PIV internal command sequences including the crypto command ('87') and any get responses are always protected by a lock. This guarantees the card is locked for verify and the next command which should be the crypto operation. The PIV card also inforces this restriction on the card. This is based on suggestions in: ://github.com/OpenSC/OpenSC/pull/1256#issuecomment-361975751 On branch piv-aid-discovery Changes to be committed: modified: src/libopensc/card-piv.c modified: src/libopensc/iso7816.c modified: src/libopensc/types.h modified: src/pkcs11/framework-pkcs15.c --- src/libopensc/card-piv.c | 53 ++++++++++++++++++++++++++++------- src/libopensc/iso7816.c | 1 + src/libopensc/types.h | 1 + src/pkcs11/framework-pkcs15.c | 11 +++++++- 4 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/libopensc/card-piv.c b/src/libopensc/card-piv.c index d6381847..7cdb93ff 100644 --- a/src/libopensc/card-piv.c +++ b/src/libopensc/card-piv.c @@ -168,6 +168,7 @@ typedef struct piv_private_data { int logged_in; int pstate; int pin_cmd_verify; + int context_specific; int pin_cmd_noparse; unsigned int pin_cmd_verify_sw1; unsigned int pin_cmd_verify_sw2; @@ -919,6 +920,8 @@ piv_get_data(sc_card_t * card, int enumtag, u8 **buf, size_t *buf_len) SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_log(card->ctx, "#%d", enumtag); + sc_lock(card); /* do check len and get data in same transaction */ + /* assert(enumtag >= 0 && enumtag < PIV_OBJ_LAST_ENUM); */ tag_len = piv_objects[enumtag].tag_len; @@ -970,6 +973,7 @@ piv_get_data(sc_card_t * card, int enumtag, u8 **buf, size_t *buf_len) r = piv_general_io(card, 0xCB, 0x3F, 0xFF, tagbuf, p - tagbuf, buf, buf_len); err: + sc_unlock(card); LOG_FUNC_RETURN(card->ctx, r); } @@ -3032,7 +3036,7 @@ static int piv_match_card(sc_card_t *card) if (card->type == -1) card->type = type; - card->drv_data = priv; /* will frre if no match, or pass on to piv_init */ + card->drv_data = priv; /* will free if no match, or pass on to piv_init */ priv->aid_file = sc_file_new(); priv->selected_obj = -1; priv->pin_preference = 0x80; /* 800-73-3 part 1, table 3 */ @@ -3056,18 +3060,18 @@ static int piv_match_card(sc_card_t *card) * putting PIV driver first might help. * TODO could be cached too */ - i = piv_find_discovery(card); + i = piv_find_discovery(card); if (i < 0) { - /* Detect by selecting applet */ - i = piv_find_aid(card, &aidfile); + /* Detect by selecting applet */ + i = piv_find_aid(card, &aidfile); } if (i < 0) { piv_finish(card); - /* don't match. Does not have a PIV applet. */ - sc_unlock(card); - return 0; + /* don't match. Does not have a PIV applet. */ + sc_unlock(card); + return 0; } /* Matched, and priv is being passed to piv_init */ @@ -3218,7 +3222,16 @@ static int piv_check_sw(struct sc_card *card, unsigned int sw1, unsigned int sw2 if (priv->pin_cmd_verify) { priv->pin_cmd_verify_sw1 = sw1; priv->pin_cmd_verify_sw2 = sw2; + } else { + /* a command has completed and it is not verify */ + /* If we are in a context_specific sequence, unlock */ + if (priv->context_specific) { + sc_log(card->ctx,"Clearing CONTEXT_SPECIFIC lock"); + priv->context_specific = 0; + sc_unlock(card); + } } + if (priv->card_issues & CI_VERIFY_630X) { /* Handle the Yubikey NEO or any other PIV card which returns in response to a verify @@ -3360,10 +3373,30 @@ piv_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) } } + /* + * If this was for a CKU_CONTEXT_SPECFIC login, lock the card one more time. + * to avoid any interference from other applications. + * Sc_unlock will be called at a later time after the next card command + * that should be a crypto operation. If its not then it is a error by the + * calling appication. + */ + if (data->cmd == SC_PIN_CMD_VERIFY && data->pin_type == SC_AC_CONTEXT_SPECIFIC) { + priv->context_specific = 1; + sc_log(card->ctx,"Starting CONTEXT_SPECIFIC verify"); + sc_lock(card); + } + priv->pin_cmd_verify = 1; /* tell piv_check_sw its a verify to save sw1, sw2 */ r = iso_drv->ops->pin_cmd(card, data, tries_left); priv->pin_cmd_verify = 0; + /* if verify failed, release the lock */ + if (data->cmd == SC_PIN_CMD_VERIFY && r < 0 && priv->context_specific) { + sc_log(card->ctx,"Clearing CONTEXT_SPECIFIC"); + priv->context_specific = 0; + sc_unlock(card); + } + /* if access to applet is know to be reset by other driver we select_aid and try again */ if ( priv->card_issues & CI_OTHER_AID_LOSE_STATE && priv->pin_cmd_verify_sw1 == 0x6DU) { sc_log(card->ctx, "AID may be lost doing piv_find_aid and retry pin_cmd"); @@ -3452,17 +3485,17 @@ static int piv_card_reader_lock_obtained(sc_card_t *card, int was_reset) /* We have a PCSC transaction and sc_lock */ if (priv == NULL || priv->pstate == PIV_STATE_MATCH) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, - priv ? "PIV_STATE_MATCH" : "priv==NULL"); + priv ? "PIV_STATE_MATCH" : "priv==NULL"); r = 0; /* do nothing, piv_match will take care of it */ goto err; } /* make sure our application is active */ - /* first see if AID is active AID be reading discovery obkect '7E' */ + /* first see if AID is active AID by reading discovery object '7E' */ /* If not try selecting AID */ r = piv_find_discovery(card); - if (r < 0) + if (r < 0) r = piv_select_aid(card, piv_aids[0].value, piv_aids[0].len_short, temp, &templen); if (r < 0) /* bad error return will show up in sc_lock as error*/ diff --git a/src/libopensc/iso7816.c b/src/libopensc/iso7816.c index e5916e7d..9ed49f2b 100644 --- a/src/libopensc/iso7816.c +++ b/src/libopensc/iso7816.c @@ -1017,6 +1017,7 @@ iso7816_build_pin_apdu(struct sc_card *card, struct sc_apdu *apdu, case SC_AC_CHV: /* fall through */ case SC_AC_SESSION: + case SC_AC_CONTEXT_SPECIFIC: break; default: return SC_ERROR_INVALID_ARGUMENTS; diff --git a/src/libopensc/types.h b/src/libopensc/types.h index e665c54b..ab0ddd8e 100644 --- a/src/libopensc/types.h +++ b/src/libopensc/types.h @@ -149,6 +149,7 @@ struct sc_crt { #define SC_AC_SCB 0x00000040 /* IAS/ECC SCB byte. */ #define SC_AC_IDA 0x00000080 /* PKCS#15 authentication ID */ #define SC_AC_SESSION 0x00000100 /* Session PIN */ +#define SC_AC_CONTEXT_SPECIFIC 0x00000200 /* Context specific login */ #define SC_AC_UNKNOWN 0xFFFFFFFE #define SC_AC_NEVER 0xFFFFFFFF diff --git a/src/pkcs11/framework-pkcs15.c b/src/pkcs11/framework-pkcs15.c index a4a1e1c3..aee08a4a 100644 --- a/src/pkcs11/framework-pkcs15.c +++ b/src/pkcs11/framework-pkcs15.c @@ -1617,7 +1617,16 @@ pkcs15_login(struct sc_pkcs11_slot *slot, CK_USER_TYPE userType, } } - rc = sc_pkcs15_verify_pin(p15card, auth_object, pPin, ulPinLen); + if (userType == CKU_CONTEXT_SPECIFIC && pin_info) { + int auth_meth_saved = pin_info->auth_method; + + sc_log(context, "Setting SC_AC_CONTEXT_SPECIFIC"); + pin_info->auth_method = SC_AC_CONTEXT_SPECIFIC; + rc = sc_pkcs15_verify_pin(p15card, auth_object, pPin, ulPinLen); + pin_info->auth_method = auth_meth_saved; + } else + rc = sc_pkcs15_verify_pin(p15card, auth_object, pPin, ulPinLen); + sc_log(context, "PKCS15 verify PIN returned %d", rc); if (rc != SC_SUCCESS)