@@ -534,10 +534,11 @@ int pcsc_card_connected; SCARDHANDLE pcsc_card; int transaction_depth; + int transaction_need_hw_lock; int slot_reset; CK_FLAGS token_flags; @@ -678,10 +679,11 @@ cackey_slots[idx].label = NULL; } cackey_slots[idx].pcsc_card_connected = 0; cackey_slots[idx].transaction_depth = 0; + cackey_slots[idx].transaction_need_hw_lock = 0; if (cackey_slots[idx].active) { CACKEY_DEBUG_PRINTF("Marking active slot %lu as being reset", (unsigned long) idx); } @@ -849,14 +851,48 @@ if (!slot->pcsc_card_connected) { slot->protocol = 0; CACKEY_DEBUG_PRINTF("SCardConnect(%s) called", slot->pcsc_reader); scard_conn_ret = SCardConnect(*cackey_pcsc_handle, slot->pcsc_reader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &slot->pcsc_card, &protocol); + + if (scard_conn_ret == SCARD_E_PROTO_MISMATCH) { + CACKEY_DEBUG_PRINTF("SCardConnect() returned SCARD_E_PROTO_MISMATCH, trying with just T=0") + scard_conn_ret = SCardConnect(*cackey_pcsc_handle, slot->pcsc_reader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0, &slot->pcsc_card, &protocol); + + if (scard_conn_ret == SCARD_E_PROTO_MISMATCH) { + CACKEY_DEBUG_PRINTF("SCardConnect() returned SCARD_E_PROTO_MISMATCH, trying with just T=1") + scard_conn_ret = SCardConnect(*cackey_pcsc_handle, slot->pcsc_reader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T1, &slot->pcsc_card, &protocol); + } + } if (scard_conn_ret == SCARD_W_UNPOWERED_CARD) { + CACKEY_DEBUG_PRINTF("SCardConnect() returned SCARD_W_UNPOWERED_CARD, trying to re-connect..."); + scard_conn_ret = SCardConnect(*cackey_pcsc_handle, slot->pcsc_reader, SCARD_SHARE_DIRECT, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &slot->pcsc_card, &protocol); + + if (scard_conn_ret == SCARD_E_PROTO_MISMATCH) { + CACKEY_DEBUG_PRINTF("SCardConnect() returned SCARD_E_PROTO_MISMATCH, trying with just T=0") + scard_conn_ret = SCardConnect(*cackey_pcsc_handle, slot->pcsc_reader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0, &slot->pcsc_card, &protocol); + + if (scard_conn_ret == SCARD_E_PROTO_MISMATCH) { + CACKEY_DEBUG_PRINTF("SCardConnect() returned SCARD_E_PROTO_MISMATCH, trying with just T=1") + scard_conn_ret = SCardConnect(*cackey_pcsc_handle, slot->pcsc_reader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T1, &slot->pcsc_card, &protocol); + } + } + scard_conn_ret = SCardReconnect(slot->pcsc_card, SCARD_SHARE_SHARED, protocol, SCARD_RESET_CARD, &protocol); + + if (scard_conn_ret == SCARD_E_PROTO_MISMATCH) { + CACKEY_DEBUG_PRINTF("SCardReconnect() returned SCARD_E_PROTO_MISMATCH, trying with just T=0") + scard_conn_ret = SCardReconnect(slot->pcsc_card, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0, SCARD_RESET_CARD, &protocol); + + if (scard_conn_ret == SCARD_E_PROTO_MISMATCH) { + CACKEY_DEBUG_PRINTF("SCardReconnect() returned SCARD_E_PROTO_MISMATCH, trying with just T=1") + scard_conn_ret = SCardReconnect(slot->pcsc_card, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T1, SCARD_RESET_CARD, &protocol); + } + } + } if (scard_conn_ret != SCARD_S_SUCCESS) { CACKEY_DEBUG_PRINTF("Connection to card failed, returning in failure (SCardConnect() = %s/%li)", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_conn_ret), (long) scard_conn_ret); @@ -863,10 +899,11 @@ return(CACKEY_PCSC_E_GENERIC); } slot->pcsc_card_connected = 1; slot->transaction_depth = 0; + slot->transaction_need_hw_lock = 0; slot->protocol = protocol; } return(CACKEY_PCSC_S_OK); } @@ -900,15 +937,17 @@ return(CACKEY_PCSC_E_GENERIC); } slot->transaction_depth++; - if (slot->transaction_depth > 1) { + if (slot->transaction_depth > 1 && !slot->transaction_need_hw_lock) { CACKEY_DEBUG_PRINTF("Already in a transaction, performing no action (new depth = %i)", slot->transaction_depth); return(CACKEY_PCSC_S_OK); } + + slot->transaction_need_hw_lock = 0; scard_trans_ret = SCardBeginTransaction(slot->pcsc_card); if (scard_trans_ret != SCARD_S_SUCCESS) { CACKEY_DEBUG_PRINTF("Unable to begin transaction, returning in error"); @@ -940,11 +979,21 @@ LONG scard_trans_ret; CACKEY_DEBUG_PRINTF("Called."); if (!slot->pcsc_card_connected) { - CACKEY_DEBUG_PRINTF("Card is not connected, unable to end transaction"); + CACKEY_DEBUG_PRINTF("Card is not connected, unable to end transaction on card"); + + if (slot->transaction_depth > 0) { + CACKEY_DEBUG_PRINTF("Decreasing transaction depth and asking for a hardware lock on the next begin transaction (current depth = %i)", slot->transaction_depth); + + slot->transaction_depth--; + + if (slot->transaction_depth > 0) { + slot->transaction_need_hw_lock = 1; + } + } return(CACKEY_PCSC_E_GENERIC); } if (slot->transaction_depth == 0) { @@ -956,11 +1005,11 @@ 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); + return(CACKEY_PCSC_S_OK); } 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"); @@ -1049,11 +1098,11 @@ DWORD protocol; DWORD xmit_len, recv_len; LONG scard_xmit_ret, scard_reconn_ret; BYTE xmit_buf[1024], recv_buf[1024]; int pcsc_connect_ret, pcsc_getresp_ret; - int idx; + int idx, retry; CACKEY_DEBUG_PRINTF("Called."); if (!slot) { CACKEY_DEBUG_PRINTF("Invalid slot specified."); @@ -1109,16 +1158,25 @@ } else { CACKEY_DEBUG_PRINTBUF("Sending APDU:", xmit_buf, xmit_len); } recv_len = sizeof(recv_buf); - scard_xmit_ret = SCardTransmit(slot->pcsc_card, pioSendPci, xmit_buf, xmit_len, NULL, recv_buf, &recv_len); + for (retry = 0; retry < 10; retry++) { + CACKEY_DEBUG_PRINTF("Calling SCardTransmit()"); + scard_xmit_ret = SCardTransmit(slot->pcsc_card, pioSendPci, xmit_buf, xmit_len, NULL, recv_buf, &recv_len); + + if (scard_xmit_ret == SCARD_E_NOT_TRANSACTED) { + CACKEY_DEBUG_PRINTF("Failed to send APDU to card (SCardTransmit() = %s/%lx), will retry...", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_xmit_ret), (unsigned long) scard_xmit_ret); + } else { + break; + } + } + 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); + CACKEY_DEBUG_PRINTF("Marking slot as having been reset"); - - slot->transaction_depth = 0; slot->slot_reset = 1; if (scard_xmit_ret == SCARD_W_RESET_CARD) { CACKEY_DEBUG_PRINTF("Reset required, please hold..."); @@ -1142,10 +1200,11 @@ } /* Re-establish transaction, if it was present */ if (slot->transaction_depth > 0) { slot->transaction_depth--; + slot->transaction_need_hw_lock = 1; cackey_begin_transaction(slot); } CACKEY_DEBUG_PRINTF("Reset successful, retransmitting"); @@ -2282,10 +2341,11 @@ slot->protocol = protocol; /* Re-establish transaction, if it was present */ if (slot->transaction_depth > 0) { slot->transaction_depth--; + slot->transaction_need_hw_lock = 1; cackey_begin_transaction(slot); } CACKEY_DEBUG_PRINTF("Reset successful, requerying"); status_ret = SCardStatus(slot->pcsc_card, NULL, &reader_len, &state, &protocol, atr, &atr_len); @@ -3025,10 +3085,11 @@ for (idx = 0; idx < (sizeof(cackey_slots) / sizeof(cackey_slots[0])); idx++) { cackey_slots[idx].active = 0; cackey_slots[idx].pcsc_reader = NULL; cackey_slots[idx].transaction_depth = 0; + cackey_slots[idx].transaction_need_hw_lock = 0; cackey_slots[idx].slot_reset = 0; cackey_slots[idx].token_flags = 0; cackey_slots[idx].label = NULL; } @@ -3240,10 +3301,11 @@ if (pSlotList) { cackey_slots[currslot].active = 1; cackey_slots[currslot].pcsc_reader = strdup(pcsc_readers); cackey_slots[currslot].pcsc_card_connected = 0; cackey_slots[currslot].transaction_depth = 0; + cackey_slots[currslot].transaction_need_hw_lock = 0; cackey_slots[currslot].slot_reset = 1; cackey_slots[currslot].token_flags = CKF_LOGIN_REQUIRED; cackey_slots[currslot].label = NULL; } currslot++;