opensc/src/libopensc/reader-pcsc.c

1157 lines
33 KiB
C
Raw Normal View History

/*
* reader-pcsc.c: Reader driver for PC/SC interface
*
* Copyright (C) 2002 Juha Yrjölä <juha.yrjola@iki.fi>
*
* 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 HAVE_PCSC
#include "ctbcs.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef PCSC_INCLUDES_IN_PCSC
#include <PCSC/wintypes.h>
#include <PCSC/winscard.h>
#else
#include <winscard.h>
#endif
#ifdef _WIN32
#include <Winsock.h>
#include "part10.h"
#define PINPAD_ENABLED
#else
#include <arpa/inet.h>
#endif
#ifdef HAVE_READER_H
#include <reader.h>
#ifdef HOST_TO_CCID_32
#define PINPAD_ENABLED
#endif
#endif
/* 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)
#ifdef _WIN32
#define SCARD_SCOPE_GLOBAL SCARD_SCOPE_USER
/* Error printing */
#define PCSC_ERROR(ctx, desc, rv) sc_error(ctx, desc ": %lx\n", rv);
#else
#define PCSC_ERROR(ctx, desc, rv) sc_error(ctx, desc ": %s\n", pcsc_stringify_error(rv));
#endif
/* 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;
};
struct pcsc_private_data {
SCARDCONTEXT pcsc_ctx;
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_E_SHARING_VIOLATION:
return SC_ERROR_READER;
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)
{
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 = SCardTransmit(card, &sSendPci, sendbuf, dwSendLength,
&sRecvPci, recvbuf, &dwRecvLength);
} else {
#ifdef HAVE_PCSC_OLD
rv = SCardControl(card, sendbuf, dwSendLength,
recvbuf, &dwRecvLength);
#else
rv = SCardControl(card, (DWORD) control, sendbuf, dwSendLength,
recvbuf, dwRecvLength, &dwRecvLength);
#endif
}
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 = SCardGetStatusChange(priv->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 = 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)
{
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 = GET_PRIV_DATA(readers[0])->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->pcsc_ctx != pcsc_ctx)
return SC_ERROR_INVALID_ARGUMENTS;
}
ret = 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 = 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;
if (_sc_check_forced_protocol
(reader->ctx, slot->atr, slot->atr_len,
(unsigned int *)&protocol)) {
protocol = opensc_proto_to_pcsc(protocol);
} else {
protocol = slot->active_protocol;
}
/* reconnect always unlocks transaction */
pslot->locked = 0;
rv = SCardReconnect(pslot->pcsc_card,
priv->gpriv->connect_exclusive ? SCARD_SHARE_EXCLUSIVE : SCARD_SHARE_SHARED, protocol,
reset ? SCARD_RESET_CARD : SCARD_LEAVE_CARD, &active_proto);
if (rv != SCARD_S_SUCCESS) {
PCSC_ERROR(reader->ctx, "SCardReconnect failed", rv);
return pcsc_ret_to_error(rv);
}
slot->active_protocol = pcsc_proto_to_opensc(active_proto);
return SC_SUCCESS;
}
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;
#ifdef PINPAD_ENABLED
u8 feature_buf[256];
DWORD i, feature_len;
PCSC_TLV_STRUCTURE *pcsc_tlv;
#endif
r = refresh_slot_attributes(reader, slot);
if (r)
return r;
if (!(slot->flags & SC_SLOT_CARD_PRESENT))
return SC_ERROR_CARD_NOT_PRESENT;
if (_sc_check_forced_protocol(reader->ctx, slot->atr, slot->atr_len, (unsigned int *) &protocol)) {
protocol = opensc_proto_to_pcsc(protocol);
} else {
protocol = SCARD_PROTOCOL_ANY;
}
rv = SCardConnect(priv->pcsc_ctx, priv->reader_name,
priv->gpriv->connect_exclusive ? SCARD_SHARE_EXCLUSIVE : SCARD_SHARE_SHARED,
protocol, &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;
/* check for pinpad support */
#ifdef PINPAD_ENABLED
sc_debug(reader->ctx, "Requesting reader features ... ");
rv = 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);
}
#endif /* PINPAD_ENABLED */
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);
SCardDisconnect(pslot->pcsc_card, priv->gpriv->transaction_reset ?
SCARD_RESET_CARD : SCARD_LEAVE_CARD);
memset(pslot, 0, sizeof(*pslot));
slot->flags = 0;
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);
SC_FUNC_CALLED(reader->ctx, 3);
assert(pslot != NULL);
rv = 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 = 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 = 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 0;
}
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 0;
}
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 != SC_SUCCESS)
return 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, *p;
const char *mszGroups = NULL;
SCARDCONTEXT pcsc_ctx;
int r;
struct pcsc_global_private_data *gpriv;
scconf_block *conf_block;
rv = SCardEstablishContext(SCARD_SCOPE_GLOBAL,
NULL, NULL, &pcsc_ctx);
if (rv != SCARD_S_SUCCESS)
return pcsc_ret_to_error(rv);
rv = SCardListReaders(pcsc_ctx, NULL, NULL,
(LPDWORD) &reader_buf_size);
if (rv != SCARD_S_SUCCESS || reader_buf_size < 2) {
SCardReleaseContext(pcsc_ctx);
return pcsc_ret_to_error(rv); /* No readers configured */
}
gpriv = (struct pcsc_global_private_data *) malloc(sizeof(struct pcsc_global_private_data));
if (gpriv == NULL) {
SCardReleaseContext(pcsc_ctx);
return SC_ERROR_OUT_OF_MEMORY;
}
gpriv->pcsc_ctx = pcsc_ctx;
conf_block = sc_get_conf_block(ctx, "reader_driver", "pcsc", 1);
if (conf_block) {
gpriv->connect_reset =
scconf_get_bool(conf_block, "connect_reset", 1);
gpriv->connect_exclusive =
scconf_get_bool(conf_block, "connect_exclusive", 0);
gpriv->transaction_reset =
scconf_get_bool(conf_block, "transaction_reset", 0);
gpriv->enable_pinpad =
scconf_get_bool(conf_block, "enable_pinpad", 0);
}
*reader_data = gpriv;
reader_buf = (char *) malloc(sizeof(char) * reader_buf_size);
if (!reader_buf) {
free(gpriv);
*reader_data = NULL;
SCardReleaseContext(pcsc_ctx);
return SC_ERROR_OUT_OF_MEMORY;
}
rv = SCardListReaders(pcsc_ctx, mszGroups, reader_buf,
(LPDWORD) &reader_buf_size);
if (rv != SCARD_S_SUCCESS) {
free(reader_buf);
free(gpriv);
*reader_data = NULL;
SCardReleaseContext(pcsc_ctx);
return pcsc_ret_to_error(rv);
}
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->pcsc_ctx = pcsc_ctx;
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));
free(reader_buf);
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) {
SCardReleaseContext(priv->pcsc_ctx);
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.
*/
#ifdef PINPAD_ENABLED
/* 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;
}
#endif
/* Do the PIN command */
static int
part10_pin_cmd(sc_reader_t *reader, sc_slot_info_t *slot,
struct sc_pin_cmd_data *data)
{
#ifdef PINPAD_ENABLED
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);
/* 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;
#else
return SC_ERROR_NOT_SUPPORTED;
#endif /* PINPAD_ENABLED */
}
#endif /* HAVE_PCSC */