allow multiple cards to connect to vpcd

due to limitations of pcscd this is currently limited to 10 cards.
Note that there is a double free corruption in pcscd when unloading the
driver.
This commit is contained in:
Frank Morgner
2013-06-25 00:14:11 +02:00
parent 2dc76bdac6
commit fb4dbe59ba
5 changed files with 151 additions and 80 deletions

View File

@@ -37,9 +37,10 @@ Currently the Virtual Smart Card supports the following types of smart cards:
The |vpcd| is a smart card driver for PCSC-Lite_. It allows The |vpcd| is a smart card driver for PCSC-Lite_. It allows
smart card applications to access the |vpicc| through the PC/SC API. By smart card applications to access the |vpicc| through the PC/SC API. By
default |vpicc| communicates with |vpcd| through a socket on localhost port default |vpcd| opens slots for communication with multiple |vpicc|'s on
35963. But the |vpicc| does not need to run on the same machine as the |vpcd|, localhost from port 35963 to port 35972. But the |vpicc| does not need to run
they can connect over the internet for example. on the same machine as the |vpcd|, they can connect over the internet for
example.
Although the Virtual Smart Card is a software emulator, you can use Although the Virtual Smart Card is a software emulator, you can use
:ref:`pcsc-relay` to make it accessible to an external contact-less smart card :ref:`pcsc-relay` to make it accessible to an external contact-less smart card

View File

@@ -37,9 +37,10 @@ Currently the @PACKAGE_NAME@ supports the following types of smart cards:
The |vpcd| is a smart card driver for PCSC-Lite_. It allows The |vpcd| is a smart card driver for PCSC-Lite_. It allows
smart card applications to access the |vpicc| through the PC/SC API. By smart card applications to access the |vpicc| through the PC/SC API. By
default |vpicc| communicates with |vpcd| through a socket on localhost port default |vpcd| opens slots for communication with multiple |vpicc|'s on
35963. But the |vpicc| does not need to run on the same machine as the |vpcd|, localhost from port 35963 to port 35972. But the |vpicc| does not need to run
they can connect over the internet for example. on the same machine as the |vpcd|, they can connect over the internet for
example.
Although the @PACKAGE_NAME@ is a software emulator, you can use Although the @PACKAGE_NAME@ is a software emulator, you can use
:ref:`pcsc-relay` to make it accessible to an external contact-less smart card :ref:`pcsc-relay` to make it accessible to an external contact-less smart card

View File

