2002-02-24 19:32:14 +00:00
|
|
|
/*
|
2003-12-10 14:52:58 +00:00
|
|
|
* reader-pcsc.c: Reader driver for PC/SC interface
|
2002-02-24 19:32:14 +00:00
|
|
|
*
|
2006-12-19 21:31:17 +00:00
|
|
|
* Copyright (C) 2002 Juha Yrjölä <juha.yrjola@iki.fi>
|
2002-02-24 19:32:14 +00:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
2002-04-05 10:44:51 +00:00
|
|
|
#include "internal.h"
|
2008-03-06 16:06:59 +00:00
|
|
|
#ifdef ENABLE_PCSC
|
2002-12-23 18:47:27 +00:00
|
|
|
#include "ctbcs.h"
|
2002-02-24 19:32:14 +00:00
|
|
|
#include <assert.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2003-01-05 18:06:35 +00:00
|
|
|
#include <time.h>
|
2008-03-06 16:06:59 +00:00
|
|
|
#include <ltdl.h>
|
2002-02-24 19:32:14 +00:00
|
|
|
|
2006-08-16 16:36:00 +00:00
|
|
|
#ifdef _WIN32
|
2008-03-06 16:06:59 +00:00
|
|
|
#include <winsock2.h>
|
2006-08-16 16:36:00 +00:00
|
|
|
#else
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#endif
|
2008-03-06 16:06:59 +00:00
|
|
|
|
|
|
|
#include "internal-winscard.h"
|
2005-09-05 11:29:57 +00:00
|
|
|
|
2002-06-14 12:52:56 +00:00
|
|
|
/* Some windows specific kludge */
|
2004-08-31 17:31:00 +00:00
|
|
|
#undef SCARD_PROTOCOL_ANY
|
2003-03-20 12:52:39 +00:00
|
|
|
#define SCARD_PROTOCOL_ANY (SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1)
|
2002-06-14 12:52:56 +00:00
|
|
|
/* Error printing */
|
2008-10-20 15:04:29 +00:00
|
|
|
#define PCSC_ERROR(ctx, desc, rv) sc_error(ctx, desc ": 0x%08lx\n", rv);
|
2002-06-14 12:52:56 +00:00
|
|
|
|
2005-04-07 19:45:39 +00:00
|
|
|
/* 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])
|
|
|
|
|
2002-02-24 19:32:14 +00:00
|
|
|
#define GET_PRIV_DATA(r) ((struct pcsc_private_data *) (r)->drv_data)
|
|
|
|
#define GET_SLOT_DATA(r) ((struct pcsc_slot_data *) (r)->drv_data)
|
|
|
|
|
2005-08-13 13:26:46 +00:00
|
|
|
static int part10_pin_cmd(sc_reader_t *reader, sc_slot_info_t *slot,
|
2005-03-09 12:44:31 +00:00
|
|
|
struct sc_pin_cmd_data *data);
|
|
|
|
|
2002-02-24 19:32:14 +00:00
|
|
|
struct pcsc_global_private_data {
|
|
|
|
SCARDCONTEXT pcsc_ctx;
|
2005-08-13 13:26:46 +00:00
|
|
|
int enable_pinpad;
|
2005-09-08 11:35:26 +00:00
|
|
|
int connect_exclusive;
|
|
|
|
int connect_reset;
|
|
|
|
int transaction_reset;
|
2008-04-02 05:44:12 +00:00
|
|
|
const char *provider_library;
|
2008-03-06 16:06:59 +00:00
|
|
|
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;
|
2002-02-24 19:32:14 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct pcsc_private_data {
|
|
|
|
char *reader_name;
|
2002-06-14 11:33:20 +00:00
|
|
|
struct pcsc_global_private_data *gpriv;
|
2002-02-24 19:32:14 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct pcsc_slot_data {
|
|
|
|
SCARDHANDLE pcsc_card;
|
2005-03-04 01:41:39 +00:00
|
|
|
SCARD_READERSTATE_A reader_state;
|
2005-03-09 12:44:31 +00:00
|
|
|
DWORD verify_ioctl;
|
2006-08-16 16:36:00 +00:00
|
|
|
DWORD verify_ioctl_start;
|
|
|
|
DWORD verify_ioctl_finish;
|
|
|
|
|
2005-03-09 12:44:31 +00:00
|
|
|
DWORD modify_ioctl;
|
2006-08-16 16:36:00 +00:00
|
|
|
DWORD modify_ioctl_start;
|
|
|
|
DWORD modify_ioctl_finish;
|
2006-03-22 21:44:09 +00:00
|
|
|
int locked;
|
2002-02-24 19:32:14 +00:00
|
|
|
};
|
|
|
|
|
2005-03-08 20:59:35 +00:00
|
|
|
static int pcsc_detect_card_presence(sc_reader_t *reader, sc_slot_info_t *slot);
|
2003-02-12 14:20:00 +00:00
|
|
|
|
2002-02-24 19:32:14 +00:00
|
|
|
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;
|
2003-02-19 13:36:58 +00:00
|
|
|
case SCARD_W_UNRESPONSIVE_CARD:
|
|
|
|
return SC_ERROR_CARD_UNRESPONSIVE;
|
2008-02-10 16:13:55 +00:00
|
|
|
case SCARD_W_UNPOWERED_CARD:
|
|
|
|
return SC_ERROR_CARD_UNRESPONSIVE;
|
2005-09-08 11:35:26 +00:00
|
|
|
case SCARD_E_SHARING_VIOLATION:
|
|
|
|
return SC_ERROR_READER;
|
2007-03-21 09:41:08 +00:00
|
|
|
#ifdef SCARD_E_NO_READERS_AVAILABLE /* Older pcsc-lite does not have it */
|
2009-01-18 23:16:53 +00:00
|
|
|
case SCARD_E_NO_READERS_AVAILABLE:
|
2009-01-19 11:57:20 +00:00
|
|
|
return SC_ERROR_NO_READERS_FOUND;
|
2007-03-21 09:41:08 +00:00
|
|
|
#endif
|
2009-01-18 23:16:53 +00:00
|
|
|
case SCARD_E_NO_SERVICE:
|
2009-01-19 11:57:20 +00:00
|
|
|
/* If the service is (auto)started, there could be readers later */
|
2009-01-18 23:16:53 +00:00
|
|
|
return SC_ERROR_NO_READERS_FOUND;
|
2002-02-24 19:32:14 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-02-05 19:00:01 +00:00
|
|
|
static int pcsc_internal_transmit(sc_reader_t *reader, sc_slot_info_t *slot,
|
2002-02-24 19:32:14 +00:00
|
|
|
const u8 *sendbuf, size_t sendsize,
|
2002-12-23 18:47:27 +00:00
|
|
|
u8 *recvbuf, size_t *recvsize,
|
2005-01-24 11:31:11 +00:00
|
|
|
unsigned long control)
|
2002-02-24 19:32:14 +00:00
|
|
|
{
|
2008-03-06 16:06:59 +00:00
|
|
|
struct pcsc_private_data *priv = GET_PRIV_DATA(reader);
|
2002-02-24 19:32:14 +00:00
|
|
|
SCARD_IO_REQUEST sSendPci, sRecvPci;
|
|
|
|
DWORD dwSendLength, dwRecvLength;
|
|
|
|
LONG rv;
|
|
|
|
SCARDHANDLE card;
|
|
|
|
struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot);
|
|
|
|
|
2007-01-05 16:25:31 +00:00
|
|
|
SC_FUNC_CALLED(reader->ctx, 3);
|
2002-02-24 19:32:14 +00:00
|
|
|
assert(pslot != NULL);
|
|
|
|
card = pslot->pcsc_card;
|
|
|
|
|
|
|
|
sSendPci.dwProtocol = opensc_proto_to_pcsc(slot->active_protocol);
|
2002-06-14 12:52:56 +00:00
|
|
|
sSendPci.cbPciLength = sizeof(sSendPci);
|
2002-02-24 19:32:14 +00:00
|
|
|
sRecvPci.dwProtocol = opensc_proto_to_pcsc(slot->active_protocol);
|
2002-06-14 12:52:56 +00:00
|
|
|
sRecvPci.cbPciLength = sizeof(sRecvPci);
|
2005-08-13 13:26:46 +00:00
|
|
|
|
2002-02-24 19:32:14 +00:00
|
|
|
dwSendLength = sendsize;
|
|
|
|
dwRecvLength = *recvsize;
|
2002-06-14 12:52:56 +00:00
|
|
|
|
2002-12-23 18:47:27 +00:00
|
|
|
if (!control) {
|
2008-03-06 16:06:59 +00:00
|
|
|
rv = priv->gpriv->SCardTransmit(card, &sSendPci, sendbuf, dwSendLength,
|
2002-12-23 18:47:27 +00:00
|
|
|
&sRecvPci, recvbuf, &dwRecvLength);
|
|
|
|
} else {
|
2008-03-06 16:06:59 +00:00
|
|
|
if (priv->gpriv->SCardControlOLD != NULL) {
|
|
|
|
rv = priv->gpriv->SCardControlOLD(card, sendbuf, dwSendLength,
|
2002-12-23 18:47:27 +00:00
|
|
|
recvbuf, &dwRecvLength);
|
2008-03-06 16:06:59 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
rv = priv->gpriv->SCardControl(card, (DWORD) control, sendbuf, dwSendLength,
|
2003-01-02 15:31:53 +00:00
|
|
|
recvbuf, dwRecvLength, &dwRecvLength);
|
2008-03-06 16:06:59 +00:00
|
|
|
}
|
2002-12-23 18:47:27 +00:00
|
|
|
}
|
2002-06-14 12:52:56 +00:00
|
|
|
|
2002-02-24 19:32:14 +00:00
|
|
|
if (rv != SCARD_S_SUCCESS) {
|
|
|
|
switch (rv) {
|
|
|
|
case SCARD_W_REMOVED_CARD:
|
|
|
|
return SC_ERROR_CARD_REMOVED;
|
|
|
|
case SCARD_E_NOT_TRANSACTED:
|
2005-09-08 11:35:26 +00:00
|
|
|
if (!(pcsc_detect_card_presence(reader, slot) & SC_SLOT_CARD_PRESENT))
|
2002-02-24 19:32:14 +00:00
|
|
|
return SC_ERROR_CARD_REMOVED;
|
|
|
|
return SC_ERROR_TRANSMIT_FAILED;
|
2005-08-13 13:26:46 +00:00
|
|
|
default:
|
|
|
|
/* Windows' PC/SC returns 0x8010002f (??) if a card is removed */
|
2003-02-12 14:20:00 +00:00
|
|
|
if (pcsc_detect_card_presence(reader, slot) != 1)
|
|
|
|
return SC_ERROR_CARD_REMOVED;
|
2005-08-13 13:26:46 +00:00
|
|
|
PCSC_ERROR(reader->ctx, "SCardTransmit failed", rv);
|
2002-02-24 19:32:14 +00:00
|
|
|
return SC_ERROR_TRANSMIT_FAILED;
|
|
|
|
}
|
|
|
|
}
|
2006-08-16 16:36:00 +00:00
|
|
|
if (!control && dwRecvLength < 2)
|
2002-04-06 12:02:28 +00:00
|
|
|
return SC_ERROR_UNKNOWN_DATA_RECEIVED;
|
2002-02-24 19:32:14 +00:00
|
|
|
*recvsize = dwRecvLength;
|
2005-08-13 13:26:46 +00:00
|
|
|
|
2009-03-21 11:09:12 +00:00
|
|
|
return SC_SUCCESS;
|
2002-02-24 19:32:14 +00:00
|
|
|
}
|
|
|
|
|
2006-02-05 19:00:01 +00:00
|
|
|
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 */
|
2006-03-03 22:56:41 +00:00
|
|
|
r = sc_apdu_get_octets(reader->ctx, apdu, &sbuf, &ssize, slot->active_protocol);
|
2006-02-05 19:00:01 +00:00
|
|
|
if (r != SC_SUCCESS)
|
|
|
|
goto out;
|
2006-11-23 22:40:01 +00:00
|
|
|
if (reader->ctx->debug >= 6)
|
|
|
|
sc_apdu_log(reader->ctx, sbuf, ssize, 1);
|
2006-03-03 22:56:41 +00:00
|
|
|
|
2006-02-05 19:00:01 +00:00
|
|
|
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;
|
|
|
|
}
|
2006-11-23 22:40:01 +00:00
|
|
|
if (reader->ctx->debug >= 6)
|
|
|
|
sc_apdu_log(reader->ctx, rbuf, rsize, 0);
|
2006-03-03 22:56:41 +00:00
|
|
|
/* set response */
|
|
|
|
r = sc_apdu_set_resp(reader->ctx, apdu, rbuf, rsize);
|
2006-02-05 19:00:01 +00:00
|
|
|
out:
|
|
|
|
if (sbuf != NULL) {
|
|
|
|
sc_mem_clear(sbuf, ssize);
|
|
|
|
free(sbuf);
|
|
|
|
}
|
|
|
|
if (rbuf != NULL) {
|
|
|
|
sc_mem_clear(rbuf, rbuflen);
|
|
|
|
free(rbuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-03-08 20:59:35 +00:00
|
|
|
static int refresh_slot_attributes(sc_reader_t *reader, sc_slot_info_t *slot)
|
2002-02-24 19:32:14 +00:00
|
|
|
{
|
|
|
|
struct pcsc_private_data *priv = GET_PRIV_DATA(reader);
|
2003-01-19 17:47:07 +00:00
|
|
|
struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot);
|
2002-02-24 19:32:14 +00:00
|
|
|
LONG ret;
|
|
|
|
|
2007-01-05 16:25:31 +00:00
|
|
|
SC_FUNC_CALLED(reader->ctx, 3);
|
2005-03-04 01:41:39 +00:00
|
|
|
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;
|
2003-01-19 17:47:07 +00:00
|
|
|
} else {
|
2005-03-04 01:41:39 +00:00
|
|
|
pslot->reader_state.dwCurrentState = pslot->reader_state.dwEventState;
|
2003-01-19 17:47:07 +00:00
|
|
|
}
|
|
|
|
|
2009-01-15 21:23:09 +00:00
|
|
|
ret = priv->gpriv->SCardGetStatusChange(priv->gpriv->pcsc_ctx, 0, &pslot->reader_state, 1);
|
2004-12-21 13:22:10 +00:00
|
|
|
if (ret == (LONG)SCARD_E_TIMEOUT) { /* timeout: nothing changed */
|
2003-01-27 13:01:17 +00:00
|
|
|
slot->flags &= ~SCARD_STATE_CHANGED;
|
|
|
|
return 0;
|
|
|
|
}
|
2008-10-20 07:27:41 +00:00
|
|
|
if (ret != SCARD_S_SUCCESS) {
|
2002-06-14 12:52:56 +00:00
|
|
|
PCSC_ERROR(reader->ctx, "SCardGetStatusChange failed", ret);
|
2002-02-24 19:32:14 +00:00
|
|
|
return pcsc_ret_to_error(ret);
|
|
|
|
}
|
2005-03-04 01:41:39 +00:00
|
|
|
if (pslot->reader_state.dwEventState & SCARD_STATE_PRESENT) {
|
2003-01-19 17:47:07 +00:00
|
|
|
int old_flags = slot->flags;
|
2003-04-17 14:35:58 +00:00
|
|
|
int maybe_changed = 0;
|
2003-01-19 17:47:07 +00:00
|
|
|
|
2002-12-06 21:40:41 +00:00
|
|
|
slot->flags |= SC_SLOT_CARD_PRESENT;
|
2005-03-04 01:41:39 +00:00
|
|
|
slot->atr_len = pslot->reader_state.cbAtr;
|
2002-02-24 19:32:14 +00:00
|
|
|
if (slot->atr_len > SC_MAX_ATR_SIZE)
|
|
|
|
slot->atr_len = SC_MAX_ATR_SIZE;
|
2005-03-04 01:41:39 +00:00
|
|
|
memcpy(slot->atr, pslot->reader_state.rgbAtr, slot->atr_len);
|
2003-04-17 14:35:58 +00:00
|
|
|
|
2003-01-27 13:01:17 +00:00
|
|
|
#ifndef _WIN32
|
2003-04-17 14:35:58 +00:00
|
|
|
/* On Linux, SCARD_STATE_CHANGED always means there was an
|
2005-03-04 01:41:39 +00:00
|
|
|
* 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;
|
|
|
|
}
|
2003-03-20 12:52:39 +00:00
|
|
|
#else
|
2005-03-04 01:41:39 +00:00
|
|
|
/* On windows, SCARD_STATE_CHANGED is turned on by lots of
|
|
|
|
* other events, so it gives us a lot of false positives.
|
2009-01-15 21:40:44 +00:00
|
|
|
* But if it's off, there really was no change */
|
2005-03-04 01:41:39 +00:00
|
|
|
if (pslot->reader_state.dwEventState & SCARD_STATE_CHANGED) {
|
|
|
|
maybe_changed = 1;
|
|
|
|
}
|
2003-04-17 14:35:58 +00:00
|
|
|
#endif
|
2005-03-04 01:41:39 +00:00
|
|
|
/* If we aren't sure if the card state changed, check if
|
|
|
|
* the card handle is still valid. If the card changed,
|
2003-04-17 14:35:58 +00:00
|
|
|
* the handle will be invalid. */
|
2003-04-22 07:51:45 +00:00
|
|
|
slot->flags &= ~SC_SLOT_CARD_CHANGED;
|
2005-06-17 15:47:28 +00:00
|
|
|
if (maybe_changed) {
|
|
|
|
if (old_flags & SC_SLOT_CARD_PRESENT) {
|
2009-01-15 21:40:44 +00:00
|
|
|
DWORD readers_len = 0, state, prot, atr_len = SC_MAX_ATR_SIZE;
|
|
|
|
unsigned char atr[SC_MAX_ATR_SIZE];
|
2008-03-06 16:06:59 +00:00
|
|
|
LONG rv = priv->gpriv->SCardStatus(pslot->pcsc_card, NULL, &readers_len,
|
2005-06-17 15:47:28 +00:00
|
|
|
&state, &prot, atr, &atr_len);
|
|
|
|
if (rv == (LONG)SCARD_W_REMOVED_CARD)
|
|
|
|
slot->flags |= SC_SLOT_CARD_CHANGED;
|
|
|
|
}
|
|
|
|
else
|
2003-03-20 12:52:39 +00:00
|
|
|
slot->flags |= SC_SLOT_CARD_CHANGED;
|
|
|
|
}
|
2002-12-06 21:40:41 +00:00
|
|
|
} else {
|
2003-01-19 17:47:07 +00:00
|
|
|
slot->flags &= ~(SC_SLOT_CARD_PRESENT|SC_SLOT_CARD_CHANGED);
|
2002-02-24 19:32:14 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-03-08 20:59:35 +00:00
|
|
|
static int pcsc_detect_card_presence(sc_reader_t *reader, sc_slot_info_t *slot)
|
2002-12-06 21:40:41 +00:00
|
|
|
{
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
if ((rv = refresh_slot_attributes(reader, slot)) < 0)
|
|
|
|
return rv;
|
2003-01-19 17:47:07 +00:00
|
|
|
return slot->flags;
|
2002-12-06 21:40:41 +00:00
|
|
|
}
|
|
|
|
|
2003-01-03 16:32:06 +00:00
|
|
|
/* 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
|
|
|
|
*/
|
2005-03-08 20:59:35 +00:00
|
|
|
static int pcsc_wait_for_event(sc_reader_t **readers,
|
|
|
|
sc_slot_info_t **slots,
|
2003-01-03 16:32:06 +00:00
|
|
|
size_t nslots,
|
|
|
|
unsigned int event_mask,
|
|
|
|
int *reader,
|
|
|
|
unsigned int *event, int timeout)
|
|
|
|
{
|
2008-03-06 16:06:59 +00:00
|
|
|
struct pcsc_private_data *priv = GET_PRIV_DATA(readers[0]);
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_context_t *ctx;
|
2003-01-03 16:32:06 +00:00
|
|
|
SCARDCONTEXT pcsc_ctx;
|
|
|
|
LONG ret;
|
|
|
|
SCARD_READERSTATE_A rgReaderStates[SC_MAX_READERS];
|
|
|
|
unsigned long on_bits, off_bits;
|
|
|
|
time_t end_time, now, delta;
|
2004-12-21 13:22:10 +00:00
|
|
|
size_t i;
|
2003-01-03 16:32:06 +00:00
|
|
|
|
|
|
|
/* Prevent buffer overflow */
|
|
|
|
if (nslots >= SC_MAX_READERS)
|
2005-08-13 13:26:46 +00:00
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
2003-01-03 16:32:06 +00:00
|
|
|
|
|
|
|
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)
|
2005-08-13 13:26:46 +00:00
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
2003-01-03 16:32:06 +00:00
|
|
|
|
2003-07-24 13:10:53 +00:00
|
|
|
/* Find out the current status */
|
2003-01-03 16:32:06 +00:00
|
|
|
ctx = readers[0]->ctx;
|
2008-03-06 16:06:59 +00:00
|
|
|
pcsc_ctx = priv->gpriv->pcsc_ctx;
|
2003-01-03 16:32:06 +00:00
|
|
|
for (i = 0; i < nslots; i++) {
|
2008-05-22 12:22:23 +00:00
|
|
|
struct pcsc_private_data *priv2 = GET_PRIV_DATA(readers[i]);
|
2003-01-03 16:32:06 +00:00
|
|
|
|
2008-05-22 12:22:23 +00:00
|
|
|
rgReaderStates[i].szReader = priv2->reader_name;
|
2003-01-03 16:32:06 +00:00
|
|
|
rgReaderStates[i].dwCurrentState = SCARD_STATE_UNAWARE;
|
|
|
|
rgReaderStates[i].dwEventState = SCARD_STATE_UNAWARE;
|
|
|
|
|
|
|
|
/* Can we handle readers from different PCSC contexts? */
|
2008-05-22 12:22:23 +00:00
|
|
|
if (priv2->gpriv->pcsc_ctx != pcsc_ctx)
|
2003-01-03 16:32:06 +00:00
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
}
|
|
|
|
|
2008-03-06 16:06:59 +00:00
|
|
|
ret = priv->gpriv->SCardGetStatusChange(pcsc_ctx, 0, rgReaderStates, nslots);
|
2008-10-20 07:27:41 +00:00
|
|
|
if (ret != SCARD_S_SUCCESS) {
|
2003-01-03 16:32:06 +00:00
|
|
|
PCSC_ERROR(ctx, "SCardGetStatusChange(1) failed", ret);
|
|
|
|
return pcsc_ret_to_error(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
time(&now);
|
2003-01-05 17:59:24 +00:00
|
|
|
end_time = now + (timeout + 999) / 1000;
|
2003-01-03 16:32:06 +00:00
|
|
|
|
|
|
|
/* Wait for a status change and return if it's a card insert/removal
|
|
|
|
*/
|
|
|
|
for( ; ; ) {
|
2003-03-10 21:23:08 +00:00
|
|
|
SCARD_READERSTATE_A *rsp;
|
|
|
|
|
2003-01-03 16:32:06 +00:00
|
|
|
/* Scan the current state of all readers to see if they
|
|
|
|
* match any of the events we're polling for */
|
|
|
|
*event = 0;
|
2005-08-13 13:26:46 +00:00
|
|
|
for (i = 0, rsp = rgReaderStates; i < nslots; i++, rsp++) {
|
2003-01-27 13:01:17 +00:00
|
|
|
unsigned long state, prev_state;
|
2003-01-03 16:32:06 +00:00
|
|
|
|
2003-03-10 21:23:08 +00:00
|
|
|
prev_state = rsp->dwCurrentState;
|
|
|
|
state = rsp->dwEventState;
|
2003-01-27 13:01:17 +00:00
|
|
|
if ((state & on_bits & SCARD_STATE_PRESENT) &&
|
|
|
|
(prev_state & SCARD_STATE_EMPTY))
|
2003-01-03 16:32:06 +00:00
|
|
|
*event |= SC_EVENT_CARD_INSERTED;
|
2003-01-27 13:01:17 +00:00
|
|
|
if ((~state & off_bits & SCARD_STATE_PRESENT) &&
|
|
|
|
(prev_state & SCARD_STATE_PRESENT))
|
2003-01-03 16:32:06 +00:00
|
|
|
*event |= SC_EVENT_CARD_REMOVED;
|
|
|
|
if (*event) {
|
|
|
|
*reader = i;
|
2009-03-21 11:09:12 +00:00
|
|
|
return SC_SUCCESS;
|
2005-08-13 13:26:46 +00:00
|
|
|
}
|
2003-01-03 16:32:06 +00:00
|
|
|
|
|
|
|
/* No match - copy the state so pcscd knows
|
|
|
|
* what to watch out for */
|
2003-03-10 21:23:08 +00:00
|
|
|
rsp->dwCurrentState = rsp->dwEventState;
|
2005-08-13 13:26:46 +00:00
|
|
|
}
|
2003-01-03 16:32:06 +00:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2008-03-06 16:06:59 +00:00
|
|
|
ret = priv->gpriv->SCardGetStatusChange(pcsc_ctx, 1000 * delta,
|
2005-08-13 13:26:46 +00:00
|
|
|
rgReaderStates, nslots);
|
|
|
|
if (ret == (LONG) SCARD_E_TIMEOUT) {
|
2003-01-05 17:59:24 +00:00
|
|
|
if (timeout < 0)
|
|
|
|
continue;
|
2005-08-13 13:26:46 +00:00
|
|
|
return SC_ERROR_EVENT_TIMEOUT;
|
|
|
|
}
|
2008-10-20 07:27:41 +00:00
|
|
|
if (ret != SCARD_S_SUCCESS) {
|
2005-08-13 13:26:46 +00:00
|
|
|
PCSC_ERROR(ctx, "SCardGetStatusChange(2) failed", ret);
|
|
|
|
return pcsc_ret_to_error(ret);
|
2003-01-05 17:59:24 +00:00
|
|
|
}
|
2003-01-03 16:32:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-03-22 21:44:09 +00:00
|
|
|
static int pcsc_reconnect(sc_reader_t * reader, sc_slot_info_t * slot, int reset)
|
2005-09-08 11:35:26 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2008-02-10 16:13:55 +00:00
|
|
|
/* reconnect always unlocks transaction */
|
|
|
|
pslot->locked = 0;
|
|
|
|
|
2008-03-06 16:06:59 +00:00
|
|
|
rv = priv->gpriv->SCardReconnect(pslot->pcsc_card,
|
2008-02-10 16:13:55 +00:00
|
|
|
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
|
2005-09-08 11:35:26 +00:00
|
|
|
(reader->ctx, slot->atr, slot->atr_len,
|
|
|
|
(unsigned int *)&protocol)) {
|
|
|
|
protocol = opensc_proto_to_pcsc(protocol);
|
2008-02-10 16:13:55 +00:00
|
|
|
if (pcsc_proto_to_opensc(active_proto) != protocol) {
|
2008-03-06 16:06:59 +00:00
|
|
|
rv = priv->gpriv->SCardReconnect(pslot->pcsc_card,
|
2008-02-10 16:13:55 +00:00
|
|
|
priv->gpriv->connect_exclusive ? SCARD_SHARE_EXCLUSIVE : SCARD_SHARE_SHARED,
|
|
|
|
protocol, SCARD_UNPOWER_CARD, &active_proto);
|
|
|
|
}
|
2005-09-08 11:35:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (rv != SCARD_S_SUCCESS) {
|
|
|
|
PCSC_ERROR(reader->ctx, "SCardReconnect failed", rv);
|
2008-02-10 16:13:55 +00:00
|
|
|
return rv;
|
2005-09-08 11:35:26 +00:00
|
|
|
}
|
2008-02-10 16:13:55 +00:00
|
|
|
|
2005-09-08 11:35:26 +00:00
|
|
|
slot->active_protocol = pcsc_proto_to_opensc(active_proto);
|
2008-02-10 16:13:55 +00:00
|
|
|
return rv;
|
2005-09-08 11:35:26 +00:00
|
|
|
}
|
|
|
|
|
2005-03-08 20:59:35 +00:00
|
|
|
static int pcsc_connect(sc_reader_t *reader, sc_slot_info_t *slot)
|
2002-02-24 19:32:14 +00:00
|
|
|
{
|
2005-02-22 07:59:42 +00:00
|
|
|
DWORD active_proto, protocol;
|
2002-02-24 19:32:14 +00:00
|
|
|
SCARDHANDLE card_handle;
|
|
|
|
LONG rv;
|
|
|
|
struct pcsc_private_data *priv = GET_PRIV_DATA(reader);
|
|
|
|
struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot);
|
2005-03-23 23:24:13 +00:00
|
|
|
int r;
|
2005-03-09 12:44:31 +00:00
|
|
|
u8 feature_buf[256];
|
2009-03-21 11:09:12 +00:00
|
|
|
DWORD i, feature_len, display_ioctl;
|
2005-10-29 21:17:15 +00:00
|
|
|
PCSC_TLV_STRUCTURE *pcsc_tlv;
|
2002-02-24 19:32:14 +00:00
|
|
|
|
|
|
|
r = refresh_slot_attributes(reader, slot);
|
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
if (!(slot->flags & SC_SLOT_CARD_PRESENT))
|
|
|
|
return SC_ERROR_CARD_NOT_PRESENT;
|
2004-08-19 08:55:15 +00:00
|
|
|
|
2007-03-21 09:41:08 +00:00
|
|
|
/* Always connect with whatever protocol possible */
|
2008-03-06 16:06:59 +00:00
|
|
|
rv = priv->gpriv->SCardConnect(priv->gpriv->pcsc_ctx, priv->reader_name,
|
2005-09-08 11:35:26 +00:00
|
|
|
priv->gpriv->connect_exclusive ? SCARD_SHARE_EXCLUSIVE : SCARD_SHARE_SHARED,
|
2007-03-21 09:41:08 +00:00
|
|
|
SCARD_PROTOCOL_ANY, &card_handle, &active_proto);
|
2008-10-20 07:27:41 +00:00
|
|
|
if (rv != SCARD_S_SUCCESS) {
|
2002-06-14 12:52:56 +00:00
|
|
|
PCSC_ERROR(reader->ctx, "SCardConnect failed", rv);
|
2002-02-24 19:32:14 +00:00
|
|
|
return pcsc_ret_to_error(rv);
|
|
|
|
}
|
|
|
|
slot->active_protocol = pcsc_proto_to_opensc(active_proto);
|
|
|
|
pslot->pcsc_card = card_handle;
|
|
|
|
|
2006-03-22 21:44:09 +00:00
|
|
|
/* after connect reader is not locked yet */
|
|
|
|
pslot->locked = 0;
|
2007-03-21 09:41:08 +00:00
|
|
|
sc_debug(reader->ctx, "After connect protocol = %d", slot->active_protocol);
|
2006-03-22 21:44:09 +00:00
|
|
|
|
2007-03-21 09:41:08 +00:00
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-01-25 11:11:13 +00:00
|
|
|
/* check for pinpad support */
|
2008-03-06 16:06:59 +00:00
|
|
|
if (priv->gpriv->SCardControl != NULL) {
|
|
|
|
sc_debug(reader->ctx, "Requesting reader features ... ");
|
2005-03-09 12:44:31 +00:00
|
|
|
|
2008-03-06 16:06:59 +00:00
|
|
|
rv = priv->gpriv->SCardControl(pslot->pcsc_card, CM_IOCTL_GET_FEATURE_REQUEST, NULL,
|
|
|
|
0, feature_buf, sizeof(feature_buf), &feature_len);
|
2009-04-03 19:17:15 +00:00
|
|
|
if (rv != SCARD_S_SUCCESS) {
|
|
|
|
sc_debug(reader->ctx, "SCardControl failed", rv);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if ((feature_len % sizeof(PCSC_TLV_STRUCTURE)) != 0) {
|
|
|
|
sc_debug(reader->ctx, "Inconsistent TLV from reader!");
|
|
|
|
}
|
|
|
|
else {
|
2008-03-06 16:06:59 +00:00
|
|
|
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);
|
2009-03-21 11:09:12 +00:00
|
|
|
} else if (pcsc_tlv[i].tag == FEATURE_IFD_PIN_PROPERTIES) {
|
|
|
|
display_ioctl = ntohl(pcsc_tlv[i].value);
|
2006-05-17 09:07:17 +00:00
|
|
|
} else {
|
2008-03-06 16:06:59 +00:00
|
|
|
sc_debug(reader->ctx, "Reader pinpad feature: %02x not supported", pcsc_tlv[i].tag);
|
2005-08-13 13:26:46 +00:00
|
|
|
}
|
2005-03-09 12:44:31 +00:00
|
|
|
}
|
2008-03-06 16:06:59 +00:00
|
|
|
|
|
|
|
/* Set slot capabilities based on detected IOCTLs */
|
|
|
|
if (pslot->verify_ioctl || (pslot->verify_ioctl_start && pslot->verify_ioctl_finish)) {
|
2009-04-03 19:17:15 +00:00
|
|
|
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);
|
|
|
|
}
|
2008-03-06 16:06:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (pslot->modify_ioctl || (pslot->modify_ioctl_start && pslot->modify_ioctl_finish)) {
|
2009-04-03 19:17:15 +00:00
|
|
|
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);
|
2009-03-21 11:09:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (display_ioctl) {
|
|
|
|
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
|
|
|
|
size_t 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-04-03 19:17:15 +00:00
|
|
|
}
|
|
|
|
}
|
2008-03-06 16:06:59 +00:00
|
|
|
}
|
2005-01-24 11:31:11 +00:00
|
|
|
}
|
2005-03-09 12:44:31 +00:00
|
|
|
return SC_SUCCESS;
|
2002-02-24 19:32:14 +00:00
|
|
|
}
|
|
|
|
|
2006-03-01 09:45:09 +00:00
|
|
|
static int pcsc_disconnect(sc_reader_t * reader, sc_slot_info_t * slot)
|
2002-02-24 19:32:14 +00:00
|
|
|
{
|
|
|
|
struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot);
|
2005-09-08 11:35:26 +00:00
|
|
|
struct pcsc_private_data *priv = GET_PRIV_DATA(reader);
|
2002-02-24 19:32:14 +00:00
|
|
|
|
2008-03-06 16:06:59 +00:00
|
|
|
priv->gpriv->SCardDisconnect(pslot->pcsc_card, priv->gpriv->transaction_reset ?
|
2005-09-08 11:35:26 +00:00
|
|
|
SCARD_RESET_CARD : SCARD_LEAVE_CARD);
|
2003-01-19 17:47:07 +00:00
|
|
|
memset(pslot, 0, sizeof(*pslot));
|
|
|
|
slot->flags = 0;
|
2008-02-10 16:13:55 +00:00
|
|
|
return SC_SUCCESS;
|
2002-02-24 19:32:14 +00:00
|
|
|
}
|
2005-03-04 01:41:39 +00:00
|
|
|
|
2005-03-08 20:59:35 +00:00
|
|
|
static int pcsc_lock(sc_reader_t *reader, sc_slot_info_t *slot)
|
2002-02-24 19:32:14 +00:00
|
|
|
{
|
|
|
|
long rv;
|
|
|
|
struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot);
|
2008-03-06 16:06:59 +00:00
|
|
|
struct pcsc_private_data *priv = GET_PRIV_DATA(reader);
|
2002-02-24 19:32:14 +00:00
|
|
|
|
2007-01-05 16:25:31 +00:00
|
|
|
SC_FUNC_CALLED(reader->ctx, 3);
|
2002-02-24 19:32:14 +00:00
|
|
|
assert(pslot != NULL);
|
2005-09-08 11:35:26 +00:00
|
|
|
|
2008-03-06 16:06:59 +00:00
|
|
|
rv = priv->gpriv->SCardBeginTransaction(pslot->pcsc_card);
|
2005-09-08 11:35:26 +00:00
|
|
|
|
2008-04-29 17:01:19 +00:00
|
|
|
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);
|
2005-09-08 11:35:26 +00:00
|
|
|
return pcsc_ret_to_error(rv);
|
|
|
|
}
|
2002-02-24 19:32:14 +00:00
|
|
|
}
|
|
|
|
|
2005-03-08 20:59:35 +00:00
|
|
|
static int pcsc_unlock(sc_reader_t *reader, sc_slot_info_t *slot)
|
2002-02-24 19:32:14 +00:00
|
|
|
{
|
|
|
|
long rv;
|
|
|
|
struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot);
|
2005-09-08 11:35:26 +00:00
|
|
|
struct pcsc_private_data *priv = GET_PRIV_DATA(reader);
|
2002-02-24 19:32:14 +00:00
|
|
|
|
2007-01-05 16:25:31 +00:00
|
|
|
SC_FUNC_CALLED(reader->ctx, 3);
|
2002-02-24 19:32:14 +00:00
|
|
|
assert(pslot != NULL);
|
2006-03-22 21:44:09 +00:00
|
|
|
|
2008-03-06 16:06:59 +00:00
|
|
|
rv = priv->gpriv->SCardEndTransaction(pslot->pcsc_card, priv->gpriv->transaction_reset ?
|
2005-09-08 11:35:26 +00:00
|
|
|
SCARD_RESET_CARD : SCARD_LEAVE_CARD);
|
2007-01-05 16:25:31 +00:00
|
|
|
|
|
|
|
pslot->locked = 0;
|
2002-02-24 19:32:14 +00:00
|
|
|
if (rv != SCARD_S_SUCCESS) {
|
2002-06-14 12:52:56 +00:00
|
|
|
PCSC_ERROR(reader->ctx, "SCardEndTransaction failed", rv);
|
2005-08-13 13:26:46 +00:00
|
|
|
return pcsc_ret_to_error(rv);
|
2002-02-24 19:32:14 +00:00
|
|
|
}
|
2008-02-10 16:13:55 +00:00
|
|
|
return SC_SUCCESS;
|
2002-02-24 19:32:14 +00:00
|
|
|
}
|
|
|
|
|
2005-03-08 20:59:35 +00:00
|
|
|
static int pcsc_release(sc_reader_t *reader)
|
2002-02-24 19:32:14 +00:00
|
|
|
{
|
|
|
|
struct pcsc_private_data *priv = GET_PRIV_DATA(reader);
|
|
|
|
|
|
|
|
free(priv->reader_name);
|
|
|
|
free(priv);
|
2009-01-15 21:40:44 +00:00
|
|
|
if (reader->slot[0].drv_data != NULL) {
|
|
|
|
free(reader->slot[0].drv_data);
|
|
|
|
reader->slot[0].drv_data = NULL;
|
2004-07-09 15:31:08 +00:00
|
|
|
}
|
2008-02-10 16:13:55 +00:00
|
|
|
return SC_SUCCESS;
|
2002-02-24 19:32:14 +00:00
|
|
|
}
|
|
|
|
|
2006-03-22 21:44:09 +00:00
|
|
|
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);
|
2008-02-10 16:13:55 +00:00
|
|
|
if(r != SCARD_S_SUCCESS)
|
|
|
|
return pcsc_ret_to_error(r);
|
2006-03-22 21:44:09 +00:00
|
|
|
|
|
|
|
/* pcsc_reconnect unlocks card... try to lock it again if it was locked */
|
|
|
|
if(old_locked)
|
|
|
|
r = pcsc_lock(reader, slot);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2002-02-24 19:32:14 +00:00
|
|
|
static struct sc_reader_operations pcsc_ops;
|
|
|
|
|
2003-12-18 16:35:28 +00:00
|
|
|
static struct sc_reader_driver pcsc_drv = {
|
2003-12-10 14:52:58 +00:00
|
|
|
"PC/SC reader",
|
2002-02-24 19:32:14 +00:00
|
|
|
"pcsc",
|
2005-09-07 08:33:55 +00:00
|
|
|
&pcsc_ops,
|
2006-02-05 19:00:01 +00:00
|
|
|
0, 0, NULL
|
2002-02-24 19:32:14 +00:00
|
|
|
};
|
|
|
|
|
2005-03-08 20:59:35 +00:00
|
|
|
static int pcsc_init(sc_context_t *ctx, void **reader_data)
|
2002-02-24 19:32:14 +00:00
|
|
|
{
|
|
|
|
struct pcsc_global_private_data *gpriv;
|
2008-03-06 16:06:59 +00:00
|
|
|
scconf_block *conf_block = NULL;
|
|
|
|
int ret = SC_ERROR_INTERNAL;
|
2002-02-24 19:32:14 +00:00
|
|
|
|
2008-03-06 16:06:59 +00:00
|
|
|
*reader_data = NULL;
|
|
|
|
|
|
|
|
gpriv = (struct pcsc_global_private_data *) calloc(1, sizeof(struct pcsc_global_private_data));
|
2002-02-24 19:32:14 +00:00
|
|
|
if (gpriv == NULL) {
|
2008-03-06 16:06:59 +00:00
|
|
|
ret = SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
goto out;
|
2002-02-24 19:32:14 +00:00
|
|
|
}
|
2007-01-05 16:36:33 +00:00
|
|
|
|
|
|
|
/* Defaults */
|
|
|
|
gpriv->connect_reset = 1;
|
|
|
|
gpriv->connect_exclusive = 0;
|
|
|
|
gpriv->transaction_reset = 0;
|
|
|
|
gpriv->enable_pinpad = 0;
|
2008-04-02 05:44:12 +00:00
|
|
|
gpriv->provider_library = DEFAULT_PCSC_PROVIDER;
|
2008-04-29 17:01:19 +00:00
|
|
|
gpriv->pcsc_ctx = -1;
|
2005-08-21 18:44:07 +00:00
|
|
|
|
2006-01-31 15:53:44 +00:00
|
|
|
conf_block = sc_get_conf_block(ctx, "reader_driver", "pcsc", 1);
|
2005-08-21 18:44:07 +00:00
|
|
|
if (conf_block) {
|
2005-09-08 11:35:26 +00:00
|
|
|
gpriv->connect_reset =
|
2007-01-05 16:36:33 +00:00
|
|
|
scconf_get_bool(conf_block, "connect_reset", gpriv->connect_reset);
|
2005-09-08 11:35:26 +00:00
|
|
|
gpriv->connect_exclusive =
|
2007-01-05 16:36:33 +00:00
|
|
|
scconf_get_bool(conf_block, "connect_exclusive", gpriv->connect_exclusive);
|
2005-09-08 11:35:26 +00:00
|
|
|
gpriv->transaction_reset =
|
2007-01-05 16:36:33 +00:00
|
|
|
scconf_get_bool(conf_block, "transaction_reset", gpriv->transaction_reset);
|
2005-08-21 18:44:07 +00:00
|
|
|
gpriv->enable_pinpad =
|
2007-01-05 16:36:33 +00:00
|
|
|
scconf_get_bool(conf_block, "enable_pinpad", gpriv->enable_pinpad);
|
2008-04-02 05:44:12 +00:00
|
|
|
gpriv->provider_library =
|
|
|
|
scconf_get_str(conf_block, "provider_library", gpriv->provider_library);
|
2008-03-06 16:06:59 +00:00
|
|
|
}
|
|
|
|
|
2008-04-02 05:44:12 +00:00
|
|
|
gpriv->dlhandle = lt_dlopen(gpriv->provider_library);
|
2008-03-06 16:06:59 +00:00
|
|
|
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 */
|
2009-01-28 12:43:30 +00:00
|
|
|
if (lt_dlsym(gpriv->dlhandle, "SCardGetAttrib") != NULL) {
|
2009-01-28 12:28:41 +00:00
|
|
|
#ifdef __APPLE__
|
|
|
|
gpriv->SCardControl = (SCardControl_t)lt_dlsym(gpriv->dlhandle, "SCardControl132");
|
|
|
|
#endif
|
2009-01-28 12:43:30 +00:00
|
|
|
if (gpriv->SCardControl == NULL) {
|
|
|
|
gpriv->SCardControl = (SCardControl_t)lt_dlsym(gpriv->dlhandle, "SCardControl");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2008-03-06 16:06:59 +00:00
|
|
|
gpriv->SCardControlOLD = (SCardControlOLD_t)lt_dlsym(gpriv->dlhandle, "SCardControl");
|
2009-01-28 12:43:30 +00:00
|
|
|
}
|
2008-03-06 16:06:59 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2008-04-29 17:01:19 +00:00
|
|
|
*reader_data = gpriv;
|
|
|
|
gpriv = NULL;
|
|
|
|
ret = SC_SUCCESS;
|
|
|
|
|
|
|
|
out:
|
2008-07-02 09:55:32 +00:00
|
|
|
if (gpriv != NULL) {
|
2008-04-29 17:01:19 +00:00
|
|
|
if (gpriv->dlhandle != NULL)
|
|
|
|
lt_dlclose(gpriv->dlhandle);
|
2008-07-02 09:55:32 +00:00
|
|
|
free(gpriv);
|
2008-03-06 16:06:59 +00:00
|
|
|
}
|
2008-04-29 17:01:19 +00:00
|
|
|
|
2008-07-02 09:55:32 +00:00
|
|
|
return ret;
|
2008-04-29 17:01:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int pcsc_finish(sc_context_t *ctx, void *prv_data)
|
|
|
|
{
|
2008-07-02 09:55:32 +00:00
|
|
|
struct pcsc_global_private_data *gpriv = (struct pcsc_global_private_data *) prv_data;
|
2008-04-29 17:01:19 +00:00
|
|
|
|
2008-07-02 09:55:32 +00:00
|
|
|
if (gpriv) {
|
|
|
|
if (gpriv->pcsc_ctx != -1)
|
|
|
|
gpriv->SCardReleaseContext(gpriv->pcsc_ctx);
|
|
|
|
if (gpriv->dlhandle != NULL)
|
|
|
|
lt_dlclose(gpriv->dlhandle);
|
|
|
|
free(gpriv);
|
2008-04-29 17:01:19 +00:00
|
|
|
}
|
|
|
|
|
2009-03-21 11:09:12 +00:00
|
|
|
return SC_SUCCESS;
|
2008-04-29 17:01:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int pcsc_detect_readers(sc_context_t *ctx, void *prv_data)
|
|
|
|
{
|
|
|
|
struct pcsc_global_private_data *gpriv = (struct pcsc_global_private_data *) prv_data;
|
|
|
|
LONG rv;
|
|
|
|
DWORD reader_buf_size;
|
2008-10-26 19:13:14 +00:00
|
|
|
char *reader_buf = NULL, *reader_name;
|
2008-04-29 17:01:19 +00:00
|
|
|
const char *mszGroups = NULL;
|
|
|
|
int ret = SC_ERROR_INTERNAL;
|
|
|
|
|
2008-10-26 19:13:14 +00:00
|
|
|
SC_FUNC_CALLED(ctx, 3);
|
|
|
|
|
2008-07-02 09:55:32 +00:00
|
|
|
if (!gpriv) {
|
2008-04-29 17:01:19 +00:00
|
|
|
ret = SC_ERROR_NO_READERS_FOUND;
|
2008-03-06 16:06:59 +00:00
|
|
|
goto out;
|
2005-08-21 18:44:07 +00:00
|
|
|
}
|
2005-08-13 13:26:46 +00:00
|
|
|
|
2008-10-26 19:13:14 +00:00
|
|
|
sc_debug(ctx, "Probing pcsc readers");
|
|
|
|
|
2008-04-29 17:01:19 +00:00
|
|
|
do {
|
2008-07-02 09:55:32 +00:00
|
|
|
if (gpriv->pcsc_ctx == -1) {
|
|
|
|
/*
|
|
|
|
* Cannot call SCardListReaders with -1
|
|
|
|
* context as in Windows ERROR_INVALID_HANDLE
|
|
|
|
* is returned instead of SCARD_E_INVALID_HANDLE
|
|
|
|
*/
|
|
|
|
rv = SCARD_E_INVALID_HANDLE;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
rv = gpriv->SCardListReaders(gpriv->pcsc_ctx, NULL, NULL,
|
|
|
|
(LPDWORD) &reader_buf_size);
|
|
|
|
}
|
2008-04-29 17:01:19 +00:00
|
|
|
if (rv != SCARD_S_SUCCESS) {
|
|
|
|
if (rv != SCARD_E_INVALID_HANDLE) {
|
2008-10-26 19:13:14 +00:00
|
|
|
PCSC_ERROR(ctx, "SCardListReaders failed", rv);
|
2008-04-29 17:01:19 +00:00
|
|
|
ret = pcsc_ret_to_error(rv);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2008-10-26 19:13:14 +00:00
|
|
|
sc_debug(ctx, "Establish pcsc context");
|
|
|
|
|
2008-04-29 17:01:19 +00:00
|
|
|
rv = gpriv->SCardEstablishContext(SCARD_SCOPE_USER,
|
|
|
|
NULL, NULL, &gpriv->pcsc_ctx);
|
|
|
|
if (rv != SCARD_S_SUCCESS) {
|
2008-10-26 19:13:14 +00:00
|
|
|
PCSC_ERROR(ctx, "SCardEstablishContext failed", rv);
|
2008-04-29 17:01:19 +00:00
|
|
|
ret = pcsc_ret_to_error(rv);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = SCARD_E_INVALID_HANDLE;
|
|
|
|
}
|
|
|
|
} while (rv != SCARD_S_SUCCESS);
|
|
|
|
|
2002-02-24 19:32:14 +00:00
|
|
|
reader_buf = (char *) malloc(sizeof(char) * reader_buf_size);
|
2004-04-27 17:41:02 +00:00
|
|
|
if (!reader_buf) {
|
2008-03-06 16:06:59 +00:00
|
|
|
ret = SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
goto out;
|
2004-04-27 17:41:02 +00:00
|
|
|
}
|
2008-03-06 16:06:59 +00:00
|
|
|
rv = gpriv->SCardListReaders(gpriv->pcsc_ctx, mszGroups, reader_buf,
|
2005-01-19 16:17:32 +00:00
|
|
|
(LPDWORD) &reader_buf_size);
|
2004-04-27 17:41:02 +00:00
|
|
|
if (rv != SCARD_S_SUCCESS) {
|
2008-10-26 19:13:14 +00:00
|
|
|
PCSC_ERROR(ctx, "SCardListReaders failed", rv);
|
2008-03-06 16:06:59 +00:00
|
|
|
ret = pcsc_ret_to_error(rv);
|
|
|
|
goto out;
|
2004-04-27 17:41:02 +00:00
|
|
|
}
|
2008-10-26 19:13:14 +00:00
|
|
|
for (reader_name = reader_buf; *reader_name != '\x0'; reader_name += strlen (reader_name) + 1) {
|
2008-04-29 17:01:19 +00:00
|
|
|
sc_reader_t *reader = NULL;
|
|
|
|
struct pcsc_private_data *priv = NULL;
|
|
|
|
struct pcsc_slot_data *pslot = NULL;
|
|
|
|
sc_slot_info_t *slot = NULL;
|
|
|
|
int i;
|
|
|
|
int found = 0;
|
|
|
|
|
|
|
|
for (i=0;i < sc_ctx_get_reader_count (ctx) && !found;i++) {
|
2008-05-22 12:22:23 +00:00
|
|
|
sc_reader_t *reader2 = sc_ctx_get_reader (ctx, i);
|
|
|
|
if (reader2 == NULL) {
|
2008-04-29 17:01:19 +00:00
|
|
|
ret = SC_ERROR_INTERNAL;
|
|
|
|
goto err1;
|
|
|
|
}
|
2008-10-26 19:13:14 +00:00
|
|
|
if (reader2->ops == &pcsc_ops && !strcmp (reader2->name, reader_name)) {
|
2008-04-29 17:01:19 +00:00
|
|
|
found = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reader already available, skip */
|
|
|
|
if (found) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2008-10-26 19:13:14 +00:00
|
|
|
sc_debug(ctx, "Found new pcsc reader '%s'", reader_name);
|
|
|
|
|
2008-04-29 17:01:19 +00:00
|
|
|
if ((reader = (sc_reader_t *) calloc(1, sizeof(sc_reader_t))) == NULL) {
|
|
|
|
ret = SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
goto err1;
|
|
|
|
}
|
|
|
|
if ((priv = (struct pcsc_private_data *) malloc(sizeof(struct pcsc_private_data))) == NULL) {
|
|
|
|
ret = SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
goto err1;
|
|
|
|
}
|
|
|
|
if ((pslot = (struct pcsc_slot_data *) malloc(sizeof(struct pcsc_slot_data))) == NULL) {
|
|
|
|
ret = SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
goto err1;
|
2002-02-24 19:32:14 +00:00
|
|
|
}
|
2003-01-19 17:47:07 +00:00
|
|
|
|
2002-02-24 19:32:14 +00:00
|
|
|
reader->drv_data = priv;
|
|
|
|
reader->ops = &pcsc_ops;
|
|
|
|
reader->driver = &pcsc_drv;
|
|
|
|
reader->slot_count = 1;
|
2008-10-26 19:13:14 +00:00
|
|
|
if ((reader->name = strdup(reader_name)) == NULL) {
|
2008-04-29 17:01:19 +00:00
|
|
|
ret = SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
goto err1;
|
|
|
|
}
|
2002-06-14 11:33:20 +00:00
|
|
|
priv->gpriv = gpriv;
|
2008-10-26 19:13:14 +00:00
|
|
|
if ((priv->reader_name = strdup(reader_name)) == NULL) {
|
2008-04-29 17:01:19 +00:00
|
|
|
ret = SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
goto err1;
|
2002-02-24 19:32:14 +00:00
|
|
|
}
|
|
|
|
slot = &reader->slot[0];
|
2003-01-19 17:47:07 +00:00
|
|
|
memset(slot, 0, sizeof(*slot));
|
|
|
|
slot->drv_data = pslot;
|
|
|
|
memset(pslot, 0, sizeof(*pslot));
|
2008-04-29 17:01:19 +00:00
|
|
|
if (_sc_add_reader(ctx, reader)) {
|
|
|
|
ret = SC_SUCCESS; /* silent ignore */
|
|
|
|
goto err1;
|
|
|
|
}
|
2002-02-24 19:32:14 +00:00
|
|
|
refresh_slot_attributes(reader, slot);
|
|
|
|
|
2008-04-29 17:01:19 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
err1:
|
|
|
|
if (priv != NULL) {
|
|
|
|
if (priv->reader_name)
|
|
|
|
free(priv->reader_name);
|
|
|
|
free(priv);
|
|
|
|
}
|
|
|
|
if (reader != NULL) {
|
|
|
|
if (reader->name)
|
|
|
|
free(reader->name);
|
|
|
|
free(reader);
|
|
|
|
}
|
|
|
|
if (slot != NULL)
|
|
|
|
free(pslot);
|
2008-03-06 16:06:59 +00:00
|
|
|
|
2008-04-29 17:01:19 +00:00
|
|
|
goto out;
|
2008-03-06 16:06:59 +00:00
|
|
|
}
|
2003-01-06 17:45:06 +00:00
|
|
|
|
2008-04-29 17:01:19 +00:00
|
|
|
ret = SC_SUCCESS;
|
2002-02-24 19:32:14 +00:00
|
|
|
|
2008-04-29 17:01:19 +00:00
|
|
|
out:
|
2002-02-24 21:14:17 +00:00
|
|
|
|
2008-04-29 17:01:19 +00:00
|
|
|
if (reader_buf != NULL)
|
|
|
|
free (reader_buf);
|
2005-08-13 13:26:46 +00:00
|
|
|
|
2008-10-26 19:13:14 +00:00
|
|
|
SC_FUNC_RETURN(ctx, 3, ret);
|
2002-02-24 19:32:14 +00:00
|
|
|
}
|
|
|
|
|
2005-01-24 18:30:55 +00:00
|
|
|
static int
|
2005-03-08 20:59:35 +00:00
|
|
|
pcsc_pin_cmd(sc_reader_t *reader, sc_slot_info_t * slot, struct sc_pin_cmd_data *data)
|
2005-01-24 18:30:55 +00:00
|
|
|
{
|
2005-01-29 10:44:07 +00:00
|
|
|
/* XXX: temporary */
|
2005-01-25 11:11:13 +00:00
|
|
|
if (slot->capabilities & SC_SLOT_CAP_PIN_PAD) {
|
2005-08-13 13:26:46 +00:00
|
|
|
return part10_pin_cmd(reader, slot, data);
|
2005-01-25 11:11:13 +00:00
|
|
|
} else {
|
|
|
|
return ctbcs_pin_cmd(reader, slot, data);
|
|
|
|
}
|
2005-01-24 18:30:55 +00:00
|
|
|
}
|
|
|
|
|
2003-12-18 16:35:28 +00:00
|
|
|
struct sc_reader_driver * sc_get_pcsc_driver(void)
|
2002-02-24 19:32:14 +00:00
|
|
|
{
|
|
|
|
pcsc_ops.init = pcsc_init;
|
|
|
|
pcsc_ops.finish = pcsc_finish;
|
2008-04-29 17:01:19 +00:00
|
|
|
pcsc_ops.detect_readers = pcsc_detect_readers;
|
2002-02-24 19:32:14 +00:00
|
|
|
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;
|
2005-01-24 18:30:55 +00:00
|
|
|
pcsc_ops.perform_verify = pcsc_pin_cmd;
|
2003-01-03 16:32:06 +00:00
|
|
|
pcsc_ops.wait_for_event = pcsc_wait_for_event;
|
2006-03-22 21:44:09 +00:00
|
|
|
pcsc_ops.reset = pcsc_reset;
|
2005-08-13 13:26:46 +00:00
|
|
|
|
2002-02-24 19:32:14 +00:00
|
|
|
return &pcsc_drv;
|
|
|
|
}
|
2005-03-09 12:44:31 +00:00
|
|
|
|
|
|
|
/*
|
2005-08-13 13:26:46 +00:00
|
|
|
* Pinpad support, based on PC/SC v2 Part 10 interface
|
2005-03-09 12:44:31 +00:00
|
|
|
* Similar to CCID in spirit.
|
|
|
|
*/
|
2005-08-13 13:26:46 +00:00
|
|
|
|
2005-03-09 12:44:31 +00:00
|
|
|
/* 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
|
|
|
|
|
2009-03-18 10:18:18 +00:00
|
|
|
/* Build a PIN verification block + APDU */
|
2005-08-13 13:26:46 +00:00
|
|
|
static int part10_build_verify_pin_block(u8 * buf, size_t * size, struct sc_pin_cmd_data *data)
|
2005-03-09 12:44:31 +00:00
|
|
|
{
|
2005-10-29 21:17:15 +00:00
|
|
|
int offset = 0, count = 0;
|
2005-03-09 12:44:31 +00:00
|
|
|
sc_apdu_t *apdu = data->apdu;
|
|
|
|
u8 tmp;
|
2005-11-25 19:11:09 +00:00
|
|
|
unsigned int tmp16;
|
2005-10-29 21:17:15 +00:00
|
|
|
PIN_VERIFY_STRUCTURE *pin_verify = (PIN_VERIFY_STRUCTURE *)buf;
|
|
|
|
|
2005-03-09 12:44:31 +00:00
|
|
|
/* PIN verification control message */
|
2005-10-29 21:17:15 +00:00
|
|
|
pin_verify->bTimerOut = SC_CCID_PIN_TIMEOUT;
|
|
|
|
pin_verify->bTimerOut2 = SC_CCID_PIN_TIMEOUT;
|
|
|
|
|
2005-03-09 12:44:31 +00:00
|
|
|
/* bmFormatString */
|
|
|
|
tmp = 0x00;
|
2005-08-13 13:26:46 +00:00
|
|
|
if (data->pin1.encoding == SC_PIN_ENCODING_ASCII) {
|
2005-03-09 12:44:31 +00:00
|
|
|
tmp |= SC_CCID_PIN_ENCODING_ASCII;
|
2005-08-13 13:26:46 +00:00
|
|
|
|
2009-03-18 10:18:18 +00:00
|
|
|
/* if the effective PIN length offset is specified, use it */
|
2005-10-29 21:17:15 +00:00
|
|
|
if (data->pin1.length_offset > 4) {
|
2005-08-13 13:26:46 +00:00
|
|
|
tmp |= SC_CCID_PIN_UNITS_BYTES;
|
2005-03-09 12:44:31 +00:00
|
|
|
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) {
|
2009-03-18 10:18:18 +00:00
|
|
|
/* see comment about GLP PINs in sec.c */
|
2005-08-13 13:26:46 +00:00
|
|
|
tmp |= SC_CCID_PIN_ENCODING_BCD;
|
2009-03-18 10:18:18 +00:00
|
|
|
tmp |= 0x08 << 3;
|
2005-03-09 12:44:31 +00:00
|
|
|
} else
|
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
|
2005-10-29 21:17:15 +00:00
|
|
|
pin_verify->bmFormatString = tmp;
|
2005-03-09 12:44:31 +00:00
|
|
|
|
|
|
|
/* bmPINBlockString */
|
|
|
|
tmp = 0x00;
|
|
|
|
if (data->pin1.encoding == SC_PIN_ENCODING_GLP) {
|
2009-03-18 10:18:18 +00:00
|
|
|
/* GLP PIN length is encoded in 4 bits and block size is always 8 bytes */
|
2005-03-09 12:44:31 +00:00
|
|
|
tmp |= 0x40 | 0x08;
|
2006-08-16 16:36:00 +00:00
|
|
|
} else if (data->pin1.encoding == SC_PIN_ENCODING_ASCII && data->pin1.pad_length) {
|
|
|
|
tmp |= data->pin1.pad_length;
|
2005-03-09 12:44:31 +00:00
|
|
|
}
|
2005-10-29 21:17:15 +00:00
|
|
|
pin_verify->bmPINBlockString = tmp;
|
2005-08-13 13:26:46 +00:00
|
|
|
|
2005-03-09 12:44:31 +00:00
|
|
|
/* bmPINLengthFormat */
|
|
|
|
tmp = 0x00;
|
|
|
|
if (data->pin1.encoding == SC_PIN_ENCODING_GLP) {
|
2009-03-18 10:18:18 +00:00
|
|
|
/* GLP PINs expect the effective PIN length from bit 4 */
|
2005-08-13 13:26:46 +00:00
|
|
|
tmp |= 0x04;
|
2005-03-09 12:44:31 +00:00
|
|
|
}
|
2005-10-29 21:17:15 +00:00
|
|
|
pin_verify->bmPINLengthFormat = tmp; /* bmPINLengthFormat */
|
2005-03-09 12:44:31 +00:00
|
|
|
|
|
|
|
if (!data->pin1.min_length || !data->pin1.max_length)
|
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
|
2005-10-29 21:17:15 +00:00
|
|
|
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 */
|
|
|
|
|
2009-03-19 17:54:45 +00:00
|
|
|
pin_verify->bNumberMessage = 0xFF; /* Default message */
|
|
|
|
|
2005-03-09 12:44:31 +00:00
|
|
|
/* Ignore language and T=1 parameters. */
|
2005-10-29 21:17:15 +00:00
|
|
|
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;
|
|
|
|
|
2005-03-09 12:44:31 +00:00
|
|
|
/* APDU itself */
|
2005-10-29 21:17:15 +00:00
|
|
|
pin_verify->abData[offset++] = apdu->cla;
|
|
|
|
pin_verify->abData[offset++] = apdu->ins;
|
|
|
|
pin_verify->abData[offset++] = apdu->p1;
|
|
|
|
pin_verify->abData[offset++] = apdu->p2;
|
2005-08-13 13:26:46 +00:00
|
|
|
|
2005-03-09 12:44:31 +00:00
|
|
|
/* Copy data if not Case 1 */
|
2005-04-07 19:45:39 +00:00
|
|
|
if (data->pin1.length_offset != 4) {
|
2006-08-16 16:36:00 +00:00
|
|
|
pin_verify->abData[offset++] = apdu->lc;
|
2005-10-29 21:17:15 +00:00
|
|
|
memcpy(&pin_verify->abData[offset], apdu->data, apdu->datalen);
|
|
|
|
offset += apdu->datalen;
|
2005-03-09 12:44:31 +00:00
|
|
|
}
|
|
|
|
|
2005-10-29 21:17:15 +00:00
|
|
|
pin_verify->ulDataLength = HOST_TO_CCID_32(offset); /* APDU size */
|
|
|
|
|
|
|
|
count = sizeof(PIN_VERIFY_STRUCTURE) + offset -1;
|
2005-03-09 12:44:31 +00:00
|
|
|
*size = count;
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-03-18 10:18:18 +00:00
|
|
|
/* Build a PIN modification block + APDU */
|
2005-08-13 13:26:46 +00:00
|
|
|
static int part10_build_modify_pin_block(u8 * buf, size_t * size, struct sc_pin_cmd_data *data)
|
2005-03-09 12:44:31 +00:00
|
|
|
{
|
2005-10-29 21:17:15 +00:00
|
|
|
int offset = 0, count = 0;
|
2005-03-09 12:44:31 +00:00
|
|
|
sc_apdu_t *apdu = data->apdu;
|
|
|
|
u8 tmp;
|
2005-11-25 19:11:09 +00:00
|
|
|
unsigned int tmp16;
|
2005-10-29 21:17:15 +00:00
|
|
|
PIN_MODIFY_STRUCTURE *pin_modify = (PIN_MODIFY_STRUCTURE *)buf;
|
2005-03-09 12:44:31 +00:00
|
|
|
|
|
|
|
/* PIN verification control message */
|
2005-10-29 21:17:15 +00:00
|
|
|
pin_modify->bTimerOut = SC_CCID_PIN_TIMEOUT; /* bTimeOut */
|
|
|
|
pin_modify->bTimerOut2 = SC_CCID_PIN_TIMEOUT; /* bTimeOut2 */
|
2005-08-13 13:26:46 +00:00
|
|
|
|
2005-03-09 12:44:31 +00:00
|
|
|
/* bmFormatString */
|
|
|
|
tmp = 0x00;
|
2005-08-13 13:26:46 +00:00
|
|
|
if (data->pin1.encoding == SC_PIN_ENCODING_ASCII) {
|
2005-03-09 12:44:31 +00:00
|
|
|
tmp |= SC_CCID_PIN_ENCODING_ASCII;
|
2005-08-13 13:26:46 +00:00
|
|
|
|
2009-03-18 10:18:18 +00:00
|
|
|
/* if the effective PIN length offset is specified, use it */
|
2005-10-29 21:17:15 +00:00
|
|
|
if (data->pin1.length_offset > 4) {
|
2005-08-13 13:26:46 +00:00
|
|
|
tmp |= SC_CCID_PIN_UNITS_BYTES;
|
2005-03-09 12:44:31 +00:00
|
|
|
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) {
|
2009-03-18 10:18:18 +00:00
|
|
|
/* see comment about GLP PINs in sec.c */
|
2005-08-13 13:26:46 +00:00
|
|
|
tmp |= SC_CCID_PIN_ENCODING_BCD;
|
2009-03-18 10:18:18 +00:00
|
|
|
tmp |= 0x08 << 3;
|
2005-03-09 12:44:31 +00:00
|
|
|
} else
|
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
|
2005-10-29 21:17:15 +00:00
|
|
|
pin_modify->bmFormatString = tmp; /* bmFormatString */
|
2005-03-09 12:44:31 +00:00
|
|
|
|
|
|
|
/* bmPINBlockString */
|
|
|
|
tmp = 0x00;
|
|
|
|
if (data->pin1.encoding == SC_PIN_ENCODING_GLP) {
|
2009-03-18 10:18:18 +00:00
|
|
|
/* GLP PIN length is encoded in 4 bits and block size is always 8 bytes */
|
2005-03-09 12:44:31 +00:00
|
|
|
tmp |= 0x40 | 0x08;
|
2006-08-16 16:36:00 +00:00
|
|
|
} else if (data->pin1.encoding == SC_PIN_ENCODING_ASCII && data->pin1.pad_length) {
|
|
|
|
tmp |= data->pin1.pad_length;
|
2005-03-09 12:44:31 +00:00
|
|
|
}
|
2005-10-29 21:17:15 +00:00
|
|
|
pin_modify->bmPINBlockString = tmp; /* bmPINBlockString */
|
2005-08-13 13:26:46 +00:00
|
|
|
|
2005-03-09 12:44:31 +00:00
|
|
|
/* bmPINLengthFormat */
|
|
|
|
tmp = 0x00;
|
|
|
|
if (data->pin1.encoding == SC_PIN_ENCODING_GLP) {
|
2009-03-18 10:18:18 +00:00
|
|
|
/* GLP PINs expect the effective PIN length from bit 4 */
|
2005-08-13 13:26:46 +00:00
|
|
|
tmp |= 0x04;
|
2005-03-09 12:44:31 +00:00
|
|
|
}
|
2005-10-29 21:17:15 +00:00
|
|
|
pin_modify->bmPINLengthFormat = tmp; /* bmPINLengthFormat */
|
2005-08-13 13:26:46 +00:00
|
|
|
|
2005-10-29 21:17:15 +00:00
|
|
|
pin_modify->bInsertionOffsetOld = 0x00; /* bOffsetOld */
|
2009-03-18 10:18:18 +00:00
|
|
|
|
|
|
|
/* bInsertionOffsetNew */
|
|
|
|
tmp = 0x00;
|
|
|
|
if (data->pin1.encoding == SC_PIN_ENCODING_GLP) {
|
|
|
|
tmp = 0x08;
|
|
|
|
}
|
|
|
|
pin_modify->bInsertionOffsetNew = tmp; /* bOffsetNew */
|
2005-08-13 13:26:46 +00:00
|
|
|
|
2005-03-09 12:44:31 +00:00
|
|
|
if (!data->pin1.min_length || !data->pin1.max_length)
|
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
2005-10-29 21:17:15 +00:00
|
|
|
|
|
|
|
tmp16 = (data->pin1.min_length << 8 ) + data->pin1.max_length;
|
|
|
|
pin_modify->wPINMaxExtraDigit = HOST_TO_CCID_16(tmp16); /* Min Max */
|
2005-03-09 12:44:31 +00:00
|
|
|
|
2005-10-29 21:17:15 +00:00
|
|
|
pin_modify->bConfirmPIN = 0x03; /* bConfirmPIN, all */
|
|
|
|
pin_modify->bEntryValidationCondition = 0x02; /* bEntryValidationCondition, keypress only */
|
2005-03-09 12:44:31 +00:00
|
|
|
|
|
|
|
/* Ignore language and T=1 parameters. */
|
2009-03-19 17:54:45 +00:00
|
|
|
pin_modify->bNumberMessage = 0x00; /* XXX: Latest released CCID driver rejects 0xFF */
|
2005-10-29 21:17:15 +00:00
|
|
|
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;
|
|
|
|
|
2005-03-09 12:44:31 +00:00
|
|
|
/* APDU itself */
|
2005-10-29 21:17:15 +00:00
|
|
|
pin_modify->abData[offset++] = apdu->cla;
|
|
|
|
pin_modify->abData[offset++] = apdu->ins;
|
|
|
|
pin_modify->abData[offset++] = apdu->p1;
|
|
|
|
pin_modify->abData[offset++] = apdu->p2;
|
2005-08-13 13:26:46 +00:00
|
|
|
|
2005-10-29 21:17:15 +00:00
|
|
|
/* Copy data if not Case 1 */
|
2005-04-07 19:45:39 +00:00
|
|
|
if (data->pin1.length_offset != 4) {
|
2006-08-16 16:36:00 +00:00
|
|
|
pin_modify->abData[offset++] = apdu->lc;
|
2005-10-29 21:17:15 +00:00
|
|
|
memcpy(&pin_modify->abData[offset], apdu->data, apdu->datalen);
|
|
|
|
offset += apdu->datalen;
|
2005-03-09 12:44:31 +00:00
|
|
|
}
|
|
|
|
|
2005-10-29 21:17:15 +00:00
|
|
|
pin_modify->ulDataLength = HOST_TO_CCID_32(offset); /* APDU size */
|
|
|
|
|
|
|
|
count = sizeof(PIN_MODIFY_STRUCTURE) + offset -1;
|
2005-03-09 12:44:31 +00:00
|
|
|
*size = count;
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Do the PIN command */
|
|
|
|
static int
|
2005-08-13 13:26:46 +00:00
|
|
|
part10_pin_cmd(sc_reader_t *reader, sc_slot_info_t *slot,
|
2005-03-09 12:44:31 +00:00
|
|
|
struct sc_pin_cmd_data *data)
|
|
|
|
{
|
2008-03-06 16:06:59 +00:00
|
|
|
struct pcsc_private_data *priv = GET_PRIV_DATA(reader);
|
2005-09-12 21:09:12 +00:00
|
|
|
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE], sbuf[SC_MAX_APDU_BUFFER_SIZE];
|
|
|
|
char dbuf[SC_MAX_APDU_BUFFER_SIZE * 3];
|
2005-03-09 12:44:31 +00:00
|
|
|
size_t rcount = sizeof(rbuf), scount = 0;
|
|
|
|
int r;
|
2005-04-07 19:45:39 +00:00
|
|
|
DWORD ioctl = 0;
|
2005-03-09 12:44:31 +00:00
|
|
|
sc_apdu_t *apdu;
|
|
|
|
struct pcsc_slot_data *pslot = (struct pcsc_slot_data *) slot->drv_data;
|
2005-08-13 13:26:46 +00:00
|
|
|
|
2005-03-09 12:44:31 +00:00
|
|
|
SC_FUNC_CALLED(reader->ctx, 3);
|
2005-08-13 13:26:46 +00:00
|
|
|
assert(pslot != NULL);
|
|
|
|
|
2008-03-06 16:06:59 +00:00
|
|
|
if (priv->gpriv->SCardControl == NULL)
|
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
|
2005-03-09 12:44:31 +00:00
|
|
|
/* The APDU must be provided by the card driver */
|
|
|
|
if (!data->apdu) {
|
2009-03-19 17:54:45 +00:00
|
|
|
sc_error(reader->ctx, "No APDU provided for PC/SC v2 pinpad verification!");
|
2005-03-09 12:44:31 +00:00
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
}
|
2005-08-13 13:26:46 +00:00
|
|
|
|
2005-03-09 12:44:31 +00:00
|
|
|
apdu = data->apdu;
|
|
|
|
switch (data->cmd) {
|
|
|
|
case SC_PIN_CMD_VERIFY:
|
2006-08-16 16:36:00 +00:00
|
|
|
if (!(pslot->verify_ioctl || (pslot->verify_ioctl_start && pslot->verify_ioctl_finish))) {
|
2005-03-09 12:44:31 +00:00
|
|
|
sc_error(reader->ctx, "Pinpad reader does not support verification!");
|
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
}
|
2005-08-13 13:26:46 +00:00
|
|
|
r = part10_build_verify_pin_block(sbuf, &scount, data);
|
2006-08-16 16:36:00 +00:00
|
|
|
ioctl = pslot->verify_ioctl ? pslot->verify_ioctl : pslot->verify_ioctl_start;
|
2005-03-09 12:44:31 +00:00
|
|
|
break;
|
|
|
|
case SC_PIN_CMD_CHANGE:
|
|
|
|
case SC_PIN_CMD_UNBLOCK:
|
2006-08-16 16:36:00 +00:00
|
|
|
if (!(pslot->modify_ioctl || (pslot->modify_ioctl_start && pslot->modify_ioctl_finish))) {
|
2005-03-09 12:44:31 +00:00
|
|
|
sc_error(reader->ctx, "Pinpad reader does not support modification!");
|
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
}
|
2005-08-13 13:26:46 +00:00
|
|
|
r = part10_build_modify_pin_block(sbuf, &scount, data);
|
2006-08-16 16:36:00 +00:00
|
|
|
ioctl = pslot->modify_ioctl ? pslot->modify_ioctl : pslot->modify_ioctl_start;
|
2005-03-09 12:44:31 +00:00
|
|
|
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 */
|
2009-03-19 17:54:45 +00:00
|
|
|
SC_TEST_RET(reader->ctx, r, "PC/SC v2 pinpad block building failed!");
|
2005-03-09 12:44:31 +00:00
|
|
|
/* If not, debug it, just for fun */
|
|
|
|
sc_bin_to_hex(sbuf, scount, dbuf, sizeof(dbuf), ':');
|
2009-03-19 17:54:45 +00:00
|
|
|
sc_debug(reader->ctx, "PC/SC v2 pinpad block: %s", dbuf);
|
2005-08-13 13:26:46 +00:00
|
|
|
|
2006-02-17 11:22:21 +00:00
|
|
|
r = pcsc_internal_transmit(reader, slot, sbuf, scount, rbuf, &rcount, ioctl);
|
2005-03-09 12:44:31 +00:00
|
|
|
|
2009-03-19 17:54:45 +00:00
|
|
|
SC_TEST_RET(reader->ctx, r, "PC/SC v2 pinpad: block transmit failed!");
|
2008-04-28 07:57:03 +00:00
|
|
|
/* finish the call if it was a two-phase operation */
|
2006-08-16 16:36:00 +00:00
|
|
|
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);
|
2009-03-19 17:54:45 +00:00
|
|
|
SC_TEST_RET(reader->ctx, r, "PC/SC v2 pinpad: finish operation failed!");
|
2006-08-16 16:36:00 +00:00
|
|
|
}
|
2005-08-13 13:26:46 +00:00
|
|
|
|
2005-03-09 12:44:31 +00:00
|
|
|
/* 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);
|
|
|
|
}
|
2005-08-13 13:26:46 +00:00
|
|
|
|
2005-03-09 12:44:31 +00:00
|
|
|
/* Extract the SWs for the result APDU */
|
|
|
|
apdu->sw1 = (unsigned int) rbuf[rcount - 2];
|
|
|
|
apdu->sw2 = (unsigned int) rbuf[rcount - 1];
|
2005-08-13 13:26:46 +00:00
|
|
|
|
2005-03-09 12:44:31 +00:00
|
|
|
r = SC_SUCCESS;
|
|
|
|
switch (((unsigned int) apdu->sw1 << 8) | apdu->sw2) {
|
2005-03-24 16:54:10 +00:00
|
|
|
case 0x6400: /* Input timed out */
|
2005-03-09 12:44:31 +00:00
|
|
|
r = SC_ERROR_KEYPAD_TIMEOUT;
|
|
|
|
break;
|
|
|
|
case 0x6401: /* Input cancelled */
|
|
|
|
r = SC_ERROR_KEYPAD_CANCELLED;
|
|
|
|
break;
|
2005-03-24 16:54:10 +00:00
|
|
|
case 0x6402: /* PINs don't match */
|
|
|
|
r = SC_ERROR_KEYPAD_PIN_MISMATCH;
|
|
|
|
break;
|
2005-03-09 12:44:31 +00:00
|
|
|
}
|
2005-08-13 13:26:46 +00:00
|
|
|
|
2005-03-09 12:44:31 +00:00
|
|
|
SC_TEST_RET(reader->ctx, r, "PIN command failed");
|
2005-08-13 13:26:46 +00:00
|
|
|
|
2005-03-09 12:44:31 +00:00
|
|
|
/* PIN command completed, all is good */
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
#endif /* HAVE_PCSC */
|
|
|
|
|