@@ -483,10 +483,12 @@ char *pcsc_reader; int pcsc_card_connected; SCARDHANDLE pcsc_card; + + int transaction_depth; }; typedef enum { CACKEY_TLV_APP_GENERIC = 0x01, CACKEY_TLV_APP_SKI = 0x02, @@ -740,10 +742,11 @@ return(CACKEY_PCSC_E_GENERIC); } slot->pcsc_card_connected = 1; + slot->transaction_depth = 0; } return(CACKEY_PCSC_S_OK); } @@ -773,17 +776,27 @@ if (cackey_conn_ret != CACKEY_PCSC_S_OK) { CACKEY_DEBUG_PRINTF("Unable to connect to card, returning in error"); return(CACKEY_PCSC_E_GENERIC); } + + slot->transaction_depth++; + + if (slot->transaction_depth > 1) { + CACKEY_DEBUG_PRINTF("Already in a transaction, performing no action (new depth = %i)", slot->transaction_depth); + + return(CACKEY_PCSC_S_OK); + } scard_trans_ret = SCardBeginTransaction(slot->pcsc_card); if (scard_trans_ret != SCARD_S_SUCCESS) { CACKEY_DEBUG_PRINTF("Unable to begin transaction, returning in error"); return(CACKEY_PCSC_E_GENERIC); } + + CACKEY_DEBUG_PRINTF("Sucessfully began transaction on slot (%s)", slot->pcsc_reader); return(CACKEY_PCSC_S_OK); } /* @@ -810,17 +823,33 @@ if (!slot->pcsc_card_connected) { CACKEY_DEBUG_PRINTF("Card is not connected, unable to end transaction"); return(CACKEY_PCSC_E_GENERIC); } + + if (slot->transaction_depth == 0) { + CACKEY_DEBUG_PRINTF("Terminating a transaction that has not begun!"); + + return(CACKEY_PCSC_E_GENERIC); + } + + slot->transaction_depth--; + + if (slot->transaction_depth > 0) { + CACKEY_DEBUG_PRINTF("Transactions still in progress, not terminating on-card Transaction (current depth = %i)", slot->transaction_depth); + + return(CACKEY_PCSC_E_GENERIC); + } scard_trans_ret = SCardEndTransaction(slot->pcsc_card, SCARD_LEAVE_CARD); if (scard_trans_ret != SCARD_S_SUCCESS) { CACKEY_DEBUG_PRINTF("Unable to end transaction, returning in error"); return(CACKEY_PCSC_E_GENERIC); } + + CACKEY_DEBUG_PRINTF("Sucessfully terminated transaction on slot (%s)", slot->pcsc_reader); return(CACKEY_PCSC_S_OK); } /* APDU Related Functions */ @@ -929,48 +958,71 @@ } if (le != 0x00) { xmit_buf[xmit_len++] = le; } + + /* Begin Smartcard Transaction */ + cackey_begin_transaction(slot); CACKEY_DEBUG_PRINTBUF("Sending APDU:", xmit_buf, xmit_len); recv_len = sizeof(recv_buf); scard_xmit_ret = SCardTransmit(slot->pcsc_card, SCARD_PCI_T0, xmit_buf, xmit_len, SCARD_PCI_T1, recv_buf, &recv_len); if (scard_xmit_ret != SCARD_S_SUCCESS) { CACKEY_DEBUG_PRINTF("Failed to send APDU to card (SCardTransmit() = %s/%lx)", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_xmit_ret), (unsigned long) scard_xmit_ret); + + slot->transaction_depth = 0; if (scard_xmit_ret == SCARD_W_RESET_CARD) { CACKEY_DEBUG_PRINTF("Reset required, please hold..."); scard_reconn_ret = SCardReconnect(slot->pcsc_card, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0, SCARD_RESET_CARD, &protocol); if (scard_reconn_ret == SCARD_S_SUCCESS) { + /* Re-establish transaction, if it was present */ + if (slot->transaction_depth > 0) { + slot->transaction_depth--; + cackey_begin_transaction(slot); + } + CACKEY_DEBUG_PRINTF("Reset successful, retransmitting"); scard_xmit_ret = SCardTransmit(slot->pcsc_card, SCARD_PCI_T0, xmit_buf, xmit_len, SCARD_PCI_T0, recv_buf, &recv_len); if (scard_xmit_ret != SCARD_S_SUCCESS) { CACKEY_DEBUG_PRINTF("Retransmit failed, returning in failure after disconnecting the card (SCardTransmit = %s/%li)", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_xmit_ret), (long) scard_xmit_ret); SCardDisconnect(slot->pcsc_card, SCARD_RESET_CARD); slot->pcsc_card_connected = 0; + + /* End Smartcard Transaction */ + slot->transaction_depth = 1; + cackey_end_transaction(slot); return(CACKEY_PCSC_E_GENERIC); } } else { CACKEY_DEBUG_PRINTF("Disconnecting card"); SCardDisconnect(slot->pcsc_card, SCARD_RESET_CARD); slot->pcsc_card_connected = 0; + + /* End Smartcard Transaction */ + slot->transaction_depth = 1; + cackey_end_transaction(slot); CACKEY_DEBUG_PRINTF("Returning in failure"); return(CACKEY_PCSC_E_GENERIC); } } else { CACKEY_DEBUG_PRINTF("Disconnecting card"); SCardDisconnect(slot->pcsc_card, SCARD_RESET_CARD); slot->pcsc_card_connected = 0; + + /* End Smartcard Transaction */ + slot->transaction_depth = 1; + cackey_end_transaction(slot); CACKEY_DEBUG_PRINTF("Returning in failure"); return(CACKEY_PCSC_E_GENERIC); } } @@ -978,10 +1030,13 @@ CACKEY_DEBUG_PRINTBUF("Returned Value:", recv_buf, recv_len); if (recv_len < 2) { /* Minimal response length is 2 bytes, returning in failure */ CACKEY_DEBUG_PRINTF("Response too small, returning in failure (recv_len = %lu)", (unsigned long) recv_len); + + /* End Smartcard Transaction */ + cackey_end_transaction(slot); return(CACKEY_PCSC_E_GENERIC); } /* Determine result code */ @@ -1023,21 +1078,30 @@ CACKEY_DEBUG_PRINTF("Buffer read required"); pcsc_getresp_ret = cackey_send_apdu(slot, GSCIS_CLASS_ISO7816, GSCIS_INSTR_GET_RESPONSE, 0x00, 0x00, 0, NULL, minor_rc, respcode, respdata, &tmp_respdata_len); if (pcsc_getresp_ret != CACKEY_PCSC_S_OK) { CACKEY_DEBUG_PRINTF("Buffer read failed! Returning in failure"); + + /* End Smartcard Transaction */ + cackey_end_transaction(slot); return(CACKEY_PCSC_E_GENERIC); } if (respdata_len) { *respdata_len += tmp_respdata_len; } + + /* End Smartcard Transaction */ + cackey_end_transaction(slot); CACKEY_DEBUG_PRINTF("Returning in success (buffer read complete)"); return(CACKEY_PCSC_S_OK); } + + /* End Smartcard Transaction */ + cackey_end_transaction(slot); if (major_rc == 0x90) { /* Success */ CACKEY_DEBUG_PRINTF("Returning in success (major_rc = 0x90)"); @@ -1490,10 +1554,11 @@ struct cackey_pcsc_identity *curr_id; struct cackey_tlv_entity *ccc_tlv, *ccc_curr, *app_tlv, *app_curr; unsigned char ccc_aid[] = {GSCIS_AID_CCC}; unsigned char curr_aid[7]; unsigned long outidx = 0; + cackey_ret transaction_ret; int certs_resizable; int send_ret, select_ret; CACKEY_DEBUG_PRINTF("Called."); @@ -1510,11 +1575,16 @@ return(certs); } } /* Begin a SmartCard transaction */ - cackey_begin_transaction(slot); + transaction_ret = cackey_begin_transaction(slot); + if (transaction_ret != CACKEY_PCSC_S_OK) { + CACKEY_DEBUG_PRINTF("Unable begin transaction, returning in failure"); + + return(NULL); + } if (certs == NULL) { certs = malloc(sizeof(*certs) * 5); *count = 5; certs_resizable = 1;