/* Reset reader */ static int ctapi_reset(sc_reader_t *reader) { struct ctapi_private_data *priv = GET_PRIV_DATA(reader); char rv; u8 cmd[5], rbuf[256], sad, dad; unsigned short lr; cmd[0] = CTBCS_CLA; cmd[1] = CTBCS_INS_RESET; cmd[2] = priv->slot ? CTBCS_P1_INTERFACE1 + priv->slot : CTBCS_P1_CT_KERNEL; cmd[3] = 0x00; /* No response. We might also use 0x01 (return ATR) or 0x02 (return historical bytes) here */ cmd[4] = 0x00; dad = 1; sad = 2; lr = 256; rv = priv->funcs.CT_data(priv->ctn, &dad, &sad, 5, cmd, &lr, rbuf); if (rv || (lr < 2)) { sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Error getting status of terminal: %d, using defaults\n", rv); return SC_ERROR_TRANSMIT_FAILED; } if (rbuf[lr-2] != 0x90) { sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "SW1/SW2: 0x%x/0x%x\n", rbuf[lr-2], rbuf[lr-1]); return SC_ERROR_TRANSMIT_FAILED; } return 0; }
static int ctapi_internal_transmit(sc_reader_t *reader, const u8 *sendbuf, size_t sendsize, u8 *recvbuf, size_t *recvsize, unsigned long control) { struct ctapi_private_data *priv = GET_PRIV_DATA(reader); u8 dad, sad; unsigned short lr; char rv; if (control) dad = 1; else dad = 0; sad = 2; lr = *recvsize; rv = priv->funcs.CT_data(priv->ctn, &dad, &sad, (unsigned short)sendsize, (u8 *) sendbuf, &lr, recvbuf); if (rv != 0) { sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Error transmitting APDU: %d\n", rv); return SC_ERROR_TRANSMIT_FAILED; } *recvsize = lr; return 0; }
static int pcsc_internal_transmit(sc_reader_t *reader, sc_slot_info_t *slot, const u8 *sendbuf, size_t sendsize, u8 *recvbuf, size_t *recvsize, unsigned long control) { struct pcsc_private_data *priv = GET_PRIV_DATA(reader); SCARD_IO_REQUEST sSendPci, sRecvPci; DWORD dwSendLength, dwRecvLength; LONG rv; SCARDHANDLE card; struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot); SC_FUNC_CALLED(reader->ctx, 3); assert(pslot != NULL); card = pslot->pcsc_card; sSendPci.dwProtocol = opensc_proto_to_pcsc(slot->active_protocol); sSendPci.cbPciLength = sizeof(sSendPci); sRecvPci.dwProtocol = opensc_proto_to_pcsc(slot->active_protocol); sRecvPci.cbPciLength = sizeof(sRecvPci); dwSendLength = sendsize; dwRecvLength = *recvsize; if (!control) { rv = priv->gpriv->SCardTransmit(card, &sSendPci, sendbuf, dwSendLength, &sRecvPci, recvbuf, &dwRecvLength); } else { if (priv->gpriv->SCardControlOLD != NULL) { rv = priv->gpriv->SCardControlOLD(card, sendbuf, dwSendLength, recvbuf, &dwRecvLength); } else { rv = priv->gpriv->SCardControl(card, (DWORD) control, sendbuf, dwSendLength, recvbuf, dwRecvLength, &dwRecvLength); } } if (rv != SCARD_S_SUCCESS) { switch (rv) { case SCARD_W_REMOVED_CARD: return SC_ERROR_CARD_REMOVED; case SCARD_E_NOT_TRANSACTED: if (!(pcsc_detect_card_presence(reader, slot) & SC_SLOT_CARD_PRESENT)) return SC_ERROR_CARD_REMOVED; return SC_ERROR_TRANSMIT_FAILED; default: /* Windows' PC/SC returns 0x8010002f (??) if a card is removed */ if (pcsc_detect_card_presence(reader, slot) != 1) return SC_ERROR_CARD_REMOVED; PCSC_ERROR(reader->ctx, "SCardTransmit failed", rv); return SC_ERROR_TRANSMIT_FAILED; } } if (!control && dwRecvLength < 2) return SC_ERROR_UNKNOWN_DATA_RECEIVED; *recvsize = dwRecvLength; return SC_SUCCESS; }
static int ctapi_connect(sc_reader_t *reader) { struct ctapi_private_data *priv = GET_PRIV_DATA(reader); char rv; u8 cmd[9], rbuf[256], sad, dad; unsigned short lr; int r; cmd[0] = CTBCS_CLA; cmd[1] = CTBCS_INS_REQUEST; cmd[2] = CTBCS_P1_INTERFACE1; cmd[3] = CTBCS_P2_REQUEST_GET_ATR; cmd[4] = 0x00; dad = 1; sad = 2; lr = 256; rv = priv->funcs.CT_data(priv->ctn, &dad, &sad, 5, cmd, &lr, rbuf); if (rv || rbuf[lr-2] != 0x90) { sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Error activating card: %d\n", rv); return SC_ERROR_TRANSMIT_FAILED; } if (lr < 2) SC_FUNC_RETURN(reader->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); lr -= 2; if (lr > SC_MAX_ATR_SIZE) return SC_ERROR_INTERNAL; reader->atr.len = lr; memcpy(reader->atr.value, rbuf, lr); r = _sc_parse_atr(reader); return 0; }
static int ctapi_release(sc_reader_t *reader) { struct ctapi_private_data *priv = GET_PRIV_DATA(reader); priv->funcs.CT_close(priv->ctn); free(priv); return 0; }
static int pcsc_disconnect(sc_reader_t * reader, sc_slot_info_t * slot) { struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot); struct pcsc_private_data *priv = GET_PRIV_DATA(reader); priv->gpriv->SCardDisconnect(pslot->pcsc_card, priv->gpriv->connect_reset ? SCARD_RESET_CARD : SCARD_LEAVE_CARD); memset(pslot, 0, sizeof(*pslot)); slot->flags = 0; return SC_SUCCESS; }
static int ctapi_release(sc_reader_t *reader) { struct ctapi_private_data *priv = GET_PRIV_DATA(reader); if (!(reader->ctx->flags & SC_CTX_FLAG_TERMINATE)) priv->funcs.CT_close(priv->ctn); free(priv); return 0; }
static int pcsc_release(sc_reader_t *reader) { struct pcsc_private_data *priv = GET_PRIV_DATA(reader); free(priv->reader_name); free(priv); if (reader->slot[0].drv_data != NULL) { free(reader->slot[0].drv_data); reader->slot[0].drv_data = NULL; } return SC_SUCCESS; }
static int pcsc_unlock(sc_reader_t *reader, sc_slot_info_t *slot) { long rv; struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot); struct pcsc_private_data *priv = GET_PRIV_DATA(reader); SC_FUNC_CALLED(reader->ctx, 3); assert(pslot != NULL); rv = priv->gpriv->SCardEndTransaction(pslot->pcsc_card, priv->gpriv->transaction_reset ? SCARD_RESET_CARD : SCARD_LEAVE_CARD); pslot->locked = 0; if (rv != SCARD_S_SUCCESS) { PCSC_ERROR(reader->ctx, "SCardEndTransaction failed", rv); return pcsc_ret_to_error(rv); } return SC_SUCCESS; }
static int pcsc_reconnect(sc_reader_t * reader, sc_slot_info_t * slot, int reset) { DWORD active_proto, protocol; LONG rv; struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot); struct pcsc_private_data *priv = GET_PRIV_DATA(reader); int r; sc_debug(reader->ctx, "Reconnecting to the card..."); r = refresh_slot_attributes(reader, slot); if (r) return r; if (!(slot->flags & SC_SLOT_CARD_PRESENT)) return SC_ERROR_CARD_NOT_PRESENT; /* reconnect always unlocks transaction */ pslot->locked = 0; rv = priv->gpriv->SCardReconnect(pslot->pcsc_card, priv->gpriv->connect_exclusive ? SCARD_SHARE_EXCLUSIVE : SCARD_SHARE_SHARED, SCARD_PROTOCOL_ANY, reset ? SCARD_UNPOWER_CARD : SCARD_LEAVE_CARD, &active_proto); /* Check for protocol difference */ if (rv == SCARD_S_SUCCESS && _sc_check_forced_protocol (reader->ctx, slot->atr, slot->atr_len, (unsigned int *)&protocol)) { protocol = opensc_proto_to_pcsc(protocol); if (pcsc_proto_to_opensc(active_proto) != protocol) { rv = priv->gpriv->SCardReconnect(pslot->pcsc_card, priv->gpriv->connect_exclusive ? SCARD_SHARE_EXCLUSIVE : SCARD_SHARE_SHARED, protocol, SCARD_UNPOWER_CARD, &active_proto); } } if (rv != SCARD_S_SUCCESS) { PCSC_ERROR(reader->ctx, "SCardReconnect failed", rv); return rv; } slot->active_protocol = pcsc_proto_to_opensc(active_proto); return rv; }
static int refresh_attributes(sc_reader_t *reader) { struct ctapi_private_data *priv = GET_PRIV_DATA(reader); char rv; u8 cmd[5], rbuf[256], sad, dad; unsigned short lr; if (reader->ctx->flags & SC_CTX_FLAG_TERMINATE) return SC_ERROR_NOT_ALLOWED; cmd[0] = CTBCS_CLA; cmd[1] = CTBCS_INS_STATUS; cmd[2] = CTBCS_P1_CT_KERNEL; cmd[3] = CTBCS_P2_STATUS_ICC; cmd[4] = 0x00; dad = 1; sad = 2; lr = 256; reader->flags = 0; rv = priv->funcs.CT_data(priv->ctn, &dad, &sad, 5, cmd, &lr, rbuf); if (rv || (lr < 3) || (rbuf[lr-2] != 0x90)) { sc_log(reader->ctx, "Error getting status of terminal: %d/%d/0x%x", rv, lr, rbuf[lr-2]); return SC_ERROR_TRANSMIT_FAILED; } if (lr < 4) { if (rbuf[0] & CTBCS_DATA_STATUS_CARD) reader->flags = SC_READER_CARD_PRESENT; } else { if (rbuf[0] != CTBCS_P2_STATUS_ICC) { /* Should we be more tolerant here? I do not think so... */ sc_log(reader->ctx, "Invalid data object returned on CTBCS_P2_STATUS_ICC: 0x%x", rbuf[0]); return SC_ERROR_TRANSMIT_FAILED; } /* Fixme - should not be reached */ sc_log(reader->ctx, "Returned status for %d slots", rbuf[1]); reader->flags = SC_READER_CARD_PRESENT; } return 0; }
static int pcsc_lock(sc_reader_t *reader, sc_slot_info_t *slot) { long rv; struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot); struct pcsc_private_data *priv = GET_PRIV_DATA(reader); SC_FUNC_CALLED(reader->ctx, 3); assert(pslot != NULL); rv = priv->gpriv->SCardBeginTransaction(pslot->pcsc_card); switch (rv) { case SCARD_E_INVALID_HANDLE: case SCARD_E_READER_UNAVAILABLE: rv = pcsc_connect(reader, slot); if (rv != SCARD_S_SUCCESS) { PCSC_ERROR(reader->ctx, "SCardConnect failed", rv); return pcsc_ret_to_error(rv); } /* return failure so that upper layers will be notified and try to lock again */ return SC_ERROR_READER_REATTACHED; case SCARD_W_RESET_CARD: /* try to reconnect if the card was reset by some other application */ rv = pcsc_reconnect(reader, slot, 0); if (rv != SCARD_S_SUCCESS) { PCSC_ERROR(reader->ctx, "SCardReconnect failed", rv); return pcsc_ret_to_error(rv); } /* return failure so that upper layers will be notified and try to lock again */ return SC_ERROR_CARD_RESET; case SCARD_S_SUCCESS: pslot->locked = 1; return SC_SUCCESS; default: PCSC_ERROR(reader->ctx, "SCardBeginTransaction failed", rv); return pcsc_ret_to_error(rv); } }
static int pcsc_connect(sc_reader_t *reader, sc_slot_info_t *slot) { DWORD active_proto, protocol; SCARDHANDLE card_handle; LONG rv; struct pcsc_private_data *priv = GET_PRIV_DATA(reader); struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot); int r; u8 feature_buf[256], rbuf[SC_MAX_APDU_BUFFER_SIZE]; size_t rcount; DWORD i, feature_len, display_ioctl = 0; PCSC_TLV_STRUCTURE *pcsc_tlv; r = refresh_slot_attributes(reader, slot); if (r) return r; if (!(slot->flags & SC_SLOT_CARD_PRESENT)) return SC_ERROR_CARD_NOT_PRESENT; /* Always connect with whatever protocol possible */ rv = priv->gpriv->SCardConnect(priv->gpriv->pcsc_ctx, priv->reader_name, priv->gpriv->connect_exclusive ? SCARD_SHARE_EXCLUSIVE : SCARD_SHARE_SHARED, SCARD_PROTOCOL_ANY, &card_handle, &active_proto); if (rv != SCARD_S_SUCCESS) { PCSC_ERROR(reader->ctx, "SCardConnect failed", rv); return pcsc_ret_to_error(rv); } slot->active_protocol = pcsc_proto_to_opensc(active_proto); pslot->pcsc_card = card_handle; /* after connect reader is not locked yet */ pslot->locked = 0; sc_debug(reader->ctx, "After connect protocol = %d", slot->active_protocol); /* If we need a specific protocol, reconnect if needed */ if (_sc_check_forced_protocol(reader->ctx, slot->atr, slot->atr_len, (unsigned int *) &protocol)) { /* If current protocol differs from the protocol we want to force */ if (slot->active_protocol != protocol) { sc_debug(reader->ctx, "Protocol difference, forcing protocol (%d)", protocol); /* Reconnect with a reset. pcsc_reconnect figures out the right forced protocol */ rv = pcsc_reconnect(reader, slot, 1); if (rv != SCARD_S_SUCCESS) { PCSC_ERROR(reader->ctx, "SCardReconnect (to force protocol) failed", rv); return pcsc_ret_to_error(rv); } sc_debug(reader->ctx, "Proto after reconnect = %d", slot->active_protocol); } } /* check for pinpad support */ if (priv->gpriv->SCardControl != NULL) { sc_debug(reader->ctx, "Requesting reader features ... "); rv = priv->gpriv->SCardControl(pslot->pcsc_card, CM_IOCTL_GET_FEATURE_REQUEST, NULL, 0, feature_buf, sizeof(feature_buf), &feature_len); if (rv != SCARD_S_SUCCESS) { sc_debug(reader->ctx, "SCardControl failed %08x", rv); } else { if ((feature_len % sizeof(PCSC_TLV_STRUCTURE)) != 0) { sc_debug(reader->ctx, "Inconsistent TLV from reader!"); } else { char *log_disabled = "but it's disabled in configuration file"; /* get the number of elements instead of the complete size */ feature_len /= sizeof(PCSC_TLV_STRUCTURE); pcsc_tlv = (PCSC_TLV_STRUCTURE *)feature_buf; for (i = 0; i < feature_len; i++) { if (pcsc_tlv[i].tag == FEATURE_VERIFY_PIN_DIRECT) { pslot->verify_ioctl = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_VERIFY_PIN_START) { pslot->verify_ioctl_start = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_VERIFY_PIN_FINISH) { pslot->verify_ioctl_finish = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_MODIFY_PIN_DIRECT) { pslot->modify_ioctl = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_MODIFY_PIN_START) { pslot->modify_ioctl_start = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_MODIFY_PIN_FINISH) { pslot->modify_ioctl_finish = ntohl(pcsc_tlv[i].value); } else if (pcsc_tlv[i].tag == FEATURE_IFD_PIN_PROPERTIES) { display_ioctl = ntohl(pcsc_tlv[i].value); } else { sc_debug(reader->ctx, "Reader feature %02x is not supported", pcsc_tlv[i].tag); } } /* Set slot capabilities based on detected IOCTLs */ if (pslot->verify_ioctl || (pslot->verify_ioctl_start && pslot->verify_ioctl_finish)) { char *log_text = "Reader supports pinpad PIN verification"; if (priv->gpriv->enable_pinpad) { sc_debug(reader->ctx, log_text); slot->capabilities |= SC_SLOT_CAP_PIN_PAD; } else { sc_debug(reader->ctx, "%s %s", log_text, log_disabled); } } if (pslot->modify_ioctl || (pslot->modify_ioctl_start && pslot->modify_ioctl_finish)) { char *log_text = "Reader supports pinpad PIN modification"; if (priv->gpriv->enable_pinpad) { sc_debug(reader->ctx, log_text); slot->capabilities |= SC_SLOT_CAP_PIN_PAD; } else { sc_debug(reader->ctx, "%s %s", log_text, log_disabled); } } if (display_ioctl) { rcount = sizeof(rbuf); r = pcsc_internal_transmit(reader, slot, NULL, 0, rbuf, &rcount, display_ioctl); if (r == SC_SUCCESS) { if (rcount != sizeof(PIN_PROPERTIES_STRUCTURE)) { PIN_PROPERTIES_STRUCTURE *caps = (PIN_PROPERTIES_STRUCTURE *)rbuf; if (caps->wLcdLayout > 0) { sc_debug(reader->ctx, "Reader has a display: %04X", caps->wLcdLayout); slot->capabilities |= SC_SLOT_CAP_DISPLAY; } else sc_debug(reader->ctx, "Reader does not have a display."); } else { sc_debug(reader->ctx, "Returned PIN properties structure has bad length (%d)", rcount); } } } } } } return SC_SUCCESS; }
/* Wait for an event to occur. * This function ignores the list of slots, because with * pcsc we have a 1:1 mapping of readers and slots anyway */ static int pcsc_wait_for_event(sc_reader_t **readers, sc_slot_info_t **slots, size_t nslots, unsigned int event_mask, int *reader, unsigned int *event, int timeout) { struct pcsc_private_data *priv = GET_PRIV_DATA(readers[0]); sc_context_t *ctx; SCARDCONTEXT pcsc_ctx; LONG ret; SCARD_READERSTATE_A rgReaderStates[SC_MAX_READERS]; unsigned long on_bits, off_bits; time_t end_time, now, delta; size_t i; /* Prevent buffer overflow */ if (nslots >= SC_MAX_READERS) return SC_ERROR_INVALID_ARGUMENTS; on_bits = off_bits = 0; if (event_mask & SC_EVENT_CARD_INSERTED) { event_mask &= ~SC_EVENT_CARD_INSERTED; on_bits |= SCARD_STATE_PRESENT; } if (event_mask & SC_EVENT_CARD_REMOVED) { event_mask &= ~SC_EVENT_CARD_REMOVED; off_bits |= SCARD_STATE_PRESENT; } if (event_mask != 0) return SC_ERROR_INVALID_ARGUMENTS; /* Find out the current status */ ctx = readers[0]->ctx; pcsc_ctx = priv->gpriv->pcsc_ctx; for (i = 0; i < nslots; i++) { struct pcsc_private_data *priv2 = GET_PRIV_DATA(readers[i]); rgReaderStates[i].szReader = priv2->reader_name; rgReaderStates[i].dwCurrentState = SCARD_STATE_UNAWARE; rgReaderStates[i].dwEventState = SCARD_STATE_UNAWARE; /* Can we handle readers from different PCSC contexts? */ if (priv2->gpriv->pcsc_ctx != pcsc_ctx) return SC_ERROR_INVALID_ARGUMENTS; } ret = priv->gpriv->SCardGetStatusChange(pcsc_ctx, 0, rgReaderStates, nslots); if (ret != SCARD_S_SUCCESS) { PCSC_ERROR(ctx, "SCardGetStatusChange(1) failed", ret); return pcsc_ret_to_error(ret); } time(&now); end_time = now + (timeout + 999) / 1000; /* Wait for a status change and return if it's a card insert/removal */ for( ; ; ) { SCARD_READERSTATE_A *rsp; /* Scan the current state of all readers to see if they * match any of the events we're polling for */ *event = 0; for (i = 0, rsp = rgReaderStates; i < nslots; i++, rsp++) { unsigned long state, prev_state; prev_state = rsp->dwCurrentState; state = rsp->dwEventState; if ((state & on_bits & SCARD_STATE_PRESENT) && (prev_state & SCARD_STATE_EMPTY)) *event |= SC_EVENT_CARD_INSERTED; if ((~state & off_bits & SCARD_STATE_PRESENT) && (prev_state & SCARD_STATE_PRESENT)) *event |= SC_EVENT_CARD_REMOVED; if (*event) { *reader = i; return SC_SUCCESS; } /* No match - copy the state so pcscd knows * what to watch out for */ rsp->dwCurrentState = rsp->dwEventState; } /* Set the timeout if caller wants to time out */ if (timeout == 0) return SC_ERROR_EVENT_TIMEOUT; if (timeout > 0) { time(&now); if (now >= end_time) return SC_ERROR_EVENT_TIMEOUT; delta = end_time - now; } else { delta = 3600; } ret = priv->gpriv->SCardGetStatusChange(pcsc_ctx, 1000 * delta, rgReaderStates, nslots); if (ret == (LONG) SCARD_E_TIMEOUT) { if (timeout < 0) continue; return SC_ERROR_EVENT_TIMEOUT; } if (ret != SCARD_S_SUCCESS) { PCSC_ERROR(ctx, "SCardGetStatusChange(2) failed", ret); return pcsc_ret_to_error(ret); } } }
static int refresh_slot_attributes(sc_reader_t *reader, sc_slot_info_t *slot) { struct pcsc_private_data *priv = GET_PRIV_DATA(reader); struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot); LONG ret; SC_FUNC_CALLED(reader->ctx, 3); if (pslot->reader_state.szReader == NULL) { pslot->reader_state.szReader = priv->reader_name; pslot->reader_state.dwCurrentState = SCARD_STATE_UNAWARE; pslot->reader_state.dwEventState = SCARD_STATE_UNAWARE; } else { pslot->reader_state.dwCurrentState = pslot->reader_state.dwEventState; } ret = priv->gpriv->SCardGetStatusChange(priv->gpriv->pcsc_ctx, 0, &pslot->reader_state, 1); if (ret == (LONG)SCARD_E_TIMEOUT) { /* timeout: nothing changed */ slot->flags &= ~SCARD_STATE_CHANGED; return 0; } if (ret != SCARD_S_SUCCESS) { PCSC_ERROR(reader->ctx, "SCardGetStatusChange failed", ret); return pcsc_ret_to_error(ret); } if (pslot->reader_state.dwEventState & SCARD_STATE_PRESENT) { int old_flags = slot->flags; int maybe_changed = 0; slot->flags |= SC_SLOT_CARD_PRESENT; slot->atr_len = pslot->reader_state.cbAtr; if (slot->atr_len > SC_MAX_ATR_SIZE) slot->atr_len = SC_MAX_ATR_SIZE; memcpy(slot->atr, pslot->reader_state.rgbAtr, slot->atr_len); #ifndef _WIN32 /* On Linux, SCARD_STATE_CHANGED always means there was an * insert or removal. But we may miss events that way. */ if (pslot->reader_state.dwEventState & SCARD_STATE_CHANGED) { slot->flags |= SC_SLOT_CARD_CHANGED; } else { maybe_changed = 1; } #else /* On windows, SCARD_STATE_CHANGED is turned on by lots of * other events, so it gives us a lot of false positives. * But if it's off, there really was no change */ if (pslot->reader_state.dwEventState & SCARD_STATE_CHANGED) { maybe_changed = 1; } #endif /* If we aren't sure if the card state changed, check if * the card handle is still valid. If the card changed, * the handle will be invalid. */ slot->flags &= ~SC_SLOT_CARD_CHANGED; if (maybe_changed) { if (old_flags & SC_SLOT_CARD_PRESENT) { DWORD readers_len = 0, state, prot, atr_len = SC_MAX_ATR_SIZE; unsigned char atr[SC_MAX_ATR_SIZE]; LONG rv = priv->gpriv->SCardStatus(pslot->pcsc_card, NULL, &readers_len, &state, &prot, atr, &atr_len); if (rv == (LONG)SCARD_W_REMOVED_CARD) slot->flags |= SC_SLOT_CARD_CHANGED; } else slot->flags |= SC_SLOT_CARD_CHANGED; } } else { slot->flags &= ~(SC_SLOT_CARD_PRESENT|SC_SLOT_CARD_CHANGED); } return 0; }
/* Do the PIN command */ static int part10_pin_cmd(sc_reader_t *reader, sc_slot_info_t *slot, struct sc_pin_cmd_data *data) { struct pcsc_private_data *priv = GET_PRIV_DATA(reader); u8 rbuf[SC_MAX_APDU_BUFFER_SIZE], sbuf[SC_MAX_APDU_BUFFER_SIZE]; char dbuf[SC_MAX_APDU_BUFFER_SIZE * 3]; size_t rcount = sizeof(rbuf), scount = 0; int r; DWORD ioctl = 0; sc_apdu_t *apdu; struct pcsc_slot_data *pslot = (struct pcsc_slot_data *) slot->drv_data; SC_FUNC_CALLED(reader->ctx, 3); assert(pslot != NULL); if (priv->gpriv->SCardControl == NULL) return SC_ERROR_NOT_SUPPORTED; /* The APDU must be provided by the card driver */ if (!data->apdu) { sc_error(reader->ctx, "No APDU provided for PC/SC v2 pinpad verification!"); return SC_ERROR_NOT_SUPPORTED; } apdu = data->apdu; switch (data->cmd) { case SC_PIN_CMD_VERIFY: if (!(pslot->verify_ioctl || (pslot->verify_ioctl_start && pslot->verify_ioctl_finish))) { sc_error(reader->ctx, "Pinpad reader does not support verification!"); return SC_ERROR_NOT_SUPPORTED; } r = part10_build_verify_pin_block(sbuf, &scount, slot, data); ioctl = pslot->verify_ioctl ? pslot->verify_ioctl : pslot->verify_ioctl_start; break; case SC_PIN_CMD_CHANGE: case SC_PIN_CMD_UNBLOCK: if (!(pslot->modify_ioctl || (pslot->modify_ioctl_start && pslot->modify_ioctl_finish))) { sc_error(reader->ctx, "Pinpad reader does not support modification!"); return SC_ERROR_NOT_SUPPORTED; } r = part10_build_modify_pin_block(sbuf, &scount, slot, data); ioctl = pslot->modify_ioctl ? pslot->modify_ioctl : pslot->modify_ioctl_start; break; default: sc_error(reader->ctx, "Unknown PIN command %d", data->cmd); return SC_ERROR_NOT_SUPPORTED; } /* If PIN block building failed, we fail too */ SC_TEST_RET(reader->ctx, r, "PC/SC v2 pinpad block building failed!"); /* If not, debug it, just for fun */ sc_bin_to_hex(sbuf, scount, dbuf, sizeof(dbuf), ':'); sc_debug(reader->ctx, "PC/SC v2 pinpad block: %s", dbuf); r = pcsc_internal_transmit(reader, slot, sbuf, scount, rbuf, &rcount, ioctl); SC_TEST_RET(reader->ctx, r, "PC/SC v2 pinpad: block transmit failed!"); /* finish the call if it was a two-phase operation */ if ((ioctl == pslot->verify_ioctl_start) || (ioctl == pslot->modify_ioctl_start)) { if (rcount != 0) { SC_FUNC_RETURN(reader->ctx, 2, SC_ERROR_UNKNOWN_DATA_RECEIVED); } ioctl = (ioctl == pslot->verify_ioctl_start) ? pslot->verify_ioctl_finish : pslot->modify_ioctl_finish; rcount = sizeof(rbuf); r = pcsc_internal_transmit(reader, slot, sbuf, 0, rbuf, &rcount, ioctl); SC_TEST_RET(reader->ctx, r, "PC/SC v2 pinpad: finish operation failed!"); } /* We expect only two bytes of result data (SW1 and SW2) */ if (rcount != 2) { SC_FUNC_RETURN(reader->ctx, 2, SC_ERROR_UNKNOWN_DATA_RECEIVED); } /* Extract the SWs for the result APDU */ apdu->sw1 = (unsigned int) rbuf[rcount - 2]; apdu->sw2 = (unsigned int) rbuf[rcount - 1]; r = SC_SUCCESS; switch (((unsigned int) apdu->sw1 << 8) | apdu->sw2) { case 0x6400: /* Input timed out */ r = SC_ERROR_KEYPAD_TIMEOUT; break; case 0x6401: /* Input cancelled */ r = SC_ERROR_KEYPAD_CANCELLED; break; case 0x6402: /* PINs don't match */ r = SC_ERROR_KEYPAD_PIN_MISMATCH; break; case 0x6B80: /* Wrong data in the buffer, rejected by firmware */ r = SC_ERROR_READER; break; } SC_TEST_RET(reader->ctx, r, "PIN command failed"); /* PIN command completed, all is good */ return SC_SUCCESS; }