/* * reader-pcsc.c: Reader driver for PC/SC interface * * Copyright (C) 2002 Juha Yrjölä * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "internal.h" #ifdef ENABLE_PCSC #include "ctbcs.h" #include #include #include #include #include #ifdef _WIN32 #include #else #include #endif #include "internal-winscard.h" /* Default timeout value for SCardGetStatusChange * Needs to be increased for some broken PC/SC * Lite implementations. */ #ifndef SC_CUSTOM_STATUS_TIMEOUT #define SC_STATUS_TIMEOUT 0 #else #define SC_STATUS_TIMEOUT SC_CUSTOM_STATUS_TIMEOUT #endif /* Some windows specific kludge */ #undef SCARD_PROTOCOL_ANY #define SCARD_PROTOCOL_ANY (SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1) /* Error printing */ #define PCSC_ERROR(ctx, desc, rv) sc_error(ctx, desc ": %lx\n", rv); /* Utility for handling big endian IOCTL codes. */ #define dw2i_be(a, x) ((((((a[x] << 8) + a[x+1]) << 8) + a[x+2]) << 8) + a[x+3]) #define GET_SLOT_PTR(s, i) (&(s)->slot[(i)]) #define GET_PRIV_DATA(r) ((struct pcsc_private_data *) (r)->drv_data) #define GET_SLOT_DATA(r) ((struct pcsc_slot_data *) (r)->drv_data) static int part10_pin_cmd(sc_reader_t *reader, sc_slot_info_t *slot, struct sc_pin_cmd_data *data); struct pcsc_global_private_data { SCARDCONTEXT pcsc_ctx; int enable_pinpad; int connect_exclusive; int connect_reset; int transaction_reset; const char *library_name; lt_dlhandle dlhandle; SCardEstablishContext_t SCardEstablishContext; SCardReleaseContext_t SCardReleaseContext; SCardConnect_t SCardConnect; SCardReconnect_t SCardReconnect; SCardDisconnect_t SCardDisconnect; SCardBeginTransaction_t SCardBeginTransaction; SCardEndTransaction_t SCardEndTransaction; SCardStatus_t SCardStatus; SCardGetStatusChange_t SCardGetStatusChange; SCardControlOLD_t SCardControlOLD; SCardControl_t SCardControl; SCardTransmit_t SCardTransmit; SCardListReaders_t SCardListReaders; }; struct pcsc_private_data { char *reader_name; struct pcsc_global_private_data *gpriv; }; struct pcsc_slot_data { SCARDHANDLE pcsc_card; SCARD_READERSTATE_A reader_state; DWORD verify_ioctl; DWORD verify_ioctl_start; DWORD verify_ioctl_finish; DWORD modify_ioctl; DWORD modify_ioctl_start; DWORD modify_ioctl_finish; int locked; }; static int pcsc_detect_card_presence(sc_reader_t *reader, sc_slot_info_t *slot); static int pcsc_ret_to_error(long rv) { switch (rv) { case SCARD_W_REMOVED_CARD: return SC_ERROR_CARD_REMOVED; case SCARD_E_NOT_TRANSACTED: return SC_ERROR_TRANSMIT_FAILED; case SCARD_W_UNRESPONSIVE_CARD: return SC_ERROR_CARD_UNRESPONSIVE; case SCARD_W_UNPOWERED_CARD: return SC_ERROR_CARD_UNRESPONSIVE; case SCARD_E_SHARING_VIOLATION: return SC_ERROR_READER; #ifdef SCARD_E_NO_READERS_AVAILABLE /* Older pcsc-lite does not have it */ case SCARD_E_NO_READERS_AVAILABLE: /* Ideelabor #11 */ /* Or SC_ERROR_NO_READERS_FOUND * or SC_ERROR_READER ? * As it currently happens on runtime after a reader has been found once already * the more suitable error seems to be detached error */ return SC_ERROR_READER_DETACHED; #endif default: return SC_ERROR_UNKNOWN; } } static unsigned int pcsc_proto_to_opensc(DWORD proto) { switch (proto) { case SCARD_PROTOCOL_T0: return SC_PROTO_T0; case SCARD_PROTOCOL_T1: return SC_PROTO_T1; case SCARD_PROTOCOL_RAW: return SC_PROTO_RAW; default: return 0; } } static DWORD opensc_proto_to_pcsc(unsigned int proto) { switch (proto) { case SC_PROTO_T0: return SCARD_PROTOCOL_T0; case SC_PROTO_T1: return SCARD_PROTOCOL_T1; case SC_PROTO_RAW: return SCARD_PROTOCOL_RAW; default: 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 (dwRecvLength > 258) dwRecvLength = 258; 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 0; } static int pcsc_transmit(sc_reader_t *reader, sc_slot_info_t *slot, sc_apdu_t *apdu) { size_t ssize, rsize, rbuflen = 0; u8 *sbuf = NULL, *rbuf = NULL; int r; /* we always use a at least 258 byte size big return buffer * to mimic the behaviour of the old implementation (some readers * seems to require a larger than necessary return buffer). * The buffer for the returned data needs to be at least 2 bytes * larger than the expected data length to store SW1 and SW2. */ rsize = rbuflen = apdu->resplen <= 256 ? 258 : apdu->resplen + 2; rbuf = malloc(rbuflen); if (rbuf == NULL) { r = SC_ERROR_MEMORY_FAILURE; goto out; } /* encode and log the APDU */ r = sc_apdu_get_octets(reader->ctx, apdu, &sbuf, &ssize, slot->active_protocol); if (r != SC_SUCCESS) goto out; if (reader->ctx->debug >= 6) sc_apdu_log(reader->ctx, sbuf, ssize, 1); r = pcsc_internal_transmit(reader, slot, sbuf, ssize, rbuf, &rsize, apdu->control); if (r < 0) { /* unable to transmit ... most likely a reader problem */ sc_error(reader->ctx, "unable to transmit"); goto out; } if (reader->ctx->debug >= 6) sc_apdu_log(reader->ctx, rbuf, rsize, 0); /* set response */ r = sc_apdu_set_resp(reader->ctx, apdu, rbuf, rsize); out: if (sbuf != NULL) { sc_mem_clear(sbuf, ssize); free(sbuf); } if (rbuf != NULL) { sc_mem_clear(rbuf, rbuflen); free(rbuf); } return r; } 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, SC_STATUS_TIMEOUT, &pslot->reader_state, 1); if (ret == (LONG)SCARD_E_TIMEOUT) { /* timeout: nothing changed */ slot->flags &= ~SCARD_STATE_CHANGED; return 0; } if (ret != 0) { 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 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 = 32; unsigned char atr[32]; 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; } static int pcsc_detect_card_presence(sc_reader_t *reader, sc_slot_info_t *slot) { int rv; if ((rv = refresh_slot_attributes(reader, slot)) < 0) return rv; return slot->flags; } /* 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 *priv = GET_PRIV_DATA(readers[i]); rgReaderStates[i].szReader = priv->reader_name; rgReaderStates[i].dwCurrentState = SCARD_STATE_UNAWARE; rgReaderStates[i].dwEventState = SCARD_STATE_UNAWARE; /* Can we handle readers from different PCSC contexts? */ if (priv->gpriv->pcsc_ctx != pcsc_ctx) return SC_ERROR_INVALID_ARGUMENTS; } ret = priv->gpriv->SCardGetStatusChange(pcsc_ctx, 0, rgReaderStates, nslots); if (ret != 0) { 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 0; } /* 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 != 0) { PCSC_ERROR(ctx, "SCardGetStatusChange(2) failed", ret); return pcsc_ret_to_error(ret); } } } 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 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]; DWORD i, feature_len; 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 != 0) { 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) { if (!(feature_len % sizeof(PCSC_TLV_STRUCTURE))) { 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 { sc_debug(reader->ctx, "Reader pinpad feature: %02x 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); } } } else sc_debug(reader->ctx, "Inconsistent TLV from reader!"); } else { sc_debug(reader->ctx, "SCardControl failed %d", rv); } } return SC_SUCCESS; } 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->transaction_reset ? SCARD_RESET_CARD : SCARD_LEAVE_CARD); memset(pslot, 0, sizeof(*pslot)); slot->flags = 0; return SC_SUCCESS; } 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); if ((unsigned int)rv == 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); } /* Now try to begin a new transaction after we reconnected and we fail if some other program was faster to lock the reader */ rv = priv->gpriv->SCardBeginTransaction(pslot->pcsc_card); } if (rv != SCARD_S_SUCCESS) { PCSC_ERROR(reader->ctx, "SCardBeginTransaction failed", rv); return pcsc_ret_to_error(rv); } pslot->locked = 1; 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_release(sc_reader_t *reader) { int i; struct pcsc_private_data *priv = GET_PRIV_DATA(reader); free(priv->reader_name); free(priv); for (i = 0; i < reader->slot_count; i++) { if (reader->slot[i].drv_data != NULL) { free(reader->slot[i].drv_data); reader->slot[i].drv_data = NULL; } } return SC_SUCCESS; } static int pcsc_reset(sc_reader_t *reader, sc_slot_info_t *slot) { int r; struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot); int old_locked = pslot->locked; r = pcsc_reconnect(reader, slot, 1); if(r != SCARD_S_SUCCESS) return pcsc_ret_to_error(r); /* pcsc_reconnect unlocks card... try to lock it again if it was locked */ if(old_locked) r = pcsc_lock(reader, slot); return r; } static struct sc_reader_operations pcsc_ops; static struct sc_reader_driver pcsc_drv = { "PC/SC reader", "pcsc", &pcsc_ops, 0, 0, NULL }; static int pcsc_init(sc_context_t *ctx, void **reader_data) { LONG rv; DWORD reader_buf_size; char *reader_buf = NULL, *p; const char *mszGroups = NULL; int r; struct pcsc_global_private_data *gpriv; scconf_block *conf_block = NULL; int ret = SC_ERROR_INTERNAL; *reader_data = NULL; gpriv = (struct pcsc_global_private_data *) calloc(1, sizeof(struct pcsc_global_private_data)); if (gpriv == NULL) { ret = SC_ERROR_OUT_OF_MEMORY; goto out; } /* Defaults */ gpriv->connect_reset = 1; gpriv->connect_exclusive = 0; gpriv->transaction_reset = 0; gpriv->enable_pinpad = 0; gpriv->library_name = PCSC_DEFAULT_LIBRARY_NAME; conf_block = sc_get_conf_block(ctx, "reader_driver", "pcsc", 1); if (conf_block) { gpriv->connect_reset = scconf_get_bool(conf_block, "connect_reset", gpriv->connect_reset); gpriv->connect_exclusive = scconf_get_bool(conf_block, "connect_exclusive", gpriv->connect_exclusive); gpriv->transaction_reset = scconf_get_bool(conf_block, "transaction_reset", gpriv->transaction_reset); gpriv->enable_pinpad = scconf_get_bool(conf_block, "enable_pinpad", gpriv->enable_pinpad); gpriv->library_name = scconf_get_str(conf_block, "library_name", gpriv->library_name); } gpriv->dlhandle = lt_dlopen(gpriv->library_name); if (gpriv->dlhandle == NULL) { ret = SC_ERROR_CANNOT_LOAD_MODULE; goto out; } gpriv->SCardEstablishContext = (SCardEstablishContext_t)lt_dlsym(gpriv->dlhandle, "SCardEstablishContext"); gpriv->SCardReleaseContext = (SCardReleaseContext_t)lt_dlsym(gpriv->dlhandle, "SCardReleaseContext"); gpriv->SCardConnect = (SCardConnect_t)lt_dlsym(gpriv->dlhandle, "SCardConnect"); gpriv->SCardReconnect = (SCardReconnect_t)lt_dlsym(gpriv->dlhandle, "SCardReconnect"); gpriv->SCardDisconnect = (SCardDisconnect_t)lt_dlsym(gpriv->dlhandle, "SCardDisconnect"); gpriv->SCardBeginTransaction = (SCardBeginTransaction_t)lt_dlsym(gpriv->dlhandle, "SCardBeginTransaction"); gpriv->SCardEndTransaction = (SCardEndTransaction_t)lt_dlsym(gpriv->dlhandle, "SCardEndTransaction"); gpriv->SCardStatus = (SCardStatus_t)lt_dlsym(gpriv->dlhandle, "SCardStatus"); gpriv->SCardGetStatusChange = (SCardGetStatusChange_t)lt_dlsym(gpriv->dlhandle, "SCardGetStatusChange"); gpriv->SCardTransmit = (SCardTransmit_t)lt_dlsym(gpriv->dlhandle, "SCardTransmit"); gpriv->SCardListReaders = (SCardListReaders_t)lt_dlsym(gpriv->dlhandle, "SCardListReaders"); if (gpriv->SCardConnect == NULL) gpriv->SCardConnect = (SCardConnect_t)lt_dlsym(gpriv->dlhandle, "SCardConnectA"); if (gpriv->SCardStatus == NULL) gpriv->SCardStatus = (SCardStatus_t)lt_dlsym(gpriv->dlhandle, "SCardStatusA"); if (gpriv->SCardGetStatusChange == NULL) gpriv->SCardGetStatusChange = (SCardGetStatusChange_t)lt_dlsym(gpriv->dlhandle, "SCardGetStatusChangeA"); if (gpriv->SCardListReaders == NULL) gpriv->SCardListReaders = (SCardListReaders_t)lt_dlsym(gpriv->dlhandle, "SCardListReadersA"); /* If we have SCardGetAttrib it is correct API */ if (lt_dlsym(gpriv->dlhandle, "SCardGetAttrib") != NULL) gpriv->SCardControl = (SCardControl_t)lt_dlsym(gpriv->dlhandle, "SCardControl"); else gpriv->SCardControlOLD = (SCardControlOLD_t)lt_dlsym(gpriv->dlhandle, "SCardControl"); if ( gpriv->SCardReleaseContext == NULL || gpriv->SCardConnect == NULL || gpriv->SCardReconnect == NULL || gpriv->SCardDisconnect == NULL || gpriv->SCardBeginTransaction == NULL || gpriv->SCardEndTransaction == NULL || gpriv->SCardStatus == NULL || gpriv->SCardGetStatusChange == NULL || (gpriv->SCardControl == NULL && gpriv->SCardControlOLD == NULL) || gpriv->SCardTransmit == NULL || gpriv->SCardListReaders == NULL ) { ret = SC_ERROR_CANNOT_LOAD_MODULE; goto out; } rv = gpriv->SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &gpriv->pcsc_ctx); if (rv != SCARD_S_SUCCESS) { ret = pcsc_ret_to_error(rv); goto out; } rv = gpriv->SCardListReaders(gpriv->pcsc_ctx, NULL, NULL, (LPDWORD) &reader_buf_size); if (rv != SCARD_S_SUCCESS || reader_buf_size < 2) { ret = pcsc_ret_to_error(rv); /* No readers configured */ goto out; } reader_buf = (char *) malloc(sizeof(char) * reader_buf_size); if (!reader_buf) { ret = SC_ERROR_OUT_OF_MEMORY; goto out; } rv = gpriv->SCardListReaders(gpriv->pcsc_ctx, mszGroups, reader_buf, (LPDWORD) &reader_buf_size); if (rv != SCARD_S_SUCCESS) { ret = pcsc_ret_to_error(rv); goto out; } p = reader_buf; do { sc_reader_t *reader = (sc_reader_t *) calloc(1, sizeof(sc_reader_t)); struct pcsc_private_data *priv = (struct pcsc_private_data *) malloc(sizeof(struct pcsc_private_data)); struct pcsc_slot_data *pslot = (struct pcsc_slot_data *) malloc(sizeof(struct pcsc_slot_data)); sc_slot_info_t *slot; if (reader == NULL || priv == NULL || pslot == NULL) { if (reader) free(reader); if (priv) free(priv); if (pslot) free(pslot); break; } reader->drv_data = priv; reader->ops = &pcsc_ops; reader->driver = &pcsc_drv; reader->slot_count = 1; reader->name = strdup(p); priv->gpriv = gpriv; priv->reader_name = strdup(p); r = _sc_add_reader(ctx, reader); if (r) { free(priv->reader_name); free(priv); free(reader->name); free(reader); free(pslot); break; } slot = &reader->slot[0]; memset(slot, 0, sizeof(*slot)); slot->drv_data = pslot; memset(pslot, 0, sizeof(*pslot)); refresh_slot_attributes(reader, slot); while (*p++ != 0); } while (p < (reader_buf + reader_buf_size - 1)); *reader_data = gpriv; gpriv = NULL; ret = SC_SUCCESS; out: if (ret != SC_SUCCESS) { if (gpriv->pcsc_ctx != 0) gpriv->SCardReleaseContext(gpriv->pcsc_ctx); if (reader_buf != NULL) free(reader_buf); if (gpriv->dlhandle != NULL) lt_dlclose(gpriv->dlhandle); if (gpriv != NULL) free(gpriv); } return 0; } static int pcsc_finish(sc_context_t *ctx, void *prv_data) { struct pcsc_global_private_data *priv = (struct pcsc_global_private_data *) prv_data; if (priv) { priv->SCardReleaseContext(priv->pcsc_ctx); lt_dlclose(priv->dlhandle); free(priv); } return 0; } static int pcsc_pin_cmd(sc_reader_t *reader, sc_slot_info_t * slot, struct sc_pin_cmd_data *data) { /* XXX: temporary */ if (slot->capabilities & SC_SLOT_CAP_PIN_PAD) { return part10_pin_cmd(reader, slot, data); } else { return ctbcs_pin_cmd(reader, slot, data); } } struct sc_reader_driver * sc_get_pcsc_driver(void) { pcsc_ops.init = pcsc_init; pcsc_ops.finish = pcsc_finish; pcsc_ops.transmit = pcsc_transmit; pcsc_ops.detect_card_presence = pcsc_detect_card_presence; pcsc_ops.lock = pcsc_lock; pcsc_ops.unlock = pcsc_unlock; pcsc_ops.release = pcsc_release; pcsc_ops.connect = pcsc_connect; pcsc_ops.disconnect = pcsc_disconnect; pcsc_ops.perform_verify = pcsc_pin_cmd; pcsc_ops.wait_for_event = pcsc_wait_for_event; pcsc_ops.reset = pcsc_reset; return &pcsc_drv; } /* * Pinpad support, based on PC/SC v2 Part 10 interface * Similar to CCID in spirit. */ /* Local definitions */ #define SC_CCID_PIN_TIMEOUT 30 /* CCID definitions */ #define SC_CCID_PIN_ENCODING_BIN 0x00 #define SC_CCID_PIN_ENCODING_BCD 0x01 #define SC_CCID_PIN_ENCODING_ASCII 0x02 #define SC_CCID_PIN_UNITS_BYTES 0x80 /* Build a pin verification block + APDU */ static int part10_build_verify_pin_block(u8 * buf, size_t * size, struct sc_pin_cmd_data *data) { int offset = 0, count = 0; sc_apdu_t *apdu = data->apdu; u8 tmp; unsigned int tmp16; PIN_VERIFY_STRUCTURE *pin_verify = (PIN_VERIFY_STRUCTURE *)buf; /* PIN verification control message */ pin_verify->bTimerOut = SC_CCID_PIN_TIMEOUT; pin_verify->bTimerOut2 = SC_CCID_PIN_TIMEOUT; /* bmFormatString */ tmp = 0x00; if (data->pin1.encoding == SC_PIN_ENCODING_ASCII) { tmp |= SC_CCID_PIN_ENCODING_ASCII; /* if the effective pin length offset is specified, use it */ if (data->pin1.length_offset > 4) { tmp |= SC_CCID_PIN_UNITS_BYTES; tmp |= (data->pin1.length_offset - 5) << 3; } } else if (data->pin1.encoding == SC_PIN_ENCODING_BCD) { tmp |= SC_CCID_PIN_ENCODING_BCD; tmp |= SC_CCID_PIN_UNITS_BYTES; } else if (data->pin1.encoding == SC_PIN_ENCODING_GLP) { /* see comment about GLP pins in sec.c */ tmp |= SC_CCID_PIN_ENCODING_BCD; tmp |= 0x04 << 3; } else return SC_ERROR_NOT_SUPPORTED; pin_verify->bmFormatString = tmp; /* bmPINBlockString */ tmp = 0x00; if (data->pin1.encoding == SC_PIN_ENCODING_GLP) { /* GLP pin length is encoded in 4 bits and block size is always 8 bytes */ tmp |= 0x40 | 0x08; } else if (data->pin1.encoding == SC_PIN_ENCODING_ASCII && data->pin1.pad_length) { tmp |= data->pin1.pad_length; } pin_verify->bmPINBlockString = tmp; /* bmPINLengthFormat */ tmp = 0x00; if (data->pin1.encoding == SC_PIN_ENCODING_GLP) { /* GLP pins expect the effective pin length from bit 4 */ tmp |= 0x04; } pin_verify->bmPINLengthFormat = tmp; /* bmPINLengthFormat */ if (!data->pin1.min_length || !data->pin1.max_length) return SC_ERROR_INVALID_ARGUMENTS; tmp16 = (data->pin1.min_length << 8 ) + data->pin1.max_length; pin_verify->wPINMaxExtraDigit = HOST_TO_CCID_16(tmp16); /* Min Max */ pin_verify->bEntryValidationCondition = 0x02; /* Keypress only */ /* Ignore language and T=1 parameters. */ pin_verify->bNumberMessage = 0x00; pin_verify->wLangId = HOST_TO_CCID_16(0x0000); pin_verify->bMsgIndex = 0x00; pin_verify->bTeoPrologue[0] = 0x00; pin_verify->bTeoPrologue[1] = 0x00; pin_verify->bTeoPrologue[2] = 0x00; /* APDU itself */ pin_verify->abData[offset++] = apdu->cla; pin_verify->abData[offset++] = apdu->ins; pin_verify->abData[offset++] = apdu->p1; pin_verify->abData[offset++] = apdu->p2; /* Copy data if not Case 1 */ if (data->pin1.length_offset != 4) { pin_verify->abData[offset++] = apdu->lc; memcpy(&pin_verify->abData[offset], apdu->data, apdu->datalen); offset += apdu->datalen; } pin_verify->ulDataLength = HOST_TO_CCID_32(offset); /* APDU size */ count = sizeof(PIN_VERIFY_STRUCTURE) + offset -1; *size = count; return SC_SUCCESS; } /* Build a pin modification block + APDU */ static int part10_build_modify_pin_block(u8 * buf, size_t * size, struct sc_pin_cmd_data *data) { int offset = 0, count = 0; sc_apdu_t *apdu = data->apdu; u8 tmp; unsigned int tmp16; PIN_MODIFY_STRUCTURE *pin_modify = (PIN_MODIFY_STRUCTURE *)buf; /* PIN verification control message */ pin_modify->bTimerOut = SC_CCID_PIN_TIMEOUT; /* bTimeOut */ pin_modify->bTimerOut2 = SC_CCID_PIN_TIMEOUT; /* bTimeOut2 */ /* bmFormatString */ tmp = 0x00; if (data->pin1.encoding == SC_PIN_ENCODING_ASCII) { tmp |= SC_CCID_PIN_ENCODING_ASCII; /* if the effective pin length offset is specified, use it */ if (data->pin1.length_offset > 4) { tmp |= SC_CCID_PIN_UNITS_BYTES; tmp |= (data->pin1.length_offset - 5) << 3; } } else if (data->pin1.encoding == SC_PIN_ENCODING_BCD) { tmp |= SC_CCID_PIN_ENCODING_BCD; tmp |= SC_CCID_PIN_UNITS_BYTES; } else if (data->pin1.encoding == SC_PIN_ENCODING_GLP) { /* see comment about GLP pins in sec.c */ tmp |= SC_CCID_PIN_ENCODING_BCD; tmp |= 0x04 << 3; } else return SC_ERROR_NOT_SUPPORTED; pin_modify->bmFormatString = tmp; /* bmFormatString */ /* bmPINBlockString */ tmp = 0x00; if (data->pin1.encoding == SC_PIN_ENCODING_GLP) { /* GLP pin length is encoded in 4 bits and block size is always 8 bytes */ tmp |= 0x40 | 0x08; } else if (data->pin1.encoding == SC_PIN_ENCODING_ASCII && data->pin1.pad_length) { tmp |= data->pin1.pad_length; } pin_modify->bmPINBlockString = tmp; /* bmPINBlockString */ /* bmPINLengthFormat */ tmp = 0x00; if (data->pin1.encoding == SC_PIN_ENCODING_GLP) { /* GLP pins expect the effective pin length from bit 4 */ tmp |= 0x04; } pin_modify->bmPINLengthFormat = tmp; /* bmPINLengthFormat */ pin_modify->bInsertionOffsetOld = 0x00; /* bOffsetOld */ pin_modify->bInsertionOffsetNew = 0x00; /* bOffsetNew */ if (!data->pin1.min_length || !data->pin1.max_length) return SC_ERROR_INVALID_ARGUMENTS; tmp16 = (data->pin1.min_length << 8 ) + data->pin1.max_length; pin_modify->wPINMaxExtraDigit = HOST_TO_CCID_16(tmp16); /* Min Max */ pin_modify->bConfirmPIN = 0x03; /* bConfirmPIN, all */ pin_modify->bEntryValidationCondition = 0x02; /* bEntryValidationCondition, keypress only */ /* Ignore language and T=1 parameters. */ pin_modify->bNumberMessage = 0x00; pin_modify->wLangId = HOST_TO_CCID_16(0x0000); pin_modify->bMsgIndex1 = 0x00; pin_modify->bMsgIndex2 = 0x00; pin_modify->bMsgIndex3 = 0x00; pin_modify->bTeoPrologue[0] = 0x00; pin_modify->bTeoPrologue[1] = 0x00; pin_modify->bTeoPrologue[2] = 0x00; /* APDU itself */ pin_modify->abData[offset++] = apdu->cla; pin_modify->abData[offset++] = apdu->ins; pin_modify->abData[offset++] = apdu->p1; pin_modify->abData[offset++] = apdu->p2; /* Copy data if not Case 1 */ if (data->pin1.length_offset != 4) { pin_modify->abData[offset++] = apdu->lc; memcpy(&pin_modify->abData[offset], apdu->data, apdu->datalen); offset += apdu->datalen; } pin_modify->ulDataLength = HOST_TO_CCID_32(offset); /* APDU size */ count = sizeof(PIN_MODIFY_STRUCTURE) + offset -1; *size = count; return SC_SUCCESS; } /* 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 Part 10 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, 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, 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, "Part10 PIN block building failed!"); /* If not, debug it, just for fun */ sc_bin_to_hex(sbuf, scount, dbuf, sizeof(dbuf), ':'); sc_debug(reader->ctx, "Part 10 block: %s", dbuf); r = pcsc_internal_transmit(reader, slot, sbuf, scount, rbuf, &rcount, ioctl); SC_TEST_RET(reader->ctx, r, "Part 10: 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, "Part 10: 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; } SC_TEST_RET(reader->ctx, r, "PIN command failed"); /* PIN command completed, all is good */ return SC_SUCCESS; } #endif /* HAVE_PCSC */