mirror of
git://git.gnupg.org/gnupg.git
synced 2025-07-02 22:46:30 +02:00
A bunch of changes for the openpgp card.
This commit is contained in:
parent
0c67c75cbe
commit
577d9c2342
19 changed files with 1958 additions and 263 deletions
717
g10/apdu.c
717
g10/apdu.c
|
@ -1,5 +1,5 @@
|
|||
/* apdu.c - ISO 7816 APDU functions and low level I/O
|
||||
* Copyright (C) 2003 Free Software Foundation, Inc.
|
||||
* Copyright (C) 2003, 2004 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
|
@ -24,11 +24,18 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#ifdef USE_GNU_PTH
|
||||
# include <pth.h>
|
||||
# include <unistd.h>
|
||||
# include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_OPENSC
|
||||
# include <opensc/opensc.h>
|
||||
#endif
|
||||
|
||||
#if GNUPG_MAJOR_VERSION == 1
|
||||
#if defined(GNUPG_SCD_MAIN_HEADER)
|
||||
#include GNUPG_SCD_MAIN_HEADER
|
||||
#elif GNUPG_MAJOR_VERSION == 1
|
||||
/* This is used with GnuPG version < 1.9. The code has been source
|
||||
copied from the current GnuPG >= 1.9 and is maintained over
|
||||
there. */
|
||||
|
@ -46,6 +53,11 @@
|
|||
#include "dynload.h"
|
||||
#include "ccid-driver.h"
|
||||
|
||||
#ifdef USE_GNU_PTH
|
||||
#define NEED_PCSC_WRAPPER 1
|
||||
#endif
|
||||
|
||||
|
||||
#define MAX_READER 4 /* Number of readers we support concurrently. */
|
||||
#define CARD_CONNECT_TIMEOUT 1 /* Number of seconds to wait for
|
||||
insertion of the card (1 = don't wait). */
|
||||
|
@ -57,6 +69,12 @@
|
|||
#define DLSTDCALL
|
||||
#endif
|
||||
|
||||
#ifdef _POSIX_OPEN_MAX
|
||||
#define MAX_OPEN_FDS _POSIX_OPEN_MAX
|
||||
#else
|
||||
#define MAX_OPEN_FDS 20
|
||||
#endif
|
||||
|
||||
|
||||
/* A structure to collect information pertaining to one reader
|
||||
slot. */
|
||||
|
@ -72,6 +90,11 @@ struct reader_table_s {
|
|||
unsigned long context;
|
||||
unsigned long card;
|
||||
unsigned long protocol;
|
||||
#ifdef NEED_PCSC_WRAPPER
|
||||
int req_fd;
|
||||
int rsp_fd;
|
||||
pid_t pid;
|
||||
#endif /*NEED_PCSC_WRAPPER*/
|
||||
} pcsc;
|
||||
#ifdef HAVE_OPENSC
|
||||
int is_osc; /* We are using the OpenSC driver layer. */
|
||||
|
@ -83,6 +106,11 @@ struct reader_table_s {
|
|||
int status;
|
||||
unsigned char atr[33];
|
||||
size_t atrlen;
|
||||
unsigned int change_counter;
|
||||
#ifdef USE_GNU_PTH
|
||||
int lock_initialized;
|
||||
pth_mutex_t lock;
|
||||
#endif
|
||||
};
|
||||
typedef struct reader_table_s *reader_table_t;
|
||||
|
||||
|
@ -183,11 +211,27 @@ new_reader_slot (void)
|
|||
log_error ("new_reader_slot: out of slots\n");
|
||||
return -1;
|
||||
}
|
||||
#ifdef USE_GNU_PTH
|
||||
if (!reader_table[reader].lock_initialized)
|
||||
{
|
||||
if (!pth_mutex_init (&reader_table[reader].lock))
|
||||
{
|
||||
log_error ("error initializing mutex: %s\n", strerror (errno));
|
||||
return -1;
|
||||
}
|
||||
reader_table[reader].lock_initialized = 1;
|
||||
}
|
||||
#endif /*USE_GNU_PTH*/
|
||||
reader_table[reader].used = 1;
|
||||
reader_table[reader].is_ccid = 0;
|
||||
reader_table[reader].is_ctapi = 0;
|
||||
#ifdef HAVE_OPENSC
|
||||
reader_table[reader].is_osc = 0;
|
||||
#endif
|
||||
#ifdef NEED_PCSC_WRAPPER
|
||||
reader_table[reader].pcsc.req_fd = -1;
|
||||
reader_table[reader].pcsc.rsp_fd = -1;
|
||||
reader_table[reader].pcsc.pid = (pid_t)(-1);
|
||||
#endif
|
||||
return reader;
|
||||
}
|
||||
|
@ -368,6 +412,18 @@ close_ct_reader (int slot)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
reset_ct_reader (int slot)
|
||||
{
|
||||
return SW_HOST_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ct_get_status (int slot, unsigned int *status)
|
||||
{
|
||||
return SW_HOST_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
/* Actually send the APDU of length APDULEN to SLOT and return a
|
||||
maximum of *BUFLEN data in BUFFER, the actual retruned size will be
|
||||
|
@ -395,6 +451,66 @@ ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
|
|||
|
||||
|
||||
|
||||
#ifdef NEED_PCSC_WRAPPER
|
||||
static int
|
||||
writen (int fd, const void *buf, size_t nbytes)
|
||||
{
|
||||
size_t nleft = nbytes;
|
||||
int nwritten;
|
||||
|
||||
/* log_printhex (" writen:", buf, nbytes); */
|
||||
|
||||
while (nleft > 0)
|
||||
{
|
||||
#ifdef USE_GNU_PTH
|
||||
nwritten = pth_write (fd, buf, nleft);
|
||||
#else
|
||||
nwritten = write (fd, buf, nleft);
|
||||
#endif
|
||||
if (nwritten < 0 && errno == EINTR)
|
||||
continue;
|
||||
if (nwritten < 0)
|
||||
return -1;
|
||||
nleft -= nwritten;
|
||||
buf = (const char*)buf + nwritten;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read up to BUFLEN bytes from FD and return the number of bytes
|
||||
actually read in NREAD. Returns -1 on error or 0 on success. */
|
||||
static int
|
||||
readn (int fd, void *buf, size_t buflen, size_t *nread)
|
||||
{
|
||||
size_t nleft = buflen;
|
||||
int n;
|
||||
/* void *orig_buf = buf; */
|
||||
|
||||
while (nleft > 0)
|
||||
{
|
||||
#ifdef USE_GNU_PTH
|
||||
n = pth_read (fd, buf, nleft);
|
||||
#else
|
||||
n = read (fd, buf, nleft);
|
||||
#endif
|
||||
if (n < 0 && errno == EINTR)
|
||||
continue;
|
||||
if (n < 0)
|
||||
return -1; /* read error. */
|
||||
if (!n)
|
||||
break; /* EOF */
|
||||
nleft -= n;
|
||||
buf = (char*)buf + n;
|
||||
}
|
||||
if (nread)
|
||||
*nread = buflen - nleft;
|
||||
|
||||
/* log_printhex (" readn:", orig_buf, *nread); */
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /*NEED_PCSC_WRAPPER*/
|
||||
|
||||
static const char *
|
||||
pcsc_error_string (long err)
|
||||
{
|
||||
|
@ -455,6 +571,172 @@ pcsc_error_string (long err)
|
|||
static int
|
||||
open_pcsc_reader (const char *portstr)
|
||||
{
|
||||
#ifdef NEED_PCSC_WRAPPER
|
||||
/* Open the PC/SC reader using the pcsc_wrapper program. This is
|
||||
needed to cope with different thread models and other peculiarities
|
||||
of libpcsclite. */
|
||||
int slot;
|
||||
reader_table_t slotp;
|
||||
int fd, rp[2], wp[2];
|
||||
int n, i;
|
||||
pid_t pid;
|
||||
size_t len;
|
||||
unsigned char msgbuf[9];
|
||||
int err;
|
||||
|
||||
slot = new_reader_slot ();
|
||||
if (slot == -1)
|
||||
return -1;
|
||||
slotp = reader_table + slot;
|
||||
|
||||
/* Fire up the pcsc wrapper. We don't use any fork/exec code from
|
||||
the common directy but implement it direclty so that this file
|
||||
may still be source copied. */
|
||||
|
||||
if (pipe (rp) == -1)
|
||||
{
|
||||
log_error ("error creating a pipe: %s\n", strerror (errno));
|
||||
slotp->used = 0;
|
||||
return -1;
|
||||
}
|
||||
if (pipe (wp) == -1)
|
||||
{
|
||||
log_error ("error creating a pipe: %s\n", strerror (errno));
|
||||
close (rp[0]);
|
||||
close (rp[1]);
|
||||
slotp->used = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
pid = fork ();
|
||||
if (pid == -1)
|
||||
{
|
||||
log_error ("error forking process: %s\n", strerror (errno));
|
||||
close (rp[0]);
|
||||
close (rp[1]);
|
||||
close (wp[0]);
|
||||
close (wp[1]);
|
||||
slotp->used = 0;
|
||||
return -1;
|
||||
}
|
||||
slotp->pcsc.pid = pid;
|
||||
|
||||
if (!pid)
|
||||
{ /*
|
||||
=== Child ===
|
||||
*/
|
||||
|
||||
/* Double fork. */
|
||||
pid = fork ();
|
||||
if (pid == -1)
|
||||
_exit (31);
|
||||
if (pid)
|
||||
_exit (0); /* Immediate exit this parent, so that the child
|
||||
gets cleaned up by the init process. */
|
||||
|
||||
/* Connect our pipes. */
|
||||
if (wp[0] != 0 && dup2 (wp[0], 0) == -1)
|
||||
log_fatal ("dup2 stdin failed: %s\n", strerror (errno));
|
||||
if (rp[1] != 1 && dup2 (rp[1], 1) == -1)
|
||||
log_fatal ("dup2 stdout failed: %s\n", strerror (errno));
|
||||
|
||||
/* Send stderr to the bit bucket. */
|
||||
fd = open ("/dev/null", O_WRONLY);
|
||||
if (fd == -1)
|
||||
log_fatal ("can't open `/dev/null': %s", strerror (errno));
|
||||
if (fd != 2 && dup2 (fd, 2) == -1)
|
||||
log_fatal ("dup2 stderr failed: %s\n", strerror (errno));
|
||||
|
||||
/* Close all other files. */
|
||||
n = sysconf (_SC_OPEN_MAX);
|
||||
if (n < 0)
|
||||
n = MAX_OPEN_FDS;
|
||||
for (i=3; i < n; i++)
|
||||
close(i);
|
||||
errno = 0;
|
||||
|
||||
execl (GNUPG_LIBDIR "/pcsc-wrapper",
|
||||
"pcsc-wrapper",
|
||||
"--",
|
||||
"1", /* API version */
|
||||
opt.pcsc_driver, /* Name of the PC/SC library. */
|
||||
NULL);
|
||||
_exit (31);
|
||||
}
|
||||
|
||||
/*
|
||||
=== Parent ===
|
||||
*/
|
||||
close (wp[0]);
|
||||
close (rp[1]);
|
||||
slotp->pcsc.req_fd = wp[1];
|
||||
slotp->pcsc.rsp_fd = rp[0];
|
||||
|
||||
/* Wait for the intermediate child to terminate. */
|
||||
while ( (i=pth_waitpid (pid, NULL, 0)) == -1 && errno == EINTR)
|
||||
;
|
||||
|
||||
/* Now send the open request. */
|
||||
msgbuf[0] = 0x01; /* OPEN command. */
|
||||
len = portstr? strlen (portstr):0;
|
||||
msgbuf[1] = (len >> 24);
|
||||
msgbuf[2] = (len >> 16);
|
||||
msgbuf[3] = (len >> 8);
|
||||
msgbuf[4] = (len );
|
||||
if ( writen (slotp->pcsc.req_fd, msgbuf, 5)
|
||||
|| (portstr && writen (slotp->pcsc.req_fd, portstr, len)))
|
||||
{
|
||||
log_error ("error sending PC/SC OPEN request: %s\n",
|
||||
strerror (errno));
|
||||
goto command_failed;
|
||||
}
|
||||
/* Read the response. */
|
||||
if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
|
||||
{
|
||||
log_error ("error receiving PC/SC OPEN response: %s\n",
|
||||
i? strerror (errno) : "premature EOF");
|
||||
goto command_failed;
|
||||
}
|
||||
len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
|
||||
if (msgbuf[0] != 0x81 || len < 4)
|
||||
{
|
||||
log_error ("invalid response header from PC/SC received\n");
|
||||
goto command_failed;
|
||||
}
|
||||
len -= 4; /* Already read the error code. */
|
||||
if (len > DIM (slotp->atr))
|
||||
{
|
||||
log_error ("PC/SC returned a too large ATR (len=%x)\n", len);
|
||||
goto command_failed;
|
||||
}
|
||||
err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
|
||||
if (err)
|
||||
{
|
||||
log_error ("PC/SC OPEN failed: %s\n", pcsc_error_string (err));
|
||||
goto command_failed;
|
||||
}
|
||||
n = len;
|
||||
if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
|
||||
{
|
||||
log_error ("error receiving PC/SC OPEN response: %s\n",
|
||||
i? strerror (errno) : "premature EOF");
|
||||
goto command_failed;
|
||||
}
|
||||
slotp->atrlen = len;
|
||||
|
||||
dump_reader_status (slot);
|
||||
return slot;
|
||||
|
||||
command_failed:
|
||||
close (slotp->pcsc.req_fd);
|
||||
close (slotp->pcsc.rsp_fd);
|
||||
slotp->pcsc.req_fd = -1;
|
||||
slotp->pcsc.rsp_fd = -1;
|
||||
kill (slotp->pcsc.pid, SIGTERM);
|
||||
slotp->pcsc.pid = (pid_t)(-1);
|
||||
slotp->used = 0;
|
||||
return -1;
|
||||
#else /*!NEED_PCSC_WRAPPER */
|
||||
long err;
|
||||
int slot;
|
||||
char *list = NULL;
|
||||
|
@ -557,9 +839,16 @@ open_pcsc_reader (const char *portstr)
|
|||
|
||||
dump_reader_status (slot);
|
||||
return slot;
|
||||
#endif /*!NEED_PCSC_WRAPPER */
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
pcsc_get_status (int slot, unsigned int *status)
|
||||
{
|
||||
return SW_HOST_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
/* Actually send the APDU of length APDULEN to SLOT and return a
|
||||
maximum of *BUFLEN data in BUFFER, the actual returned size will be
|
||||
set to BUFLEN. Returns: CT API error code. */
|
||||
|
@ -567,6 +856,109 @@ static int
|
|||
pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
|
||||
unsigned char *buffer, size_t *buflen)
|
||||
{
|
||||
#ifdef NEED_PCSC_WRAPPER
|
||||
long err;
|
||||
reader_table_t slotp;
|
||||
size_t len, full_len;
|
||||
int i, n;
|
||||
unsigned char msgbuf[9];
|
||||
|
||||
if (DBG_CARD_IO)
|
||||
log_printhex (" PCSC_data:", apdu, apdulen);
|
||||
|
||||
slotp = reader_table + slot;
|
||||
|
||||
if (slotp->pcsc.req_fd == -1
|
||||
|| slotp->pcsc.rsp_fd == -1
|
||||
|| slotp->pcsc.pid == (pid_t)(-1) )
|
||||
{
|
||||
log_error ("pcsc_send_apdu: pcsc-wrapper not running\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
msgbuf[0] = 0x03; /* TRANSMIT command. */
|
||||
len = apdulen;
|
||||
msgbuf[1] = (len >> 24);
|
||||
msgbuf[2] = (len >> 16);
|
||||
msgbuf[3] = (len >> 8);
|
||||
msgbuf[4] = (len );
|
||||
if ( writen (slotp->pcsc.req_fd, msgbuf, 5)
|
||||
|| writen (slotp->pcsc.req_fd, apdu, len))
|
||||
{
|
||||
log_error ("error sending PC/SC TRANSMIT request: %s\n",
|
||||
strerror (errno));
|
||||
goto command_failed;
|
||||
}
|
||||
|
||||
/* Read the response. */
|
||||
if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
|
||||
{
|
||||
log_error ("error receiving PC/SC TRANSMIT response: %s\n",
|
||||
i? strerror (errno) : "premature EOF");
|
||||
goto command_failed;
|
||||
}
|
||||
len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
|
||||
if (msgbuf[0] != 0x81 || len < 4)
|
||||
{
|
||||
log_error ("invalid response header from PC/SC received\n");
|
||||
goto command_failed;
|
||||
}
|
||||
len -= 4; /* Already read the error code. */
|
||||
err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
|
||||
if (err)
|
||||
{
|
||||
log_error ("pcsc_transmit failed: %s (0x%lx)\n",
|
||||
pcsc_error_string (err), err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
full_len = len;
|
||||
|
||||
n = *buflen < len ? *buflen : len;
|
||||
if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != n)
|
||||
{
|
||||
log_error ("error receiving PC/SC TRANSMIT response: %s\n",
|
||||
i? strerror (errno) : "premature EOF");
|
||||
goto command_failed;
|
||||
}
|
||||
*buflen = n;
|
||||
|
||||
full_len -= len;
|
||||
if (full_len)
|
||||
{
|
||||
log_error ("pcsc_send_apdu: provided buffer too short - truncated\n");
|
||||
err = -1;
|
||||
}
|
||||
/* We need to read any rest of the response, to keep the
|
||||
protocol runnng. */
|
||||
while (full_len)
|
||||
{
|
||||
unsigned char dummybuf[128];
|
||||
|
||||
n = full_len < DIM (dummybuf) ? full_len : DIM (dummybuf);
|
||||
if ((i=readn (slotp->pcsc.rsp_fd, dummybuf, n, &len)) || len != n)
|
||||
{
|
||||
log_error ("error receiving PC/SC TRANSMIT response: %s\n",
|
||||
i? strerror (errno) : "premature EOF");
|
||||
goto command_failed;
|
||||
}
|
||||
full_len -= n;
|
||||
}
|
||||
|
||||
return err;
|
||||
|
||||
command_failed:
|
||||
close (slotp->pcsc.req_fd);
|
||||
close (slotp->pcsc.rsp_fd);
|
||||
slotp->pcsc.req_fd = -1;
|
||||
slotp->pcsc.rsp_fd = -1;
|
||||
kill (slotp->pcsc.pid, SIGTERM);
|
||||
slotp->pcsc.pid = (pid_t)(-1);
|
||||
slotp->used = 0;
|
||||
return -1;
|
||||
|
||||
#else /*!NEED_PCSC_WRAPPER*/
|
||||
|
||||
long err;
|
||||
struct pcsc_io_request_s send_pci;
|
||||
unsigned long recv_len;
|
||||
|
@ -589,14 +981,87 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
|
|||
pcsc_error_string (err), err);
|
||||
|
||||
return err? -1:0; /* FIXME: Return appropriate error code. */
|
||||
#endif /*!NEED_PCSC_WRAPPER*/
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
close_pcsc_reader (int slot)
|
||||
{
|
||||
#ifdef NEED_PCSC_WRAPPER
|
||||
long err;
|
||||
reader_table_t slotp;
|
||||
size_t len;
|
||||
int i;
|
||||
unsigned char msgbuf[9];
|
||||
|
||||
slotp = reader_table + slot;
|
||||
|
||||
if (slotp->pcsc.req_fd == -1
|
||||
|| slotp->pcsc.rsp_fd == -1
|
||||
|| slotp->pcsc.pid == (pid_t)(-1) )
|
||||
{
|
||||
log_error ("close_pcsc_reader: pcsc-wrapper not running\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
msgbuf[0] = 0x02; /* CLOSE command. */
|
||||
len = 0;
|
||||
msgbuf[1] = (len >> 24);
|
||||
msgbuf[2] = (len >> 16);
|
||||
msgbuf[3] = (len >> 8);
|
||||
msgbuf[4] = (len );
|
||||
if ( writen (slotp->pcsc.req_fd, msgbuf, 5) )
|
||||
{
|
||||
log_error ("error sending PC/SC CLOSE request: %s\n",
|
||||
strerror (errno));
|
||||
goto command_failed;
|
||||
}
|
||||
|
||||
/* Read the response. */
|
||||
if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
|
||||
{
|
||||
log_error ("error receiving PC/SC CLOSE response: %s\n",
|
||||
i? strerror (errno) : "premature EOF");
|
||||
goto command_failed;
|
||||
}
|
||||
len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
|
||||
if (msgbuf[0] != 0x81 || len < 4)
|
||||
{
|
||||
log_error ("invalid response header from PC/SC received\n");
|
||||
goto command_failed;
|
||||
}
|
||||
len -= 4; /* Already read the error code. */
|
||||
err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
|
||||
if (err)
|
||||
log_error ("pcsc_close failed: %s (0x%lx)\n",
|
||||
pcsc_error_string (err), err);
|
||||
|
||||
/* We will the wrapper in any case - errors are merely
|
||||
informational. */
|
||||
|
||||
command_failed:
|
||||
close (slotp->pcsc.req_fd);
|
||||
close (slotp->pcsc.rsp_fd);
|
||||
slotp->pcsc.req_fd = -1;
|
||||
slotp->pcsc.rsp_fd = -1;
|
||||
kill (slotp->pcsc.pid, SIGTERM);
|
||||
slotp->pcsc.pid = (pid_t)(-1);
|
||||
slotp->used = 0;
|
||||
return 0;
|
||||
|
||||
#else /*!NEED_PCSC_WRAPPER*/
|
||||
|
||||
pcsc_release_context (reader_table[slot].pcsc.context);
|
||||
reader_table[slot].used = 0;
|
||||
return 0;
|
||||
#endif /*!NEED_PCSC_WRAPPER*/
|
||||
}
|
||||
|
||||
static int
|
||||
reset_pcsc_reader (int slot)
|
||||
{
|
||||
return SW_HOST_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
|
||||
|
@ -659,6 +1124,46 @@ close_ccid_reader (int slot)
|
|||
}
|
||||
|
||||
|
||||
static int
|
||||
reset_ccid_reader (int slot)
|
||||
{
|
||||
int err;
|
||||
reader_table_t slotp = reader_table + slot;
|
||||
unsigned char atr[33];
|
||||
size_t atrlen;
|
||||
|
||||
err = ccid_get_atr (slotp->ccid.handle, atr, sizeof atr, &atrlen);
|
||||
if (err)
|
||||
return -1;
|
||||
/* If the reset was successful, update the ATR. */
|
||||
assert (sizeof slotp->atr >= sizeof atr);
|
||||
slotp->atrlen = atrlen;
|
||||
memcpy (slotp->atr, atr, atrlen);
|
||||
dump_reader_status (slot);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
get_status_ccid (int slot, unsigned int *status)
|
||||
{
|
||||
int rc;
|
||||
int bits;
|
||||
|
||||
rc = ccid_slot_status (reader_table[slot].ccid.handle, &bits);
|
||||
if (rc)
|
||||
return -1;
|
||||
|
||||
if (bits == 0)
|
||||
*status = 1|2|4;
|
||||
else if (bits == 1)
|
||||
*status = 2;
|
||||
else
|
||||
*status = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Actually send the APDU of length APDULEN to SLOT and return a
|
||||
maximum of *BUFLEN data in BUFFER, the actual returned size will be
|
||||
|
@ -796,6 +1301,18 @@ close_osc_reader (int slot)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
reset_osc_reader (int slot)
|
||||
{
|
||||
return SW_HOST_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ocsc_get_status (int slot, unsigned int *status)
|
||||
{
|
||||
return SW_HOST_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
|
||||
/* Actually send the APDU of length APDULEN to SLOT and return a
|
||||
|
@ -894,6 +1411,45 @@ osc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
|
|||
Driver Access
|
||||
*/
|
||||
|
||||
|
||||
static int
|
||||
lock_slot (int slot)
|
||||
{
|
||||
#ifdef USE_GNU_PTH
|
||||
if (!pth_mutex_acquire (&reader_table[slot].lock, 0, NULL))
|
||||
{
|
||||
log_error ("failed to acquire apdu lock: %s\n", strerror (errno));
|
||||
return SW_HOST_LOCKING_FAILED;
|
||||
}
|
||||
#endif /*USE_GNU_PTH*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
trylock_slot (int slot)
|
||||
{
|
||||
#ifdef USE_GNU_PTH
|
||||
if (!pth_mutex_acquire (&reader_table[slot].lock, TRUE, NULL))
|
||||
{
|
||||
if (errno == EBUSY)
|
||||
return SW_HOST_BUSY;
|
||||
log_error ("failed to acquire apdu lock: %s\n", strerror (errno));
|
||||
return SW_HOST_LOCKING_FAILED;
|
||||
}
|
||||
#endif /*USE_GNU_PTH*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
unlock_slot (int slot)
|
||||
{
|
||||
#ifdef USE_GNU_PTH
|
||||
if (!pth_mutex_release (&reader_table[slot].lock))
|
||||
log_error ("failed to release apdu lock: %s\n", strerror (errno));
|
||||
#endif /*USE_GNU_PTH*/
|
||||
}
|
||||
|
||||
|
||||
/* Open the reader and return an internal slot number or -1 on
|
||||
error. If PORTSTR is NULL we default to a suitable port (for ctAPI:
|
||||
the first USB reader. For PC/SC the first listed reader). If
|
||||
|
@ -935,7 +1491,7 @@ apdu_open_reader (const char *portstr)
|
|||
handle = dlopen (opt.ctapi_driver, RTLD_LAZY);
|
||||
if (!handle)
|
||||
{
|
||||
log_error ("apdu_open_reader: failed to open driver: %s",
|
||||
log_error ("apdu_open_reader: failed to open driver: %s\n",
|
||||
dlerror ());
|
||||
return -1;
|
||||
}
|
||||
|
@ -957,12 +1513,13 @@ apdu_open_reader (const char *portstr)
|
|||
/* No ctAPI configured, so lets try the PC/SC API */
|
||||
if (!pcsc_api_loaded)
|
||||
{
|
||||
#ifndef NEED_PCSC_WRAPPER
|
||||
void *handle;
|
||||
|
||||
handle = dlopen (opt.pcsc_driver, RTLD_LAZY);
|
||||
if (!handle)
|
||||
{
|
||||
log_error ("apdu_open_reader: failed to open driver `%s': %s",
|
||||
log_error ("apdu_open_reader: failed to open driver `%s': %s\n",
|
||||
opt.pcsc_driver, dlerror ());
|
||||
return -1;
|
||||
}
|
||||
|
@ -1018,9 +1575,10 @@ apdu_open_reader (const char *portstr)
|
|||
dlclose (handle);
|
||||
return -1;
|
||||
}
|
||||
#endif /*!NEED_PCSC_WRAPPER*/
|
||||
pcsc_api_loaded = 1;
|
||||
}
|
||||
|
||||
|
||||
return open_pcsc_reader (portstr);
|
||||
}
|
||||
|
||||
|
@ -1044,6 +1602,47 @@ apdu_close_reader (int slot)
|
|||
return close_pcsc_reader (slot);
|
||||
}
|
||||
|
||||
/* Enumerate all readers and return information on whether this reader
|
||||
is in use. The caller should start with SLOT set to 0 and
|
||||
increment it with each call until an error is returned. */
|
||||
int
|
||||
apdu_enum_reader (int slot, int *used)
|
||||
{
|
||||
if (slot < 0 || slot >= MAX_READER)
|
||||
return SW_HOST_NO_DRIVER;
|
||||
*used = reader_table[slot].used;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Do a reset for the card in reader at SLOT. */
|
||||
int
|
||||
apdu_reset (int slot)
|
||||
{
|
||||
int sw;
|
||||
|
||||
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
|
||||
return SW_HOST_NO_DRIVER;
|
||||
|
||||
if ((sw = lock_slot (slot)))
|
||||
return sw;
|
||||
|
||||
if (reader_table[slot].is_ctapi)
|
||||
sw = reset_ct_reader (slot);
|
||||
#ifdef HAVE_LIBUSB
|
||||
else if (reader_table[slot].is_ccid)
|
||||
sw = reset_ccid_reader (slot);
|
||||
#endif
|
||||
#ifdef HAVE_OPENSC
|
||||
else if (reader_table[slot].is_osc)
|
||||
sw = reset_osc_reader (slot);
|
||||
#endif
|
||||
else
|
||||
sw = reset_pcsc_reader (slot);
|
||||
|
||||
unlock_slot (slot);
|
||||
return sw;
|
||||
}
|
||||
|
||||
|
||||
unsigned char *
|
||||
apdu_get_atr (int slot, size_t *atrlen)
|
||||
|
@ -1060,6 +1659,7 @@ apdu_get_atr (int slot, size_t *atrlen)
|
|||
*atrlen = reader_table[slot].atrlen;
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const char *
|
||||
|
@ -1082,7 +1682,62 @@ error_string (int slot, long rc)
|
|||
}
|
||||
|
||||
|
||||
/* Dispatcher for the actual send_apdu fucntion. */
|
||||
/* Retrieve the status for SLOT. The function does obnly wait fot the
|
||||
card to become available if HANG is set to true. On success the
|
||||
bits in STATUS will be set to
|
||||
|
||||
bit 0 = card present and usable
|
||||
bit 1 = card present
|
||||
bit 2 = card active
|
||||
bit 3 = card access locked [not yet implemented]
|
||||
|
||||
For must application, tetsing bit 0 is sufficient.
|
||||
|
||||
CHANGED will receive the value of the counter tracking the number
|
||||
of card insertions. This value may be used to detect a card
|
||||
change.
|
||||
*/
|
||||
int
|
||||
apdu_get_status (int slot, int hang,
|
||||
unsigned int *status, unsigned int *changed)
|
||||
{
|
||||
int sw;
|
||||
unsigned int s;
|
||||
|
||||
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
|
||||
return SW_HOST_NO_DRIVER;
|
||||
|
||||
if ((sw = hang? lock_slot (slot) : trylock_slot (slot)))
|
||||
return sw;
|
||||
|
||||
if (reader_table[slot].is_ctapi)
|
||||
sw = ct_get_status (slot, &s);
|
||||
#ifdef HAVE_LIBUSB
|
||||
else if (reader_table[slot].is_ccid)
|
||||
sw = get_status_ccid (slot, &s);
|
||||
#endif
|
||||
#ifdef HAVE_OPENSC
|
||||
else if (reader_table[slot].is_osc)
|
||||
sw = osc_get_status (slot, &s);
|
||||
#endif
|
||||
else
|
||||
sw = pcsc_get_status (slot, &s);
|
||||
|
||||
unlock_slot (slot);
|
||||
|
||||
if (sw)
|
||||
return sw;
|
||||
|
||||
if (status)
|
||||
*status = s;
|
||||
if (changed)
|
||||
*changed = reader_table[slot].change_counter;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Dispatcher for the actual send_apdu function. Note, that this
|
||||
function should be called in locked state. */
|
||||
static int
|
||||
send_apdu (int slot, unsigned char *apdu, size_t apdulen,
|
||||
unsigned char *buffer, size_t *buflen)
|
||||
|
@ -1117,13 +1772,18 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
|
|||
int lc, const char *data, int le,
|
||||
unsigned char **retbuf, size_t *retbuflen)
|
||||
{
|
||||
unsigned char result[256+10]; /* 10 extra in case of bugs in the driver. */
|
||||
size_t resultlen = 256;
|
||||
#define RESULTLEN 256
|
||||
unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
|
||||
the driver. */
|
||||
size_t resultlen;
|
||||
unsigned char apdu[5+256+1];
|
||||
size_t apdulen;
|
||||
int sw;
|
||||
long rc; /* we need a long here due to PC/SC. */
|
||||
|
||||
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
|
||||
return SW_HOST_NO_DRIVER;
|
||||
|
||||
if (DBG_CARD_IO)
|
||||
log_debug ("send apdu: c=%02X i=%02X p0=%02X p1=%02X lc=%d le=%d\n",
|
||||
class, ins, p0, p1, lc, le);
|
||||
|
@ -1135,6 +1795,9 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
|
|||
if ((!data && lc != -1) || (data && lc == -1))
|
||||
return SW_HOST_INV_VALUE;
|
||||
|
||||
if ((sw = lock_slot (slot)))
|
||||
return sw;
|
||||
|
||||
apdulen = 0;
|
||||
apdu[apdulen++] = class;
|
||||
apdu[apdulen++] = ins;
|
||||
|
@ -1151,11 +1814,13 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
|
|||
assert (sizeof (apdu) >= apdulen);
|
||||
/* As safeguard don't pass any garbage from the stack to the driver. */
|
||||
memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
|
||||
resultlen = RESULTLEN;
|
||||
rc = send_apdu (slot, apdu, apdulen, result, &resultlen);
|
||||
if (rc || resultlen < 2)
|
||||
{
|
||||
log_error ("apdu_send_simple(%d) failed: %s\n",
|
||||
slot, error_string (slot, rc));
|
||||
unlock_slot (slot);
|
||||
return SW_HOST_INCOMPLETE_CARD_RESPONSE;
|
||||
}
|
||||
sw = (result[resultlen-2] << 8) | result[resultlen-1];
|
||||
|
@ -1168,13 +1833,16 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
|
|||
log_printhex (" dump: ", result, resultlen);
|
||||
}
|
||||
|
||||
if (sw == SW_SUCCESS)
|
||||
if (sw == SW_SUCCESS || sw == SW_EOF_REACHED)
|
||||
{
|
||||
if (retbuf)
|
||||
{
|
||||
*retbuf = xtrymalloc (resultlen? resultlen : 1);
|
||||
if (!*retbuf)
|
||||
return SW_HOST_OUT_OF_CORE;
|
||||
{
|
||||
unlock_slot (slot);
|
||||
return SW_HOST_OUT_OF_CORE;
|
||||
}
|
||||
*retbuflen = resultlen;
|
||||
memcpy (*retbuf, result, resultlen);
|
||||
}
|
||||
|
@ -1190,7 +1858,10 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
|
|||
{
|
||||
*retbuf = p = xtrymalloc (bufsize);
|
||||
if (!*retbuf)
|
||||
return SW_HOST_OUT_OF_CORE;
|
||||
{
|
||||
unlock_slot (slot);
|
||||
return SW_HOST_OUT_OF_CORE;
|
||||
}
|
||||
assert (resultlen < bufsize);
|
||||
memcpy (p, result, resultlen);
|
||||
p += resultlen;
|
||||
|
@ -1200,20 +1871,23 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
|
|||
{
|
||||
int len = (sw & 0x00ff);
|
||||
|
||||
log_debug ("apdu_send_simple(%d): %d more bytes available\n",
|
||||
slot, len);
|
||||
if (DBG_CARD_IO)
|
||||
log_debug ("apdu_send_simple(%d): %d more bytes available\n",
|
||||
slot, len);
|
||||
apdulen = 0;
|
||||
apdu[apdulen++] = class;
|
||||
apdu[apdulen++] = 0xC0;
|
||||
apdu[apdulen++] = 0;
|
||||
apdu[apdulen++] = 0;
|
||||
apdu[apdulen++] = 64; /* that is 256 bytes for Le */
|
||||
apdu[apdulen++] = len;
|
||||
memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
|
||||
resultlen = RESULTLEN;
|
||||
rc = send_apdu (slot, apdu, apdulen, result, &resultlen);
|
||||
if (rc || resultlen < 2)
|
||||
{
|
||||
log_error ("apdu_send_simple(%d) for get response failed: %s\n",
|
||||
slot, error_string (slot, rc));
|
||||
unlock_slot (slot);
|
||||
return SW_HOST_INCOMPLETE_CARD_RESPONSE;
|
||||
}
|
||||
sw = (result[resultlen-2] << 8) | result[resultlen-1];
|
||||
|
@ -1225,16 +1899,21 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
|
|||
log_printhex (" dump: ", result, resultlen);
|
||||
}
|
||||
|
||||
if ((sw & 0xff00) == SW_MORE_DATA || sw == SW_SUCCESS)
|
||||
if ((sw & 0xff00) == SW_MORE_DATA
|
||||
|| sw == SW_SUCCESS
|
||||
|| sw == SW_EOF_REACHED )
|
||||
{
|
||||
if (retbuf)
|
||||
if (retbuf && resultlen)
|
||||
{
|
||||
if (p - *retbuf + resultlen > bufsize)
|
||||
{
|
||||
bufsize += resultlen > 4096? resultlen: 4096;
|
||||
tmp = xtryrealloc (*retbuf, bufsize);
|
||||
if (!tmp)
|
||||
return SW_HOST_OUT_OF_CORE;
|
||||
{
|
||||
unlock_slot (slot);
|
||||
return SW_HOST_OUT_OF_CORE;
|
||||
}
|
||||
p = tmp + (p - *retbuf);
|
||||
*retbuf = tmp;
|
||||
}
|
||||
|
@ -1257,10 +1936,14 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
|
|||
*retbuf = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
unlock_slot (slot);
|
||||
|
||||
if (DBG_CARD_IO && retbuf && sw == SW_SUCCESS)
|
||||
log_printhex (" dump: ", *retbuf, *retbuflen);
|
||||
|
||||
return sw;
|
||||
#undef RESULTLEN
|
||||
}
|
||||
|
||||
/* Send an APDU to the card in SLOT. The APDU is created from all
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue