Index: cackey.c ================================================================== --- cackey.c +++ cackey.c @@ -784,17 +784,20 @@ CK_ULONG decrypt_mech_parmlen; struct cackey_identity *decrypt_identity; }; struct cackey_slot { + unsigned int id; + int active; int internal; char *pcsc_reader; int pcsc_card_connected; SCARDHANDLE pcsc_card; + DWORD pcsc_state; int transaction_depth; int transaction_need_hw_lock; int slot_reset; @@ -882,10 +885,11 @@ static char *cackey_pin_command = NULL; static char *cackey_pin_command_xonly = NULL; /* PCSC Global Handles */ static LPSCARDCONTEXT cackey_pcsc_handle = NULL; +static LPSCARDCONTEXT cackey_waiting_pcsc_handle = NULL; static unsigned long cackey_getversion(void) { static unsigned long retval = 255; unsigned long major = 0; unsigned long minor = 0; @@ -1019,10 +1023,39 @@ cackey_slots_disconnect_all(); return(CACKEY_PCSC_E_GENERIC); } } + + if (cackey_waiting_pcsc_handle == NULL) { + cackey_waiting_pcsc_handle = malloc(sizeof(*cackey_waiting_pcsc_handle)); + if (cackey_waiting_pcsc_handle == NULL) { + CACKEY_DEBUG_PRINTF("Call to malloc() failed, returning in failure"); + + cackey_slots_disconnect_all(); + + free(cackey_pcsc_handle); + cackey_pcsc_handle = NULL; + + return(CACKEY_PCSC_E_GENERIC); + } + + CACKEY_DEBUG_PRINTF("SCardEstablishContext() called"); + scard_est_context_ret = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, cackey_waiting_pcsc_handle); + if (scard_est_context_ret != SCARD_S_SUCCESS) { + CACKEY_DEBUG_PRINTF("Call to SCardEstablishContext failed (returned %s/%li), returning in failure", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_est_context_ret), (long) scard_est_context_ret); + + free(cackey_pcsc_handle); + cackey_pcsc_handle = NULL; + free(cackey_waiting_pcsc_handle); + cackey_waiting_pcsc_handle = NULL; + + cackey_slots_disconnect_all(); + + return(CACKEY_PCSC_E_GENERIC); + } + } #ifdef HAVE_SCARDISVALIDCONTEXT CACKEY_DEBUG_PRINTF("SCardIsValidContext() called"); scard_isvalid_ret = SCardIsValidContext(*cackey_pcsc_handle); if (scard_isvalid_ret != SCARD_S_SUCCESS) { @@ -1041,10 +1074,31 @@ return(CACKEY_PCSC_E_GENERIC); } CACKEY_DEBUG_PRINTF("Handle has been re-established"); } + + CACKEY_DEBUG_PRINTF("SCardIsValidContext() called"); + scard_isvalid_ret = SCardIsValidContext(*cackey_waiting_pcsc_handle); + if (scard_isvalid_ret != SCARD_S_SUCCESS) { + CACKEY_DEBUG_PRINTF("Handle has become invalid (SCardIsValidContext = %s/%li), trying to re-establish...", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_isvalid_ret), (long) scard_isvalid_ret); + + CACKEY_DEBUG_PRINTF("SCardEstablishContext() called"); + scard_est_context_ret = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, cackey_waiting_pcsc_handle); + if (scard_est_context_ret != SCARD_S_SUCCESS) { + CACKEY_DEBUG_PRINTF("Call to SCardEstablishContext failed (returned %s/%li), returning in failure", CACKEY_DEBUG_FUNC_SCARDERR_TO_STR(scard_est_context_ret), (long) scard_est_context_ret); + + free(cackey_waiting_pcsc_handle); + cackey_waiting_pcsc_handle = NULL; + + cackey_slots_disconnect_all(); + + return(CACKEY_PCSC_E_GENERIC); + } + + CACKEY_DEBUG_PRINTF("Waiting handle has been re-established"); + } #endif CACKEY_DEBUG_PRINTF("Sucessfully connected to PC/SC, returning in success"); return(CACKEY_PCSC_S_OK); @@ -1066,30 +1120,37 @@ * the global handle. * */ static cackey_ret cackey_pcsc_disconnect(void) { LONG scard_rel_context_ret; + cackey_ret retval = CACKEY_PCSC_S_OK; CACKEY_DEBUG_PRINTF("Called."); - if (cackey_pcsc_handle == NULL) { - return(CACKEY_PCSC_S_OK); - } + if (cackey_pcsc_handle != NULL) { + scard_rel_context_ret = SCardReleaseContext(*cackey_pcsc_handle); + if (scard_rel_context_ret != SCARD_S_SUCCESS) { + retval = CACKEY_PCSC_E_GENERIC; + } - scard_rel_context_ret = SCardReleaseContext(*cackey_pcsc_handle); - - if (cackey_pcsc_handle) { free(cackey_pcsc_handle); cackey_pcsc_handle = NULL; } - if (scard_rel_context_ret != SCARD_S_SUCCESS) { - return(CACKEY_PCSC_E_GENERIC); + if (cackey_waiting_pcsc_handle != NULL) { + scard_rel_context_ret = SCardReleaseContext(*cackey_waiting_pcsc_handle); + if (scard_rel_context_ret != SCARD_S_SUCCESS) { + retval = CACKEY_PCSC_E_GENERIC; + } + + free(cackey_waiting_pcsc_handle); + + cackey_waiting_pcsc_handle = NULL; } - return(CACKEY_PCSC_S_OK); + return(retval); } /* * SYNPOSIS * void cackey_mark_slot_reset(struct cackey_slot *slot); @@ -1117,10 +1178,11 @@ } slot->slot_reset = 1; slot->pcsc_card_connected = 0; slot->token_flags = CKF_LOGIN_REQUIRED; + slot->pcsc_state = SCARD_STATE_UNAWARE; CACKEY_DEBUG_PRINTF("Returning."); return; } @@ -4111,12 +4173,14 @@ for (idx = 0; idx < (sizeof(cackey_sessions) / sizeof(cackey_sessions[0])); idx++) { cackey_sessions[idx].active = 0; } for (idx = 0; idx < (sizeof(cackey_slots) / sizeof(cackey_slots[0])); idx++) { + cackey_slots[idx].id = idx; cackey_slots[idx].active = 0; cackey_slots[idx].pcsc_reader = NULL; + cackey_slots[idx].pcsc_state = SCARD_STATE_UNAWARE; 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; @@ -4197,10 +4261,12 @@ if (cackey_sessions[idx].active) { C_CloseSession(idx); } } + cackey_mutex_lock(cackey_biglock); + cackey_slots_disconnect_all(); for (idx = 0; idx < (sizeof(cackey_slots) / sizeof(cackey_slots[0])); idx++) { if (cackey_slots[idx].internal) { continue; @@ -4218,10 +4284,12 @@ } cackey_pcsc_disconnect(); cackey_initialized = 0; + + cackey_mutex_unlock(cackey_biglock); CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK); return(CKR_OK); } @@ -4722,28 +4790,223 @@ return(CKR_OK); } CK_DEFINE_FUNCTION(CK_RV, C_WaitForSlotEvent)(CK_FLAGS flags, CK_SLOT_ID_PTR pSlotID, CK_VOID_PTR pReserved) { + SCARD_READERSTATE reader_states[(sizeof(cackey_slots) / sizeof(cackey_slots[0])) + 1]; + LPSCARDCONTEXT handle; + LONG scard_getstatchng_ret; + cackey_ret pcsc_connect_ret; + struct cackey_slot *cackey_slot; + unsigned int currslot, reader_state_slot, reader_state_slot_cnt; + int mutex_retval; + int slot_changed; + CACKEY_DEBUG_PRINTF("Called."); if (pReserved != NULL) { CACKEY_DEBUG_PRINTF("Error. pReserved is not NULL."); return(CKR_ARGUMENTS_BAD); } + + if (pSlotID == NULL) { + CACKEY_DEBUG_PRINTF("Error. pSlotID is NULL."); + + return(CKR_ARGUMENTS_BAD); + } if (!cackey_initialized) { CACKEY_DEBUG_PRINTF("Error. Not initialized."); return(CKR_CRYPTOKI_NOT_INITIALIZED); } - /* XXX: TODO: Implement this... */ - CACKEY_DEBUG_PRINTF("Returning CKR_FUNCTION_NOT_SUPPORTED (%i)", CKR_FUNCTION_NOT_SUPPORTED); + mutex_retval = cackey_mutex_lock(cackey_biglock); + if (mutex_retval != 0) { + CACKEY_DEBUG_PRINTF("Error. Locking failed."); - return(CKR_FUNCTION_NOT_SUPPORTED); + return(CKR_GENERAL_ERROR); + } + + pcsc_connect_ret = cackey_pcsc_connect(); + if (pcsc_connect_ret != CACKEY_PCSC_S_OK) { + cackey_mutex_unlock(cackey_biglock); + + CACKEY_DEBUG_PRINTF("Connection to PC/SC failed, returning in failure"); + + return(CKR_GENERAL_ERROR); + } + + for (reader_state_slot = currslot = 0; currslot < (sizeof(cackey_slots) / sizeof(cackey_slots[0])); currslot++) { + if (cackey_slots[currslot].internal) { + continue; + } + + if (cackey_slots[currslot].active == 0) { + continue; + } + + reader_states[reader_state_slot].szReader = cackey_slots[currslot].pcsc_reader; + reader_states[reader_state_slot].pvUserData = &cackey_slots[currslot]; + + if ((flags & CKF_DONT_BLOCK) == CKF_DONT_BLOCK) { + reader_states[reader_state_slot].dwCurrentState = SCARD_STATE_UNAWARE; + } else { + reader_states[reader_state_slot].dwCurrentState = cackey_slots[currslot].pcsc_state; + } + + reader_state_slot++; + } + + mutex_retval = cackey_mutex_unlock(cackey_biglock); + if (mutex_retval != 0) { + CACKEY_DEBUG_PRINTF("Error. Unlocking failed."); + + return(CKR_GENERAL_ERROR); + } + + reader_states[reader_state_slot].szReader = "\\\\?PnP?\\Notification"; + reader_states[reader_state_slot].pvUserData = NULL; + reader_states[reader_state_slot].dwCurrentState = SCARD_STATE_UNAWARE; + reader_state_slot++; + + reader_state_slot_cnt = reader_state_slot; + + while (1) { + cackey_mutex_lock(cackey_biglock); + + handle = cackey_waiting_pcsc_handle; + + cackey_mutex_unlock(cackey_biglock); + + if (handle == NULL) { + CACKEY_DEBUG_PRINTF("Waiting handle disappeared, not attempting to wait."); + + break; + } + + CACKEY_DEBUG_PRINTF("Calling SCardGetStatusChange(), which will block for 1 second"); + scard_getstatchng_ret = SCardGetStatusChange(*handle, 1000, reader_states, reader_state_slot_cnt); + + if (scard_getstatchng_ret == SCARD_E_TIMEOUT) { + continue; + } + + break; + } + + if (scard_getstatchng_ret != SCARD_S_SUCCESS) { + CACKEY_DEBUG_PRINTF("Returning CKR_GENERAL_ERROR (%i) because SCardGetStatusChange failed: %lx", CKR_GENERAL_ERROR, scard_getstatchng_ret); + + return(CKR_GENERAL_ERROR); + } + + mutex_retval = cackey_mutex_lock(cackey_biglock); + if (mutex_retval != 0) { + CACKEY_DEBUG_PRINTF("Error. Locking failed."); + + return(CKR_GENERAL_ERROR); + } + + for (reader_state_slot = 0; reader_state_slot < reader_state_slot_cnt; reader_state_slot++) { + CACKEY_DEBUG_PRINTF("[pcsc/slot = %u] CurrentState = %lx, EventState = %lx", + reader_state_slot, + reader_states[reader_state_slot].dwCurrentState & 0xffff, + reader_states[reader_state_slot].dwEventState & 0xffff + ); + + cackey_slot = NULL; + for (currslot = 0; currslot < (sizeof(cackey_slots) / sizeof(cackey_slots[0])); currslot++) { + if (cackey_slots[currslot].internal) { + continue; + } + + if (reader_states[reader_state_slot].szReader == NULL) { + continue; + } + + if (cackey_slots[currslot].pcsc_reader == NULL) { + continue; + } + + CACKEY_DEBUG_PRINTF("Checking to see if pcsc/slot:%u (%s) is cackey/slot:%u (%s)...", + reader_state_slot, + reader_states[reader_state_slot].szReader, + currslot, + cackey_slots[currslot].pcsc_reader + ); + + if (strcmp(reader_states[reader_state_slot].szReader, cackey_slots[currslot].pcsc_reader) != 0) { + CACKEY_DEBUG_PRINTF(" no match"); + + continue; + } + + cackey_slot = &cackey_slots[currslot]; + + CACKEY_DEBUG_PRINTF(" match, slot = %p", cackey_slot); + + break; + } + + if (cackey_slot == NULL) { + /* XXX: TODO: Someone plugged in a new slot or removed a slot */ + CACKEY_DEBUG_PRINTF("WARNING: Unhandled code. Slot inserted or removed."); + + continue; + } + + slot_changed = 0; + + if ((flags & CKF_DONT_BLOCK) == CKF_DONT_BLOCK) { + if (cackey_slot->pcsc_state != reader_states[reader_state_slot].dwEventState) { + slot_changed = 1; + } + } else { + if (reader_states[reader_state_slot].dwCurrentState != reader_states[reader_state_slot].dwEventState) { + slot_changed = 1; + } + } + + if (slot_changed == 0) { + CACKEY_DEBUG_PRINTF("[pcsc/slot = %u] Slot did not change", (unsigned int) reader_state_slot); + + continue; + } + + CACKEY_DEBUG_PRINTF("Returning slot changed: %u", (unsigned int) cackey_slot->id); + + cackey_slot->pcsc_state = reader_states[reader_state_slot].dwEventState; + *pSlotID = (CK_SLOT_ID) cackey_slot->id; + + CACKEY_DEBUG_PRINTF("Returning CKR_OK (%i)", CKR_OK); + + mutex_retval = cackey_mutex_unlock(cackey_biglock); + if (mutex_retval != 0) { + CACKEY_DEBUG_PRINTF("Error. Unlocking failed."); + + return(CKR_GENERAL_ERROR); + } + + return(CKR_OK); + } + + mutex_retval = cackey_mutex_unlock(cackey_biglock); + if (mutex_retval != 0) { + CACKEY_DEBUG_PRINTF("Error. Unlocking failed."); + + return(CKR_GENERAL_ERROR); + } + + if ((flags & CKF_DONT_BLOCK) != CKF_DONT_BLOCK) { + CACKEY_DEBUG_PRINTF("Returning CKR_NO_EVENT (%i), but asked to block !? BUG ENCOUNTERED.", CKR_NO_EVENT); + } else { + CACKEY_DEBUG_PRINTF("Returning CKR_NO_EVENT (%i)", CKR_NO_EVENT); + } + + return(CKR_NO_EVENT); } CK_DEFINE_FUNCTION(CK_RV, C_GetMechanismList)(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList, CK_ULONG_PTR pulCount) { CACKEY_DEBUG_PRINTF("Called."); Index: test.c ================================================================== --- test.c +++ test.c @@ -228,10 +228,26 @@ chk_rv = C_GetSlotList(FALSE, slots, &numSlots); if (chk_rv != CKR_OK) { return(1); } + /* Test waiting for slot events */ + currSlot = 0; + printf("Please insert a card now.\n"); + + /* Initially, every slot has changed state (but probably should not) */ + chk_rv = C_WaitForSlotEvent(0, &currSlot, NULL); + + /* This actually waits */ + chk_rv = C_WaitForSlotEvent(0, &currSlot, NULL); + if (chk_rv != CKR_OK) { + printf("Failed to wait for slot event.\n"); + } + + /* This just ensures DONT_BLOCK works */ + chk_rv = C_WaitForSlotEvent(CKF_DONT_BLOCK, &currSlot, NULL); + for (currSlot = 0; currSlot < numSlots; currSlot++) { printf(" Slot %lu:\n", currSlot); chk_rv = C_GetSlotInfo(slots[currSlot], &slotInfo); if (chk_rv != CKR_OK) {