@@ -18,14 +18,28 @@
#include "vpcd.h" #include "vpcd.h"
/* pcscd allows at most 16 readers. We will use 10.
* See PCSCLITE_MAX_READERS_CONTEXTS in pcsclite.h */
#define VICC_MAX_SLOTS \
(PCSCLITE_MAX_READERS_CONTEXTS > 6 ? \
PCSCLITE_MAX_READERS_CONTEXTS-6 : \
1)
static struct vicc_ctx *ctx[VICC_MAX_SLOTS];
RESPONSECODE RESPONSECODE
IFDHCreateChannel (DWORD Lun, DWORD Channel) IFDHCreateChannel (DWORD Lun, DWORD Channel)
{ {
if (vicc_init(Channel) < 0) { size_t slot = Lun & 0xffff;
if (slot >= VICC_MAX_SLOTS) {
return IFD_COMMUNICATION_ERROR;
}
ctx[slot] = vicc_init(Channel+slot);
if (!ctx[slot]) {
Log1(PCSC_LOG_ERROR, "Could not initialize connection to virtual ICC"); Log1(PCSC_LOG_ERROR, "Could not initialize connection to virtual ICC");
return IFD_COMMUNICATION_ERROR; return IFD_COMMUNICATION_ERROR;
} }
Log2(PCSC_LOG_INFO, "Waiting for virtual ICC on port %hu", (unsigned short) Channel); Log2(PCSC_LOG_INFO, "Waiting for virtual ICC on port %hu", (unsigned short) Channel+slot);
return IFD_SUCCESS; return IFD_SUCCESS;
} }
@@ -66,13 +80,19 @@ IFDHControl(DWORD Lun, PUCHAR TxBuffer, DWORD TxLength, PUCHAR RxBuffer,
RESPONSECODE RESPONSECODE
IFDHCloseChannel (DWORD Lun) IFDHCloseChannel (DWORD Lun)
{ {
RESPONSECODE r = vicc_eject(); size_t slot = Lun & 0xffff;
if (vicc_exit() < 0) { if (slot >= VICC_MAX_SLOTS) {
return IFD_COMMUNICATION_ERROR;
}
Log2(PCSC_LOG_ERROR, "Slot %u", slot);
if (vicc_exit(ctx[slot]) < 0) {
Log1(PCSC_LOG_ERROR, "Could not close connection to virtual ICC"); Log1(PCSC_LOG_ERROR, "Could not close connection to virtual ICC");
return IFD_COMMUNICATION_ERROR; return IFD_COMMUNICATION_ERROR;
} }
ctx[slot] = NULL;
Log2(PCSC_LOG_ERROR, "Slot %u", slot);
return r; return IFD_SUCCESS;
} }
RESPONSECODE RESPONSECODE
@@ -80,6 +100,10 @@ IFDHGetCapabilities (DWORD Lun, DWORD Tag, PDWORD Length, PUCHAR Value)
{ {
unsigned char *atr = NULL; unsigned char *atr = NULL;
ssize_t size; ssize_t size;
size_t slot = Lun & 0xffff;
if (slot >= VICC_MAX_SLOTS) {
return IFD_COMMUNICATION_ERROR;
}
if (!Length || !Value) if (!Length || !Value)
return IFD_COMMUNICATION_ERROR; return IFD_COMMUNICATION_ERROR;
@@ -87,7 +111,7 @@ IFDHGetCapabilities (DWORD Lun, DWORD Tag, PDWORD Length, PUCHAR Value)
switch (Tag) { switch (Tag) {
case TAG_IFD_ATR: case TAG_IFD_ATR:
size = vicc_getatr(&atr); size = vicc_getatr(ctx[slot], &atr);
if (size < 0) { if (size < 0) {
Log1(PCSC_LOG_ERROR, "could not get ATR"); Log1(PCSC_LOG_ERROR, "could not get ATR");
return IFD_COMMUNICATION_ERROR; return IFD_COMMUNICATION_ERROR;
@@ -115,6 +139,13 @@ IFDHGetCapabilities (DWORD Lun, DWORD Tag, PDWORD Length, PUCHAR Value)
return IFD_COMMUNICATION_ERROR; return IFD_COMMUNICATION_ERROR;
} }
*Value = VICC_MAX_SLOTS;
*Length = 1;
break;
case TAG_IFD_SLOT_THREAD_SAFE:
/* driver supports access to multiple slots of the same reader at
* the same time */
*Value = 1; *Value = 1;
*Length = 1; *Length = 1;
break; break;
@@ -149,9 +180,13 @@ IFDHSetProtocolParameters (DWORD Lun, DWORD Protocol, UCHAR Flags, UCHAR PTS1,
RESPONSECODE RESPONSECODE
IFDHPowerICC (DWORD Lun, DWORD Action, PUCHAR Atr, PDWORD AtrLength) IFDHPowerICC (DWORD Lun, DWORD Action, PUCHAR Atr, PDWORD AtrLength)
{ {
size_t slot = Lun & 0xffff;
if (slot >= VICC_MAX_SLOTS) {
return IFD_COMMUNICATION_ERROR;
}
switch (Action) { switch (Action) {
case IFD_POWER_DOWN: case IFD_POWER_DOWN:
if (vicc_poweroff() < 0) { if (vicc_poweroff(ctx[slot]) < 0) {
Log1(PCSC_LOG_ERROR, "could not powerdown"); Log1(PCSC_LOG_ERROR, "could not powerdown");
return IFD_COMMUNICATION_ERROR; return IFD_COMMUNICATION_ERROR;
} }
@@ -163,13 +198,13 @@ IFDHPowerICC (DWORD Lun, DWORD Action, PUCHAR Atr, PDWORD AtrLength)
#endif #endif
return IFD_SUCCESS; return IFD_SUCCESS;
case IFD_POWER_UP: case IFD_POWER_UP:
if (vicc_poweron() < 0) { if (vicc_poweron(ctx[slot]) < 0) {
Log1(PCSC_LOG_ERROR, "could not powerup"); Log1(PCSC_LOG_ERROR, "could not powerup");
return IFD_COMMUNICATION_ERROR; return IFD_COMMUNICATION_ERROR;
} }
break; break;
case IFD_RESET: case IFD_RESET:
if (vicc_reset() < 0) { if (vicc_reset(ctx[slot]) < 0) {
Log1(PCSC_LOG_ERROR, "could not reset"); Log1(PCSC_LOG_ERROR, "could not reset");
return IFD_COMMUNICATION_ERROR; return IFD_COMMUNICATION_ERROR;
} }
@@ -190,13 +225,17 @@ IFDHTransmitToICC (DWORD Lun, SCARD_IO_HEADER SendPci, PUCHAR TxBuffer,
unsigned char *rapdu = NULL; unsigned char *rapdu = NULL;
ssize_t size; ssize_t size;
RESPONSECODE r = IFD_COMMUNICATION_ERROR; RESPONSECODE r = IFD_COMMUNICATION_ERROR;
size_t slot = Lun & 0xffff;
if (slot >= VICC_MAX_SLOTS) {
return IFD_COMMUNICATION_ERROR;
}
if (!RxLength || !RecvPci) { if (!RxLength || !RecvPci) {
Log1(PCSC_LOG_ERROR, "Invalid input data"); Log1(PCSC_LOG_ERROR, "Invalid input data");
goto err; goto err;
} }
size = vicc_transmit(TxLength, TxBuffer, &rapdu); size = vicc_transmit(ctx[slot], TxLength, TxBuffer, &rapdu);
if (size < 0) { if (size < 0) {
Log1(PCSC_LOG_ERROR, "could not send apdu or receive rapdu"); Log1(PCSC_LOG_ERROR, "could not send apdu or receive rapdu");
@@ -226,7 +265,11 @@ err:
RESPONSECODE RESPONSECODE
IFDHICCPresence (DWORD Lun) IFDHICCPresence (DWORD Lun)
{ {
switch (vicc_present()) { size_t slot = Lun & 0xffff;
if (slot >= VICC_MAX_SLOTS) {
return IFD_COMMUNICATION_ERROR;
}
switch (vicc_present(ctx[slot])) {
case 0: case 0:
return IFD_ICC_NOT_PRESENT; return IFD_ICC_NOT_PRESENT;
case 1: case 1:

View File

@@ -43,11 +43,8 @@ typedef WORD uint16_t;
#define VPCD_CTRL_RESET 2 #define VPCD_CTRL_RESET 2
#define VPCD_CTRL_ATR 4 #define VPCD_CTRL_ATR 4
static int server_sock = -1; ssize_t sendToVICC(struct vicc_ctx *ctx, size_t size, const unsigned char *buffer);
static int client_sock = -1; ssize_t recvFromVICC(struct vicc_ctx *ctx, unsigned char **buffer);
ssize_t sendToVICC(size_t size, const unsigned char *buffer);
ssize_t recvFromVICC(unsigned char **buffer);
static ssize_t sendall(int sock, const void *buffer, size_t size); static ssize_t sendall(int sock, const void *buffer, size_t size);
static ssize_t recvall(int sock, void *buffer, size_t size); static ssize_t recvall(int sock, void *buffer, size_t size);
@@ -135,37 +132,42 @@ int waitforclient(int server, long secs, long usecs)
return 0; return 0;
} }
ssize_t sendToVICC(size_t length, const unsigned char* buffer) ssize_t sendToVICC(struct vicc_ctx *ctx, size_t length, const unsigned char* buffer)
{ {
ssize_t r; ssize_t r;
uint16_t size; uint16_t size;
if (!ctx) {
errno = EINVAL;
return -1;
}
/* send size of message on 2 bytes */ /* send size of message on 2 bytes */
size = htons((uint16_t) length); size = htons((uint16_t) length);
r = sendall(client_sock, (void *) &size, sizeof size); r = sendall(ctx->client_sock, (void *) &size, sizeof size);
if (r == sizeof size) if (r == sizeof size)
/* send message */ /* send message */
r = sendall(client_sock, buffer, length); r = sendall(ctx->client_sock, buffer, length);
if (r < 0) if (r < 0)
vicc_eject(); vicc_eject(ctx);
return r; return r;
} }
ssize_t recvFromVICC(unsigned char **buffer) ssize_t recvFromVICC(struct vicc_ctx *ctx, unsigned char **buffer)
{ {
ssize_t r; ssize_t r;
uint16_t size; uint16_t size;
unsigned char *p = NULL; unsigned char *p = NULL;
if (!buffer) { if (!buffer || !ctx) {
errno = EINVAL; errno = EINVAL;
return -1; return -1;
} }
/* receive size of message on 2 bytes */ /* receive size of message on 2 bytes */
r = recvall(client_sock, &size, sizeof size); r = recvall(ctx->client_sock, &size, sizeof size);
if (r < sizeof size) if (r < sizeof size)
return r; return r;
@@ -179,57 +181,75 @@ ssize_t recvFromVICC(unsigned char **buffer)
*buffer = p; *buffer = p;
/* receive message */ /* receive message */
return recvall(client_sock, *buffer, size); return recvall(ctx->client_sock, *buffer, size);
} }
int vicc_eject(void) { int vicc_eject(struct vicc_ctx *ctx)
if (client_sock > 0) { {
client_sock = close(client_sock); if (ctx && ctx->client_sock > 0) {
if (client_sock < 0) { ctx->client_sock = close(ctx->client_sock);
if (ctx->client_sock < 0) {
return -1; return -1;
} }
} }
return 0; return 0;
} }
int vicc_init(unsigned short port) { struct vicc_ctx * vicc_init(unsigned short port)
server_sock = opensock(port); {
if (server_sock < 0) struct vicc_ctx *ctx = malloc(sizeof *ctx);
return -1; if (!ctx) {
return NULL;
return 0;
}
int vicc_exit(void) {
if (server_sock > 0) {
server_sock = close(server_sock);
if (server_sock < 0) return -1;
} }
return 0; ctx->server_sock = opensock(port);
if (ctx->server_sock < 0) {
free(ctx);
return NULL;
}
ctx->client_sock = -1;
return ctx;
} }
ssize_t vicc_transmit(size_t apdu_len, int vicc_exit(struct vicc_ctx *ctx)
const unsigned char *apdu, unsigned char **rapdu)
{ {
ssize_t r; int r = vicc_eject(ctx);
if (ctx) {
r = sendToVICC(apdu_len, apdu); if (ctx->server_sock > 0) {
ctx->server_sock = close(ctx->server_sock);
if (r > 0) if (ctx->server_sock < 0) {
r = recvFromVICC(rapdu); r -= 1;
}
if (r <= 0) }
vicc_eject(); }
return r; return r;
} }
int vicc_present(void) { ssize_t vicc_transmit(struct vicc_ctx *ctx,
size_t apdu_len, const unsigned char *apdu,
unsigned char **rapdu)
{
ssize_t r;
r = sendToVICC(ctx, apdu_len, apdu);
if (r > 0)
r = recvFromVICC(ctx, rapdu);
if (r <= 0)
vicc_eject(ctx);
return r;
}
int vicc_present(struct vicc_ctx *ctx) {
unsigned char *atr = NULL; unsigned char *atr = NULL;
if (client_sock > 0) { if (ctx->client_sock > 0) {
if (vicc_getatr(&atr) <= 0) if (vicc_getatr(ctx, &atr) <= 0)
return 0; return 0;
free(atr); free(atr);
@@ -237,31 +257,31 @@ int vicc_present(void) {
return 1; return 1;
} else { } else {
/* Wait up to one microsecond. */ /* Wait up to one microsecond. */
client_sock = waitforclient(server_sock, 0, 1); ctx->client_sock = waitforclient(ctx->server_sock, 0, 1);
if (client_sock < 0) if (ctx->client_sock < 0)
return -1; return -1;
} }
return 0; return 0;
} }
ssize_t vicc_getatr(unsigned char **atr) { ssize_t vicc_getatr(struct vicc_ctx *ctx, unsigned char **atr) {
unsigned char i = VPCD_CTRL_ATR; unsigned char i = VPCD_CTRL_ATR;
return vicc_transmit(VPCD_CTRL_LEN, &i, atr); return vicc_transmit(ctx, VPCD_CTRL_LEN, &i, atr);
} }
int vicc_poweron(void) { int vicc_poweron(struct vicc_ctx *ctx) {
unsigned char i = VPCD_CTRL_ON; unsigned char i = VPCD_CTRL_ON;
return sendToVICC(VPCD_CTRL_LEN, &i); return sendToVICC(ctx, VPCD_CTRL_LEN, &i);
} }
int vicc_poweroff(void) { int vicc_poweroff(struct vicc_ctx *ctx) {
unsigned char i = VPCD_CTRL_OFF; unsigned char i = VPCD_CTRL_OFF;
return sendToVICC(VPCD_CTRL_LEN, &i); return sendToVICC(ctx, VPCD_CTRL_LEN, &i);
} }
int vicc_reset(void) { int vicc_reset(struct vicc_ctx *ctx) {
unsigned char i = VPCD_CTRL_RESET; unsigned char i = VPCD_CTRL_RESET;
return sendToVICC(VPCD_CTRL_LEN, &i); return sendToVICC(ctx, VPCD_CTRL_LEN, &i);
} }

View File

@@ -26,6 +26,11 @@ typedef int ssize_t;
#include <unistd.h> #include <unistd.h>
#endif #endif
struct vicc_ctx {
int server_sock;
int client_sock;
};
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@@ -34,14 +39,14 @@ extern "C" {
/** Standard port of the virtual smart card reader */ /** Standard port of the virtual smart card reader */
#define VPCDPORT 35963 #define VPCDPORT 35963
int vicc_init(unsigned short port); struct vicc_ctx * vicc_init(unsigned short port);
int vicc_exit(void); int vicc_exit(struct vicc_ctx *ctx);
int vicc_eject(void); int vicc_eject(struct vicc_ctx *ctx);
int vicc_present(void); int vicc_present(struct vicc_ctx *ctx);
int vicc_poweron(void); int vicc_poweron(struct vicc_ctx *ctx);
int vicc_poweroff(void); int vicc_poweroff(struct vicc_ctx *ctx);
int vicc_reset(void); int vicc_reset(struct vicc_ctx *ctx);
/** /**
* @brief Receive ATR from the virtual smart card. * @brief Receive ATR from the virtual smart card.
@@ -52,7 +57,7 @@ int vicc_reset(void);
* @return On success, the call returns the number of bytes received. * @return On success, the call returns the number of bytes received.
* On error, -1 is returned, and errno is set appropriately. * On error, -1 is returned, and errno is set appropriately.
*/ */
ssize_t vicc_getatr(unsigned char** atr); ssize_t vicc_getatr(struct vicc_ctx *ctx, unsigned char** atr);
/** /**
* @brief Send an APDU to the virtual smart card. * @brief Send an APDU to the virtual smart card.
@@ -66,7 +71,8 @@ ssize_t vicc_getatr(unsigned char** atr);
* @return On success, the call returns the number of bytes received. * @return On success, the call returns the number of bytes received.
* On error, -1 is returned, and errno is set appropriately. * On error, -1 is returned, and errno is set appropriately.
*/ */
ssize_t vicc_transmit(size_t apdu_len, const unsigned char *apdu, ssize_t vicc_transmit(struct vicc_ctx *ctx,
size_t apdu_len, const unsigned char *apdu,
unsigned char **rapdu); unsigned char **rapdu);
#ifdef __cplusplus #ifdef __cplusplus