aboutsummaryrefslogtreecommitdiffstats
path: root/scd/pcsc-wrapper.c
diff options
context:
space:
mode:
Diffstat (limited to 'scd/pcsc-wrapper.c')
-rw-r--r--scd/pcsc-wrapper.c274
1 files changed, 235 insertions, 39 deletions
diff --git a/scd/pcsc-wrapper.c b/scd/pcsc-wrapper.c
index 4f47ee95c..93e78fdfe 100644
--- a/scd/pcsc-wrapper.c
+++ b/scd/pcsc-wrapper.c
@@ -86,6 +86,26 @@ static int verbose;
#define PCSC_UNPOWER_CARD 2
#define PCSC_EJECT_CARD 3
+#define PCSC_UNKNOWN 0x0001
+#define PCSC_ABSENT 0x0002 /* Card is absent. */
+#define PCSC_PRESENT 0x0004 /* Card is present. */
+#define PCSC_SWALLOWED 0x0008 /* Card is present and electrical connected. */
+#define PCSC_POWERED 0x0010 /* Card is powered. */
+#define PCSC_NEGOTIABLE 0x0020 /* Card is awaiting PTS. */
+#define PCSC_SPECIFIC 0x0040 /* Card is ready for use. */
+
+#define PCSC_STATE_UNAWARE 0x0000 /* Want status. */
+#define PCSC_STATE_IGNORE 0x0001 /* Ignore this reader. */
+#define PCSC_STATE_CHANGED 0x0002 /* State has changed. */
+#define PCSC_STATE_UNKNOWN 0x0004 /* Reader unknown. */
+#define PCSC_STATE_UNAVAILABLE 0x0008 /* Status unavailable. */
+#define PCSC_STATE_EMPTY 0x0010 /* Card removed. */
+#define PCSC_STATE_PRESENT 0x0020 /* Card inserted. */
+#define PCSC_STATE_ATRMATCH 0x0040 /* ATR matches card. */
+#define PCSC_STATE_EXCLUSIVE 0x0080 /* Exclusive Mode. */
+#define PCSC_STATE_INUSE 0x0100 /* Shared mode. */
+#define PCSC_STATE_MUTE 0x0200 /* Unresponsive card. */
+
struct pcsc_io_request_s {
unsigned long protocol;
unsigned long pci_len;
@@ -93,12 +113,25 @@ struct pcsc_io_request_s {
typedef struct pcsc_io_request_s *pcsc_io_request_t;
+struct pcsc_readerstate_s
+{
+ const char *reader;
+ void *user_data;
+ unsigned long current_state;
+ unsigned long event_state;
+ unsigned long atrlen;
+ unsigned char atr[33];
+};
+
+typedef struct pcsc_readerstate_s *pcsc_readerstate_t;
+
static int driver_is_open; /* True if the PC/SC driver has been
initialzied and is ready for
- operations. The follwoing variables
+ operations. The following variables
are then valid. */
static unsigned long pcsc_context; /* The current PC/CS context. */
+static char *current_rdrname;
static unsigned long pcsc_card;
static unsigned long pcsc_protocol;
static unsigned char current_atr[33];
@@ -112,12 +145,21 @@ long (* pcsc_release_context) (unsigned long context);
long (* pcsc_list_readers) (unsigned long context,
const char *groups,
char *readers, unsigned long*readerslen);
+long (* pcsc_get_status_change) (unsigned long context,
+ unsigned long timeout,
+ pcsc_readerstate_t readerstates,
+ unsigned long nreaderstates);
long (* pcsc_connect) (unsigned long context,
const char *reader,
unsigned long share_mode,
unsigned long preferred_protocols,
unsigned long *r_card,
unsigned long *r_active_protocol);
+long (* pcsc_reconnect) (unsigned long card,
+ unsigned long share_mode,
+ unsigned long preferred_protocols,
+ unsigned long initialization,
+ unsigned long *r_active_protocol);
long (* pcsc_disconnect) (unsigned long card,
unsigned long disposition);
long (* pcsc_status) (unsigned long card,
@@ -284,7 +326,9 @@ load_pcsc_driver (const char *libname)
pcsc_establish_context = dlsym (handle, "SCardEstablishContext");
pcsc_release_context = dlsym (handle, "SCardReleaseContext");
pcsc_list_readers = dlsym (handle, "SCardListReaders");
+ pcsc_get_status_change = dlsym (handle, "SCardGetStatusChange");
pcsc_connect = dlsym (handle, "SCardConnect");
+ pcsc_reconnect = dlsym (handle, "SCardReconnect");
pcsc_disconnect = dlsym (handle, "SCardDisconnect");
pcsc_status = dlsym (handle, "SCardStatus");
pcsc_begin_transaction = dlsym (handle, "SCardBeginTransaction");
@@ -295,7 +339,9 @@ load_pcsc_driver (const char *libname)
if (!pcsc_establish_context
|| !pcsc_release_context
|| !pcsc_list_readers
+ || !pcsc_get_status_change
|| !pcsc_connect
+ || !pcsc_reconnect
|| !pcsc_disconnect
|| !pcsc_status
|| !pcsc_begin_transaction
@@ -307,11 +353,13 @@ load_pcsc_driver (const char *libname)
available under Windows. */
fprintf (stderr,
"apdu_open_reader: invalid PC/SC driver "
- "(%d%d%d%d%d%d%d%d%d%d)\n",
+ "(%d%d%d%d%d%d%d%d%d%d%d%d)\n",
!!pcsc_establish_context,
!!pcsc_release_context,
!!pcsc_list_readers,
+ !!pcsc_get_status_change,
!!pcsc_connect,
+ !!pcsc_reconnect,
!!pcsc_disconnect,
!!pcsc_status,
!!pcsc_begin_transaction,
@@ -327,8 +375,8 @@ load_pcsc_driver (const char *libname)
/* Handle a open request. The argument is expected to be a string
- with the port indentification. ARGBUF is always guaranteed to be
- terminted by a 0 which is not counted in ARGLEN. We may modifiy
+ with the port identification. ARGBUF is always guaranteed to be
+ terminted by a 0 which is not counted in ARGLEN. We may modifiy
ARGBUF. */
static void
handle_open (unsigned char *argbuf, size_t arglen)
@@ -350,6 +398,7 @@ handle_open (unsigned char *argbuf, size_t arglen)
{
fprintf (stderr, PGM ": PC/SC has already been opened\n");
request_failed (-1);
+ return;
}
err = pcsc_establish_context (PCSC_SCOPE_SYSTEM, NULL, NULL, &pcsc_context);
@@ -398,45 +447,64 @@ handle_open (unsigned char *argbuf, size_t arglen)
p += strlen (p) + 1;
}
+ current_rdrname = malloc (strlen (portstr && *portstr? portstr:list)+1);
+ if (!current_rdrname)
+ {
+ fprintf (stderr, PGM": error allocating memory for reader name\n");
+ exit (1);
+ }
+ strcpy (current_rdrname, portstr && *portstr? portstr:list);
+ free (list);
+
err = pcsc_connect (pcsc_context,
- portstr && *portstr? portstr : list,
+ current_rdrname,
PCSC_SHARE_EXCLUSIVE,
PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
&pcsc_card,
&pcsc_protocol);
- if (err)
+ if (err == 0x8010000c) /* No smartcard. */
+ {
+ pcsc_card = 0;
+ }
+ else if (err)
{
fprintf (stderr, PGM": pcsc_connect failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
pcsc_release_context (pcsc_context);
- free (list);
+ free (current_rdrname);
+ current_rdrname = NULL;
request_failed (err);
return;
}
- atrlen = 32;
- /* (We need to pass a dummy buffer. We use LIST because it ought to
- be large enough.) */
- err = pcsc_status (pcsc_card,
- list, &listlen,
- &card_state, &card_protocol,
- atr, &atrlen);
- free (list);
- if (err)
- {
- fprintf (stderr, PGM": pcsc_status failed: %s (0x%lx)\n",
- pcsc_error_string (err), err);
- pcsc_release_context (pcsc_context);
- request_failed (err);
- return;
- }
- if (atrlen >= sizeof atr || atrlen >= sizeof current_atr)
+ current_atrlen = 0;
+ if (!err)
{
- fprintf (stderr, PGM": ATR returned by pcsc_status is too large\n");
- exit (4);
+ char reader[250];
+ unsigned long readerlen;
+
+ atrlen = 33;
+ readerlen = sizeof reader -1;
+ err = pcsc_status (pcsc_card,
+ reader, &readerlen,
+ &card_state, &card_protocol,
+ atr, &atrlen);
+ if (err)
+ fprintf (stderr, PGM": pcsc_status failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ else
+ {
+ if (atrlen >= sizeof atr || atrlen >= sizeof current_atr)
+ {
+ fprintf (stderr, PGM": ATR returned by pcsc_status"
+ " is too large\n");
+ exit (4);
+ }
+ memcpy (current_atr, atr, atrlen);
+ current_atrlen = atrlen;
+ }
}
- memcpy (current_atr, atr, atrlen);
- current_atrlen = atrlen;
+
driver_is_open = 1;
request_succeeded (current_atr, current_atrlen);
}
@@ -452,8 +520,11 @@ handle_close (unsigned char *argbuf, size_t arglen)
{
fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
request_failed (-1);
+ return;
}
+ free (current_rdrname);
+ current_rdrname = NULL;
pcsc_release_context (pcsc_context);
request_succeeded (NULL, 0);
@@ -461,7 +532,133 @@ handle_close (unsigned char *argbuf, size_t arglen)
-/* Handle a transmit request. The argument is expected to be a bufer
+/* Handle a status request. We expect no arguments. We may modifiy
+ ARGBUF. */
+static void
+handle_status (unsigned char *argbuf, size_t arglen)
+{
+ long err;
+ struct pcsc_readerstate_s rdrstates[1];
+ int status;
+ unsigned char buf[20];
+
+ if (!driver_is_open)
+ {
+ fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
+ request_failed (-1);
+ return;
+ }
+
+ memset (rdrstates, 0, sizeof *rdrstates);
+ rdrstates[0].reader = current_rdrname;
+ rdrstates[0].current_state = PCSC_STATE_UNAWARE;
+ err = pcsc_get_status_change (pcsc_context,
+ 0,
+ rdrstates, 1);
+ if (err == 0x8010000a) /* Timeout. */
+ err = 0;
+ if (err)
+ {
+ fprintf (stderr, PGM": pcsc_get_status_change failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ request_failed (err);
+ return;
+ }
+
+ status = 0;
+ if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) )
+ status |= 2;
+ if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
+ status |= 4;
+ /* We indicate a useful card if it is not in use by another
+ application. This is because we only use exclusive access
+ mode. */
+ if ( (status & 6) == 6
+ && !(rdrstates[0].event_state & PCSC_STATE_INUSE) )
+ status |= 1;
+
+ /* First word is identical to the one used by apdu.c. */
+ buf[0] = 0;
+ buf[1] = 0;
+ buf[2] = 0;
+ buf[3] = status;
+ /* The second word is the native PCSC state. */
+ buf[4] = (rdrstates[0].event_state >> 24);
+ buf[5] = (rdrstates[0].event_state >> 16);
+ buf[6] = (rdrstates[0].event_state >> 8);
+ buf[7] = (rdrstates[0].event_state >> 0);
+
+ request_succeeded (buf, 8);
+}
+
+
+/* Handle a reset request. We expect no arguments. We may modifiy
+ ARGBUF. */
+static void
+handle_reset (unsigned char *argbuf, size_t arglen)
+{
+ long err;
+ char reader[250];
+ unsigned long nreader, atrlen;
+ unsigned long card_state, card_protocol;
+
+ if (!driver_is_open)
+ {
+ fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
+ request_failed (-1);
+ return;
+ }
+
+ if (pcsc_card)
+ {
+ err = pcsc_disconnect (pcsc_card, PCSC_LEAVE_CARD);
+ if (err)
+ {
+ fprintf (stderr, PGM": pcsc_disconnect failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ request_failed (err);
+ return;
+ }
+ pcsc_card = 0;
+ }
+
+ err = pcsc_connect (pcsc_context,
+ current_rdrname,
+ PCSC_SHARE_EXCLUSIVE,
+ PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
+ &pcsc_card,
+ &pcsc_protocol);
+ if (err)
+ {
+ fprintf (stderr, PGM": pcsc_connect failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ pcsc_card = 0;
+ request_failed (err);
+ return;
+ }
+
+
+ atrlen = 33;
+ nreader = sizeof reader - 1;
+ err = pcsc_status (pcsc_card,
+ reader, &nreader,
+ &card_state, &card_protocol,
+ current_atr, &atrlen);
+ if (err)
+ {
+ fprintf (stderr, PGM": pcsc_status failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ current_atrlen = 0;
+ request_failed (err);
+ return;
+ }
+
+ request_succeeded (current_atr, current_atrlen);
+}
+
+
+
+/* Handle a transmit request. The argument is expected to be a buffer
with the APDU. We may modifiy ARGBUF. */
static void
handle_transmit (unsigned char *argbuf, size_t arglen)
@@ -479,8 +676,8 @@ handle_transmit (unsigned char *argbuf, size_t arglen)
{
fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
request_failed (-1);
+ return;
}
-
if ((pcsc_protocol & PCSC_PROTOCOL_T1))
send_pci.protocol = PCSC_PROTOCOL_T1;
else
@@ -502,15 +699,6 @@ handle_transmit (unsigned char *argbuf, size_t arglen)
-
-
-
-
-
-
-
-
-
static void
print_version (int with_help)
{
@@ -613,6 +801,14 @@ main (int argc, char **argv)
handle_transmit (argbuffer, arglen);
break;
+ case 4:
+ handle_status (argbuffer, arglen);
+ break;
+
+ case 5:
+ handle_reset (argbuffer, arglen);
+ break;
+
default:
fprintf (stderr, PGM ": invalid request 0x%02X\n", c);
exit (1);