391 lines
12 KiB
C
391 lines
12 KiB
C
|
|
#include "../globals.h"
|
||
|
|
|
||
|
|
#ifdef CARDREADER_PCSC
|
||
|
|
|
||
|
|
#include "atr.h"
|
||
|
|
#include "../oscam-string.h"
|
||
|
|
|
||
|
|
#if defined(__CYGWIN__)
|
||
|
|
#define __reserved
|
||
|
|
#define __nullnullterminated
|
||
|
|
#include <specstrings.h>
|
||
|
|
#include <WinSCard.h>
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if !defined(__CYGWIN__) && !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
|
||
|
|
#include <PCSC/pcsclite.h>
|
||
|
|
#include <PCSC/winscard.h>
|
||
|
|
#include <PCSC/wintypes.h>
|
||
|
|
#include <PCSC/reader.h>
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||
|
|
#include "pcsclite.h"
|
||
|
|
#include "winscard.h"
|
||
|
|
#include "wintypes.h"
|
||
|
|
#include "reader.h"
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifndef ERR_INVALID
|
||
|
|
#define ERR_INVALID -1
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if defined(__CYGWIN__)
|
||
|
|
#undef OK
|
||
|
|
#undef ERROR
|
||
|
|
#undef LOBYTE
|
||
|
|
#undef HIBYTE
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#define OK 0
|
||
|
|
#define ERROR 1
|
||
|
|
|
||
|
|
struct pcsc_data
|
||
|
|
{
|
||
|
|
bool pcsc_has_card;
|
||
|
|
char pcsc_name[128];
|
||
|
|
SCARDCONTEXT hContext;
|
||
|
|
SCARDHANDLE hCard;
|
||
|
|
DWORD dwActiveProtocol;
|
||
|
|
};
|
||
|
|
|
||
|
|
static int32_t pcsc_init(struct s_reader *pcsc_reader)
|
||
|
|
{
|
||
|
|
ULONG rv;
|
||
|
|
DWORD dwReaders = 0;
|
||
|
|
LPSTR mszReaders = NULL;
|
||
|
|
char *ptr, **readers = NULL;
|
||
|
|
char *device = pcsc_reader->device;
|
||
|
|
int32_t nbReaders;
|
||
|
|
int32_t reader_nb;
|
||
|
|
|
||
|
|
rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC establish context for PCSC pcsc_reader %s", device);
|
||
|
|
SCARDCONTEXT hContext;
|
||
|
|
memset(&hContext, 0, sizeof(hContext));
|
||
|
|
rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
|
||
|
|
if(rv == SCARD_S_SUCCESS)
|
||
|
|
{
|
||
|
|
if(!cs_malloc(&pcsc_reader->crdr_data, sizeof(struct pcsc_data)))
|
||
|
|
{ return ERROR; }
|
||
|
|
struct pcsc_data *crdr_data = pcsc_reader->crdr_data;
|
||
|
|
crdr_data->hContext = hContext;
|
||
|
|
|
||
|
|
// here we need to list the pcsc readers and get the name from there,
|
||
|
|
// the pcsc_reader->device should contain the pcsc_reader number
|
||
|
|
// and after the actual device name is copied in crdr_data->pcsc_name .
|
||
|
|
rv = SCardListReaders(crdr_data->hContext, NULL, NULL, &dwReaders);
|
||
|
|
if(rv != SCARD_S_SUCCESS)
|
||
|
|
{
|
||
|
|
rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC failed listing readers [1] : (%lx)", (unsigned long)rv);
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
if(!cs_malloc(&mszReaders, dwReaders))
|
||
|
|
{ return ERROR; }
|
||
|
|
rv = SCardListReaders(crdr_data->hContext, NULL, mszReaders, &dwReaders);
|
||
|
|
if(rv != SCARD_S_SUCCESS)
|
||
|
|
{
|
||
|
|
rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC failed listing readers [2]: (%lx)", (unsigned long)rv);
|
||
|
|
NULLFREE(mszReaders);
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
/* Extract readers from the null separated string and get the total
|
||
|
|
* number of readers
|
||
|
|
*/
|
||
|
|
nbReaders = 0;
|
||
|
|
ptr = mszReaders;
|
||
|
|
while(*ptr != '\0')
|
||
|
|
{
|
||
|
|
ptr += cs_strlen(ptr) + 1;
|
||
|
|
nbReaders++;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(nbReaders == 0)
|
||
|
|
{
|
||
|
|
rdr_log(pcsc_reader, "PCSC : no pcsc_reader found");
|
||
|
|
NULLFREE(mszReaders);
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(!cs_malloc(&readers, nbReaders * sizeof(char *)))
|
||
|
|
{
|
||
|
|
NULLFREE(mszReaders);
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
char* device_line;
|
||
|
|
char* device_first;
|
||
|
|
char* device_second;
|
||
|
|
|
||
|
|
device_line = strdup((const char *)&pcsc_reader->device);
|
||
|
|
device_first = strsep(&device_line, ":");
|
||
|
|
device_second = strsep(&device_line, ":");
|
||
|
|
reader_nb = atoi(device_first);
|
||
|
|
|
||
|
|
/* fill the readers table */
|
||
|
|
nbReaders = 0;
|
||
|
|
ptr = mszReaders;
|
||
|
|
while(*ptr != '\0')
|
||
|
|
{
|
||
|
|
rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC pcsc_reader %d: %s", nbReaders, ptr);
|
||
|
|
readers[nbReaders] = ptr;
|
||
|
|
if ((reader_nb == -1) && (device_second != NULL) && strstr(ptr,device_second)){
|
||
|
|
reader_nb = nbReaders;
|
||
|
|
}
|
||
|
|
ptr += cs_strlen(ptr) + 1;
|
||
|
|
nbReaders++;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(reader_nb < 0 || reader_nb >= nbReaders)
|
||
|
|
{
|
||
|
|
rdr_log(pcsc_reader, "Wrong pcsc_reader index: %d", reader_nb);
|
||
|
|
NULLFREE(mszReaders);
|
||
|
|
NULLFREE(readers);
|
||
|
|
NULLFREE(device_line);
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (readers)
|
||
|
|
{
|
||
|
|
snprintf(crdr_data->pcsc_name, sizeof(crdr_data->pcsc_name), "%s", readers[reader_nb]);
|
||
|
|
NULLFREE(readers);
|
||
|
|
}
|
||
|
|
NULLFREE(mszReaders);
|
||
|
|
NULLFREE(device_line);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
rdr_log(pcsc_reader, "PCSC failed establish context (%lx)", (unsigned long)rv);
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
return OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t pcsc_do_api(struct s_reader *pcsc_reader, const uint8_t *buf, uint8_t *cta_res, uint16_t *cta_lr, int32_t l)
|
||
|
|
{
|
||
|
|
LONG rv;
|
||
|
|
DWORD dwSendLength, dwRecvLength;
|
||
|
|
|
||
|
|
*cta_lr = 0;
|
||
|
|
if(!l)
|
||
|
|
{
|
||
|
|
rdr_log(pcsc_reader, "ERROR: Data length to be send to the pcsc_reader is %d", l);
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
char tmp[l * 3];
|
||
|
|
dwRecvLength = CTA_RES_LEN;
|
||
|
|
|
||
|
|
struct pcsc_data *crdr_data = pcsc_reader->crdr_data;
|
||
|
|
|
||
|
|
if(pcsc_reader->resetalways)
|
||
|
|
{
|
||
|
|
SCardReconnect(crdr_data->hCard, SCARD_SHARE_EXCLUSIVE, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, SCARD_RESET_CARD, &crdr_data->dwActiveProtocol);
|
||
|
|
}
|
||
|
|
|
||
|
|
if(crdr_data->dwActiveProtocol == SCARD_PROTOCOL_T0)
|
||
|
|
{
|
||
|
|
// explanantion as to why we do the test on buf[4] :
|
||
|
|
// Issuing a command without exchanging data :
|
||
|
|
//To issue a command to the card that does not involve the exchange of data (either sent or received), the send and receive buffers must be formatted as follows.
|
||
|
|
//The pbSendBuffer buffer must contain the CLA, INS, P1, and P2 values for the T=0 operation. The P3 value is not sent. (This is to differentiate the header from the case where 256 bytes are expected to be returned.)
|
||
|
|
//The cbSendLength parameter must be set to four, the size of the T=0 header information (CLA, INS, P1, and P2).
|
||
|
|
//The pbRecvBuffer will receive the SW1 and SW2 status codes from the operation.
|
||
|
|
//The pcbRecvLength should be at least two and will be set to two upon return.
|
||
|
|
if(buf[4])
|
||
|
|
{ dwSendLength = l; }
|
||
|
|
else
|
||
|
|
{ dwSendLength = l - 1; }
|
||
|
|
rdr_log_dbg(pcsc_reader, D_DEVICE, "sending %lu bytes to PCSC : %s", (unsigned long)dwSendLength, cs_hexdump(1, buf, l, tmp, sizeof(tmp)));
|
||
|
|
rv = SCardTransmit(crdr_data->hCard, SCARD_PCI_T0, (LPCBYTE) buf, dwSendLength, NULL, (LPBYTE) cta_res, (LPDWORD) &dwRecvLength);
|
||
|
|
*cta_lr = dwRecvLength;
|
||
|
|
}
|
||
|
|
else if(crdr_data->dwActiveProtocol == SCARD_PROTOCOL_T1)
|
||
|
|
{
|
||
|
|
dwSendLength = l;
|
||
|
|
rdr_log_dbg(pcsc_reader, D_DEVICE, "sending %lu bytes to PCSC : %s", (unsigned long)dwSendLength, cs_hexdump(1, buf, l, tmp, sizeof(tmp)));
|
||
|
|
rv = SCardTransmit(crdr_data->hCard, SCARD_PCI_T1, (LPCBYTE) buf, dwSendLength, NULL, (LPBYTE) cta_res, (LPDWORD) &dwRecvLength);
|
||
|
|
*cta_lr = dwRecvLength;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC invalid protocol (T=%lu)", (unsigned long)crdr_data->dwActiveProtocol);
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
rdr_log_dbg(pcsc_reader, D_DEVICE, "received %d bytes from PCSC with rv=%lx : %s", *cta_lr, (unsigned long)rv, cs_hexdump(1, cta_res, *cta_lr, tmp, sizeof(tmp)));
|
||
|
|
|
||
|
|
rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC doapi (%lx ) (T=%d), %d", (unsigned long)rv, (crdr_data->dwActiveProtocol == SCARD_PROTOCOL_T0 ? 0 : 1), *cta_lr);
|
||
|
|
|
||
|
|
if(rv == SCARD_S_SUCCESS)
|
||
|
|
{
|
||
|
|
return OK;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t pcsc_activate_card(struct s_reader *pcsc_reader, uint8_t *atr, uint16_t *atr_size)
|
||
|
|
{
|
||
|
|
struct pcsc_data *crdr_data = pcsc_reader->crdr_data;
|
||
|
|
LONG rv;
|
||
|
|
DWORD dwState, dwAtrLen, dwReaderLen;
|
||
|
|
unsigned char pbAtr[ATR_MAX_SIZE];
|
||
|
|
char tmp[sizeof(pbAtr) * 3 + 1];
|
||
|
|
|
||
|
|
rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC initializing card in (%s)", crdr_data->pcsc_name);
|
||
|
|
dwAtrLen = sizeof(pbAtr);
|
||
|
|
dwReaderLen = 0;
|
||
|
|
|
||
|
|
rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC resetting card in (%s) with handle %" PRIuPTR, crdr_data->pcsc_name, (uintptr_t)crdr_data->hCard);
|
||
|
|
rv = SCardReconnect(crdr_data->hCard, SCARD_SHARE_EXCLUSIVE, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, SCARD_RESET_CARD, &crdr_data->dwActiveProtocol);
|
||
|
|
|
||
|
|
if(rv != SCARD_S_SUCCESS)
|
||
|
|
{
|
||
|
|
rdr_log_dbg(pcsc_reader, D_DEVICE, "ERROR: PCSC failed to reset card (%lx)", (unsigned long)rv);
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC resetting done on card in (%s)", crdr_data->pcsc_name);
|
||
|
|
rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC Protocol (T=%d)", (crdr_data->dwActiveProtocol == SCARD_PROTOCOL_T0 ? 0 : 1));
|
||
|
|
|
||
|
|
rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC getting ATR for card in (%s)", crdr_data->pcsc_name);
|
||
|
|
rv = SCardStatus(crdr_data->hCard, NULL, &dwReaderLen, &dwState, &crdr_data->dwActiveProtocol, pbAtr, &dwAtrLen);
|
||
|
|
if(rv == SCARD_S_SUCCESS)
|
||
|
|
{
|
||
|
|
rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC Protocol (T=%d)", (crdr_data->dwActiveProtocol == SCARD_PROTOCOL_T0 ? 0 : 1));
|
||
|
|
memcpy(atr, pbAtr, dwAtrLen);
|
||
|
|
*atr_size = dwAtrLen;
|
||
|
|
|
||
|
|
rdr_log(pcsc_reader, "ATR: %s", cs_hexdump(1, (uint8_t *)pbAtr, dwAtrLen, tmp, sizeof(tmp)));
|
||
|
|
memcpy(pcsc_reader->card_atr, pbAtr, dwAtrLen);
|
||
|
|
pcsc_reader->card_atr_length = dwAtrLen;
|
||
|
|
return OK;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
rdr_log_dbg(pcsc_reader, D_DEVICE, "ERROR: PCSC failed to get ATR for card (%lx)", (unsigned long)rv);
|
||
|
|
}
|
||
|
|
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t pcsc_activate(struct s_reader *reader, struct s_ATR *atr)
|
||
|
|
{
|
||
|
|
unsigned char atrarr[ATR_MAX_SIZE];
|
||
|
|
uint16_t atr_size = 0;
|
||
|
|
if(pcsc_activate_card(reader, atrarr, &atr_size) == OK)
|
||
|
|
{
|
||
|
|
if(ATR_InitFromArray(atr, atrarr, atr_size) != ERROR) // ATR is OK or softfail malformed
|
||
|
|
{ return OK; }
|
||
|
|
else
|
||
|
|
{ return ERROR; }
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{ return ERROR; }
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t pcsc_check_card_inserted(struct s_reader *pcsc_reader)
|
||
|
|
{
|
||
|
|
struct pcsc_data *crdr_data = pcsc_reader->crdr_data;
|
||
|
|
DWORD dwState, dwAtrLen, dwReaderLen;
|
||
|
|
unsigned char pbAtr[64];
|
||
|
|
SCARDHANDLE rv;
|
||
|
|
|
||
|
|
dwAtrLen = sizeof(pbAtr);
|
||
|
|
rv = 0;
|
||
|
|
dwState = 0;
|
||
|
|
dwReaderLen = 0;
|
||
|
|
|
||
|
|
// Do we have a card ?
|
||
|
|
if(!crdr_data->pcsc_has_card && !crdr_data->hCard)
|
||
|
|
{
|
||
|
|
// try connecting to the card
|
||
|
|
rv = SCardConnect(crdr_data->hContext, crdr_data->pcsc_name, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &crdr_data->hCard, &crdr_data->dwActiveProtocol);
|
||
|
|
if(rv == (SCARDHANDLE)SCARD_E_NO_SMARTCARD)
|
||
|
|
{
|
||
|
|
// no card in pcsc_reader
|
||
|
|
crdr_data->pcsc_has_card = 0;
|
||
|
|
if(crdr_data->hCard)
|
||
|
|
{
|
||
|
|
SCardDisconnect(crdr_data->hCard, SCARD_RESET_CARD);
|
||
|
|
crdr_data->hCard = 0;
|
||
|
|
}
|
||
|
|
// rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC card in %s removed / absent [dwstate=%lx rv=(%lx)]", crdr_data->pcsc_name, dwState, (unsigned long)rv );
|
||
|
|
return OK;
|
||
|
|
}
|
||
|
|
else if(rv == (SCARDHANDLE)SCARD_W_UNRESPONSIVE_CARD)
|
||
|
|
{
|
||
|
|
// there is a problem with the card in the pcsc_reader
|
||
|
|
crdr_data->pcsc_has_card = 0;
|
||
|
|
crdr_data->hCard = 0;
|
||
|
|
rdr_log(pcsc_reader, "PCSC card in %s is unresponsive. Eject and re-insert please.", crdr_data->pcsc_name);
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
else if(rv == SCARD_S_SUCCESS)
|
||
|
|
{
|
||
|
|
// we have a card
|
||
|
|
crdr_data->pcsc_has_card = 1;
|
||
|
|
rdr_log(pcsc_reader, "PCSC was opened with handle: %" PRIuPTR, (uintptr_t)crdr_data->hCard);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
// if we get here we have a bigger problem -> display status and debug
|
||
|
|
// rdr_log_dbg(pcsc_reader, D_DEVICE, "PCSC pcsc_reader %s status [dwstate=%lx rv=(%lx)]", crdr_data->pcsc_name, dwState, (unsigned long)rv );
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
// if we get there the card is ready, check its status
|
||
|
|
rv = SCardStatus(crdr_data->hCard, NULL, &dwReaderLen, &dwState, &crdr_data->dwActiveProtocol, pbAtr, &dwAtrLen);
|
||
|
|
|
||
|
|
if(rv == SCARD_S_SUCCESS && (dwState & (SCARD_PRESENT | SCARD_NEGOTIABLE | SCARD_POWERED)))
|
||
|
|
{
|
||
|
|
return OK;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
SCardDisconnect(crdr_data->hCard, SCARD_RESET_CARD);
|
||
|
|
crdr_data->hCard = 0;
|
||
|
|
crdr_data->pcsc_has_card = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t pcsc_get_status(struct s_reader *reader, int32_t *in)
|
||
|
|
{
|
||
|
|
struct pcsc_data *crdr_data = reader->crdr_data;
|
||
|
|
int32_t ret = pcsc_check_card_inserted(reader);
|
||
|
|
*in = crdr_data->pcsc_has_card;
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t pcsc_close(struct s_reader *pcsc_reader)
|
||
|
|
{
|
||
|
|
struct pcsc_data *crdr_data = pcsc_reader->crdr_data;
|
||
|
|
rdr_log_dbg(pcsc_reader, D_IFD, "PCSC : Closing device %s", pcsc_reader->device);
|
||
|
|
SCardDisconnect(crdr_data->hCard, SCARD_LEAVE_CARD);
|
||
|
|
SCardReleaseContext(crdr_data->hContext);
|
||
|
|
return OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
const struct s_cardreader cardreader_pcsc =
|
||
|
|
{
|
||
|
|
.desc = "pcsc",
|
||
|
|
.typ = R_PCSC,
|
||
|
|
.skip_extra_atr_parsing = 1,
|
||
|
|
.skip_t1_command_retries = 1,
|
||
|
|
.skip_setting_ifsc = 1,
|
||
|
|
.reader_init = pcsc_init,
|
||
|
|
.get_status = pcsc_get_status,
|
||
|
|
.activate = pcsc_activate,
|
||
|
|
.card_write = pcsc_do_api,
|
||
|
|
.close = pcsc_close,
|
||
|
|
};
|
||
|
|
|
||
|
|
#endif
|