* pcsc-wrapper.c: New.

* Makefile.am (pkglib_PROGRAMS): Install it here.
* apdu.c (writen, readn): New.
(open_pcsc_reader, pcsc_send_apdu, close_pcsc_reader): Use the
pcsc-wrapper if we are using Pth.
This commit is contained in:
Werner Koch 2004-04-20 14:17:10 +00:00
parent d70bc68c30
commit e950b01ff5
11 changed files with 1828 additions and 79 deletions

View File

@ -1,3 +1,57 @@
2004-04-20 Werner Koch <wk@gnupg.org>
* pcsc-wrapper.c: New.
* Makefile.am (pkglib_PROGRAMS): Install it here.
* apdu.c (writen, readn): New.
(open_pcsc_reader, pcsc_send_apdu, close_pcsc_reader): Use the
pcsc-wrapper if we are using Pth.
2004-04-19 Werner Koch <wk@gnupg.org>
* ccid-driver.c (parse_ccid_descriptor): Store some of the reader
features away. New arg HANDLE
(read_device_info): New arg HANDLE. Changed caller.
(bulk_in): Handle time extension requests.
(ccid_get_atr): Setup parameters and the IFSD.
(compute_edc): New. Factored out code.
(ccid_transceive): Use default NADs when required.
2004-04-14 Werner Koch <wk@gnupg.org>
* scdaemon.h (server_control_s): Add member READER_SLOT.
* scdaemon.c (scd_init_default_ctrl): Initialize READER_SLOT to -1.
* command.c (open_card): Reuse an open slot.
(reset_notify): Just reset the slot if supported by the reader.
(do_reset): Factored code from above out.
(scd_command_handler): Use it for cleanup.
* apdu.h: New pseudo stati SW_HOST_NOT_SUPPORTED,
SW_HOST_LOCKING_FAILED and SW_HOST_BUSY.
* iso7816.c (map_sw): Map it.
* ccid-driver.c (ccid_slot_status): Add arg STATUSBITS.
* apdu.c (apdu_get_status): New.
(ct_get_status, pcsc_get_status, ocsc_get_status): New stubs.
(get_status_ccid): New.
(apdu_reset): New.
(reset_ct_reader, reset_pcsc_reader, reset_osc_reader): New stubs.
(reset_ccid_reader): New.
(apdu_enum_reader): New.
* apdu.c (lock_slot, trylock_slot, unlock_slot): New helpers.
(new_reader_slot) [USE_GNU_PTH]: Init mutex.
(apdu_reset, apdu_get_status, apdu_send_le): Run functions
in locked mode.
* command.c (scd_update_reader_status_file): New.
* scdaemon.c (handle_tick): Call it.
2004-04-13 Werner Koch <wk@gnupg.org>
* scdaemon.c: Convert to a Pth application.
(handle_signal, ticker_thread, handle_tick): New.
(main): Fire up the ticker thread in server mode.
2004-03-23 Werner Koch <wk@gnupg.org>
* scdaemon.c (main) <gpgconf_list>: Fixed output for pcsc_driver.

View File

@ -19,13 +19,14 @@
## Process this file with automake to produce Makefile.in
bin_PROGRAMS = scdaemon sc-investigate sc-copykeys
pkglib_PROGRAMS = pcsc-wrapper
AM_CPPFLAGS = -I$(top_srcdir)/intl -I$(top_srcdir)/common
include $(top_srcdir)/am/cmacros.am
AM_CFLAGS = $(OPENSC_CFLAGS) $(LIBGCRYPT_CFLAGS) \
$(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS)
$(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) $(PTH_CFLAGS)
card_apps = app-openpgp.c app-nks.c app-dinsig.c
@ -43,7 +44,8 @@ scdaemon_SOURCES = \
scdaemon_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \
$(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) \
$(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(PTH_LIBS) \
$(KSBA_LIBS) $(LIBASSUAN_LIBS) \
$(LIBUSB_LIBS) -lgpg-error @INTLLIBS@ -ldl
sc_investigate_SOURCES = \
@ -57,7 +59,8 @@ sc_investigate_SOURCES = \
sc_investigate_LDADD = \
../jnlib/libjnlib.a ../common/libcommon.a \
$(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBUSB_LIBS) \
$(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(PTH_LIBS) \
$(KSBA_LIBS) $(LIBUSB_LIBS) \
@INTLLIBS@ -lgpg-error -ldl
@ -73,5 +76,10 @@ sc_copykeys_SOURCES = \
sc_copykeys_LDADD = \
../jnlib/libjnlib.a ../common/libcommon.a \
../common/libsimple-pwquery.a \
$(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBUSB_LIBS) \
$(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(PTH_LIBS) \
$(KSBA_LIBS) $(LIBUSB_LIBS) \
-lgpg-error @INTLLIBS@ -ldl
pcsc_wrapper_SOURCES = pcsc-wrapper.c
pcsc_wrapper_LDADD = -ldl
pcsc_wrapper_CFLAGS =

View File

@ -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,6 +24,11 @@
#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
@ -48,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). */
@ -59,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. */
@ -74,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. */
@ -85,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;
@ -185,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;
}
@ -370,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
@ -397,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)
{
@ -457,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;
@ -559,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. */
@ -569,6 +856,108 @@ 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;
@ -591,14 +980,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;
}
@ -661,6 +1123,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
@ -798,6 +1300,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
@ -896,6 +1410,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
@ -937,7 +1490,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;
}
@ -959,12 +1512,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;
}
@ -1020,9 +1574,10 @@ apdu_open_reader (const char *portstr)
dlclose (handle);
return -1;
}
#endif /*!NEED_PCSC_WRAPPER*/
pcsc_api_loaded = 1;
}
return open_pcsc_reader (portstr);
}
@ -1046,6 +1601,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)
@ -1062,6 +1658,7 @@ apdu_get_atr (int slot, size_t *atrlen)
*atrlen = reader_table[slot].atrlen;
return buf;
}
static const char *
@ -1084,7 +1681,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)
@ -1126,6 +1778,9 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
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);
@ -1137,6 +1792,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;
@ -1158,6 +1816,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
{
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];
@ -1176,7 +1835,10 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
{
*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);
}
@ -1192,7 +1854,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;
@ -1216,6 +1881,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
{
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];
@ -1236,7 +1902,10 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
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;
}
@ -1259,6 +1928,9 @@ 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);

View File

@ -48,9 +48,12 @@ enum {
those values can't be issued by a card. */
SW_HOST_OUT_OF_CORE = 0x10001, /* No way yet to differentiate
between errnos on a failed malloc. */
SW_HOST_INV_VALUE = 0x10002,
SW_HOST_INV_VALUE = 0x10002,
SW_HOST_INCOMPLETE_CARD_RESPONSE = 0x10003,
SW_HOST_NO_DRIVER = 0x10004
SW_HOST_NO_DRIVER = 0x10004,
SW_HOST_NOT_SUPPORTED = 0x10005,
SW_HOST_LOCKING_FAILED= 0x10006,
SW_HOST_BUSY = 0x10007
};
@ -58,10 +61,14 @@ enum {
/* Note , that apdu_open_reader returns no status word but -1 on error. */
int apdu_open_reader (const char *portstr);
int apdu_close_reader (int slot);
int apdu_enum_reader (int slot, int *used);
unsigned char *apdu_get_atr (int slot, size_t *atrlen);
/* The apdu send functions do return status words. */
int apdu_reset (int slot);
int apdu_get_status (int slot, int hang,
unsigned int *status, unsigned int *changed);
int apdu_send_simple (int slot, int class, int ins, int p0, int p1,
int lc, const char *data);
int apdu_send (int slot, int class, int ins, int p0, int p1,

View File

@ -1,5 +1,5 @@
/* ccid-driver.c - USB ChipCardInterfaceDevices driver
* Copyright (C) 2003 Free Software Foundation, Inc.
* Copyright (C) 2003, 2004 Free Software Foundation, Inc.
* Written by Werner Koch.
*
* This file is part of GnuPG.
@ -108,7 +108,10 @@
/* Disable all debugging output for now. */
#undef DBG_CARD_IO
#define DBG_CARD_IO 0
#define DBG_CARD_IO 1
/* Define to print information pertaining the T=1 protocol. */
#undef DEBUG_T1
# define DEBUGOUT(t) do { if (DBG_CARD_IO) \
@ -145,8 +148,6 @@
#endif /* This source not used by scdaemon. */
/* Define to print information pertaining the T=1 protocol. */
#undef DEBUG_T1
@ -184,9 +185,15 @@ struct ccid_driver_s {
int seqno;
unsigned char t1_ns;
unsigned char t1_nr;
int nonnull_nad;
int auto_ifsd;
int max_ifsd;
int ifsd;
};
static unsigned int compute_edc (const unsigned char *data, size_t datalen,
int use_crc);
static int bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen);
static int bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
size_t *nread, int expected_type, int seqno);
@ -220,13 +227,18 @@ set_msg_len (unsigned char *msg, unsigned int length)
Note, that this code is based on the one in lsusb.c of the
usb-utils package, I wrote on 2003-09-01. -wk. */
static int
parse_ccid_descriptor (const unsigned char *buf, size_t buflen)
parse_ccid_descriptor (ccid_driver_t handle,
const unsigned char *buf, size_t buflen)
{
unsigned int i;
unsigned int us;
int have_t1 = 0, have_tpdu=0, have_auto_conf = 0;
handle->nonnull_nad = 0;
handle->auto_ifsd = 0;
handle->max_ifsd = 32;
handle->ifsd = 0;
if (buflen < 54 || buf[0] < 54)
{
DEBUGOUT ("CCID device descriptor is too short\n");
@ -272,6 +284,7 @@ parse_ccid_descriptor (const unsigned char *buf, size_t buflen)
us = convert_le_u32(buf+28);
DEBUGOUT_1 (" dwMaxIFSD %5u\n", us);
handle->max_ifsd = us;
us = convert_le_u32(buf+32);
DEBUGOUT_1 (" dwSyncProtocols %08X ", us);
@ -320,9 +333,15 @@ parse_ccid_descriptor (const unsigned char *buf, size_t buflen)
if ((us & 0x0100))
DEBUGOUT (" CCID can set ICC in clock stop mode\n");
if ((us & 0x0200))
DEBUGOUT (" NAD value other than 0x00 accpeted\n");
{
DEBUGOUT (" NAD value other than 0x00 accepted\n");
handle->nonnull_nad = 1;
}
if ((us & 0x0400))
DEBUGOUT (" Auto IFSD exchange\n");
{
DEBUGOUT (" Auto IFSD exchange\n");
handle->auto_ifsd = 1;
}
if ((us & 0x00010000))
{
@ -389,7 +408,7 @@ parse_ccid_descriptor (const unsigned char *buf, size_t buflen)
that the device is usable for us. Returns 0 on success or an error
code. */
static int
read_device_info (struct usb_device *dev)
read_device_info (ccid_driver_t handle, struct usb_device *dev)
{
int cfg_no;
@ -414,8 +433,9 @@ read_device_info (struct usb_device *dev)
{
if (ifcdesc->extra)
{
if (!parse_ccid_descriptor (ifcdesc->extra,
ifcdesc->extralen))
if (!parse_ccid_descriptor (handle,
ifcdesc->extra,
ifcdesc->extralen))
return 0; /* okay. we can use it. */
}
}
@ -458,10 +478,22 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
dev->descriptor->idVendor, dev->descriptor->idProduct);
if (!readerno)
{
rc = read_device_info (dev);
*handle = calloc (1, sizeof **handle);
if (!*handle)
{
DEBUGOUT ("out of memory\n");
rc = -1;
free (*handle);
*handle = NULL;
goto leave;
}
rc = read_device_info (*handle, dev);
if (rc)
{
DEBUGOUT ("device not supported\n");
free (*handle);
*handle = NULL;
goto leave;
}
@ -469,6 +501,8 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
if (rc)
{
DEBUGOUT_1 ("usb_open failed: %d\n", rc);
free (*handle);
*handle = NULL;
goto leave;
}
@ -479,16 +513,11 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
if (rc)
{
DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
free (*handle);
*handle = NULL;
goto leave;
}
*handle = calloc (1, sizeof **handle);
if (!*handle)
{
DEBUGOUT ("out of memory\n");
rc = -1;
goto leave;
}
(*handle)->idev = idev;
idev = NULL;
/* FIXME: Do we need to get the endpoint addresses from the
@ -508,7 +537,7 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
usb_free_match (match);
if (!rc && !*handle)
rc = -1; /* In case we didn't enter the while lool at all. */
rc = -1; /* In case we didn't enter the while loop at all. */
return rc;
}
@ -592,6 +621,7 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
int i, rc;
size_t msglen;
retry:
rc = usb_bulk_read (handle->idev,
0x82,
buffer, length,
@ -628,6 +658,14 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
return -1;
}
if ( !(buffer[7] & 0x03) && (buffer[7] & 0xC0) == 0x80)
{
/* Card present and active, time extension requested. */
DEBUGOUT_2 ("time extension requested (%02X,%02X)\n",
buffer[7], buffer[8]);
goto retry;
}
DEBUGOUT_3 ("status: %02X error: %02X octet[9]: %02X\n"
" data:", buffer[7], buffer[8], buffer[9] );
for (i=10; i < msglen; i++)
@ -695,7 +733,7 @@ ccid_poll (ccid_driver_t handle)
int
ccid_slot_status (ccid_driver_t handle)
ccid_slot_status (ccid_driver_t handle, int *statusbits)
{
int rc;
unsigned char msg[100];
@ -716,6 +754,7 @@ ccid_slot_status (ccid_driver_t handle)
rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, seqno);
if (rc)
return rc;
*statusbits = (msg[7] & 3);
return 0;
}
@ -727,8 +766,12 @@ ccid_get_atr (ccid_driver_t handle,
{
int rc;
unsigned char msg[100];
size_t msglen;
unsigned char *tpdu;
size_t msglen, tpdulen;
unsigned char seqno;
int use_crc = 0;
unsigned int edc;
int i;
msg[0] = PC_to_RDR_IccPowerOn;
msg[5] = 0; /* slot */
@ -756,11 +799,135 @@ ccid_get_atr (ccid_driver_t handle,
*atrlen = n;
}
/* Setup parameters to select T=1. */
msg[0] = PC_to_RDR_SetParameters;
msg[5] = 0; /* slot */
msg[6] = seqno = handle->seqno++;
msg[7] = 1; /* Select T=1. */
msg[8] = 0; /* RFU */
msg[9] = 0; /* RFU */
/* FIXME: Get those values from the ATR. */
msg[10]= 0x01; /* Fi/Di */
msg[11]= 0x10; /* LRC, direct convention. */
msg[12]= 0; /* Extra guardtime. */
msg[13]= 0x41; /* BWI/CWI */
msg[14]= 0; /* No clock stoppping. */
msg[15]= 254; /* IFSC */
msg[16]= 0; /* Does not support non default NAD values. */
set_msg_len (msg, 7);
msglen = 10 + 7;
DEBUGOUT ("sending");
for (i=0; i < msglen; i++)
DEBUGOUT_CONT_1 (" %02X", msg[i]);
DEBUGOUT_LF ();
rc = bulk_out (handle, msg, msglen);
if (rc)
return rc;
/* Note that we ignore the error code on purpose. */
bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters, seqno);
/* Send an S-Block with our maximun IFSD to the CCID. */
if (!handle->auto_ifsd)
{
tpdu = msg+10;
/* NAD: DAD=1, SAD=0 */
tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
tpdu[1] = (0xc0 | 0 | 1); /* S-block request: change IFSD */
tpdu[2] = 1;
tpdu[3] = handle->max_ifsd? handle->max_ifsd : 32;
tpdulen = 4;
edc = compute_edc (tpdu, tpdulen, use_crc);
if (use_crc)
tpdu[tpdulen++] = (edc >> 8);
tpdu[tpdulen++] = edc;
msg[0] = PC_to_RDR_XfrBlock;
msg[5] = 0; /* slot */
msg[6] = seqno = handle->seqno++;
msg[7] = 0;
msg[8] = 0; /* RFU */
msg[9] = 0; /* RFU */
set_msg_len (msg, tpdulen);
msglen = 10 + tpdulen;
DEBUGOUT ("sending");
for (i=0; i < msglen; i++)
DEBUGOUT_CONT_1 (" %02X", msg[i]);
DEBUGOUT_LF ();
#ifdef DEBUG_T1
fprintf (stderr, "T1: put %c-block seq=%d\n",
((msg[11] & 0xc0) == 0x80)? 'R' :
(msg[11] & 0x80)? 'S' : 'I',
((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)));
#endif
rc = bulk_out (handle, msg, msglen);
if (rc)
return rc;
/* Fixme: The next line for the current Valgrid without support
for USB IOCTLs. */
memset (msg, 0, sizeof msg);
rc = bulk_in (handle, msg, sizeof msg, &msglen,
RDR_to_PC_DataBlock, seqno);
if (rc)
return rc;
tpdu = msg + 10;
tpdulen = msglen - 10;
if (tpdulen < 4)
{
DEBUGOUT ("cannot yet handle short blocks!\n");
return -1;
}
#ifdef DEBUG_T1
fprintf (stderr, "T1: got %c-block seq=%d err=%d\n",
((msg[11] & 0xc0) == 0x80)? 'R' :
(msg[11] & 0x80)? 'S' : 'I',
((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)),
((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0
);
#endif
if ((tpdu[1] & 0xe0) != 0xe0 || tpdu[2] != 1)
{
DEBUGOUT ("invalid response for S-block (Change-IFSD)\n");
return -1;
}
DEBUGOUT_1 ("IFSD has been set to %d\n", tpdu[3]);
}
return 0;
}
static unsigned int
compute_edc (const unsigned char *data, size_t datalen, int use_crc)
{
if (use_crc)
{
return 0x42; /* Not yet implemented. */
}
else
{
unsigned char crc = 0;
for (; datalen; datalen--)
crc ^= *data++;
return crc;
}
}
/*
Protocol T=1 overview
@ -819,17 +986,19 @@ ccid_transceive (ccid_driver_t handle,
unsigned char *resp, size_t maxresplen, size_t *nresp)
{
int rc;
unsigned char send_buffer[10+258], recv_buffer[10+258];
unsigned char send_buffer[10+259], recv_buffer[10+259];
const unsigned char *apdu;
size_t apdulen;
unsigned char *msg, *tpdu, *p;
size_t msglen, tpdulen, n;
size_t msglen, tpdulen, last_tpdulen, n;
unsigned char seqno;
int i;
unsigned char crc;
unsigned int edc;
int use_crc = 0;
size_t dummy_nresp;
int next_chunk = 1;
int sending = 1;
int retries = 0;
if (!nresp)
nresp = &dummy_nresp;
@ -852,7 +1021,8 @@ ccid_transceive (ccid_driver_t handle,
return -1; /* Invalid length. */
tpdu = msg+10;
tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */
/* NAD: DAD=1, SAD=0 */
tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
tpdu[1] = ((handle->t1_ns & 1) << 6); /* I-block */
if (apdulen > 128 /* fixme: replace by ifsc */)
{
@ -863,12 +1033,11 @@ ccid_transceive (ccid_driver_t handle,
}
tpdu[2] = apdulen;
memcpy (tpdu+3, apdu, apdulen);
crc = 0;
for (i=0,p=tpdu; i < apdulen+3; i++)
crc ^= *p++;
tpdu[3+apdulen] = crc;
tpdulen = apdulen + 4;
tpdulen = 3 + apdulen;
edc = compute_edc (tpdu, tpdulen, use_crc);
if (use_crc)
tpdu[tpdulen++] = (edc >> 8);
tpdu[tpdulen++] = edc;
}
msg[0] = PC_to_RDR_XfrBlock;
@ -879,6 +1048,7 @@ ccid_transceive (ccid_driver_t handle,
msg[9] = 0; /* RFU */
set_msg_len (msg, tpdulen);
msglen = 10 + tpdulen;
last_tpdulen = tpdulen;
DEBUGOUT ("sending");
for (i=0; i < msglen; i++)
@ -926,7 +1096,7 @@ ccid_transceive (ccid_driver_t handle,
if (!(tpdu[1] & 0x80))
{ /* This is an I-block. */
retries = 0;
if (sending)
{ /* last block sent was successful. */
handle->t1_ns ^= 1;
@ -937,13 +1107,15 @@ ccid_transceive (ccid_driver_t handle,
{ /* Reponse does not match our sequence number. */
msg = send_buffer;
tpdu = msg+10;
tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */
/* NAD: DAD=1, SAD=0 */
tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4 | 2); /* R-block */
tpdu[2] = 0;
tpdulen = 3;
for (crc=0,i=0,p=tpdu; i < tpdulen; i++)
crc ^= *p++;
tpdu[tpdulen++] = crc;
edc = compute_edc (tpdu, tpdulen, use_crc);
if (use_crc)
tpdu[tpdulen++] = (edc >> 8);
tpdu[tpdulen++] = edc;
continue;
}
@ -974,20 +1146,27 @@ ccid_transceive (ccid_driver_t handle,
msg = send_buffer;
tpdu = msg+10;
tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */
/* NAD: DAD=1, SAD=0 */
tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4); /* R-block */
tpdu[2] = 0;
tpdulen = 3;
for (crc=0,i=0,p=tpdu; i < tpdulen; i++)
crc ^= *p++;
tpdu[tpdulen++] = crc;
edc = compute_edc (tpdu, tpdulen, use_crc);
if (use_crc)
tpdu[tpdulen++] = (edc >> 8);
tpdu[tpdulen++] = edc;
}
else if ((tpdu[1] & 0xc0) == 0x80)
{ /* This is a R-block. */
if ( (tpdu[1] & 0x0f))
{ /* Error: repeat last block */
if (++retries > 3)
{
DEBUGOUT ("3 failed retries\n");
return -1;
}
msg = send_buffer;
tpdulen = last_tpdulen;
}
else if (sending && !!(tpdu[1] & 0x40) == handle->t1_ns)
{ /* Reponse does not match our sequence number. */
@ -996,6 +1175,7 @@ ccid_transceive (ccid_driver_t handle,
}
else if (sending)
{ /* Send next chunk. */
retries = 0;
msg = send_buffer;
next_chunk = 1;
handle->t1_ns ^= 1;
@ -1008,6 +1188,7 @@ ccid_transceive (ccid_driver_t handle,
}
else
{ /* This is a S-block. */
retries = 0;
DEBUGOUT_2 ("T1 S-block %s received cmd=%d\n",
(tpdu[1] & 0x20)? "response": "request",
(tpdu[1] & 0x1f));
@ -1016,14 +1197,16 @@ ccid_transceive (ccid_driver_t handle,
unsigned char bwi = tpdu[3];
msg = send_buffer;
tpdu = msg+10;
tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */
/* NAD: DAD=1, SAD=0 */
tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
tpdu[1] = (0xc0 | 0x20 | 3); /* S-block response */
tpdu[2] = 1;
tpdu[3] = bwi;
tpdulen = 4;
for (crc=0,i=0,p=tpdu; i < tpdulen; i++)
crc ^= *p++;
tpdu[tpdulen++] = crc;
edc = compute_edc (tpdu, tpdulen, use_crc);
if (use_crc)
tpdu[tpdulen++] = (edc >> 8);
tpdu[tpdulen++] = edc;
DEBUGOUT_1 ("T1 waittime extension of bwi=%d\n", bwi);
}
else
@ -1043,6 +1226,7 @@ main (int argc, char **argv)
{
int rc;
ccid_driver_t ccid;
unsigned int slotstat;
rc = ccid_open_reader (&ccid, 0);
if (rc)
@ -1056,7 +1240,7 @@ main (int argc, char **argv)
ccid_poll (ccid);
fputs ("getting slot status ...\n", stderr);
rc = ccid_slot_status (ccid);
rc = ccid_slot_status (ccid, &slotstat);
if (rc)
return 1;

View File

@ -63,6 +63,7 @@ int ccid_open_reader (ccid_driver_t *handle, int readerno);
int ccid_close_reader (ccid_driver_t handle);
int ccid_get_atr (ccid_driver_t handle,
unsigned char *atr, size_t maxatrlen, size_t *atrlen);
int ccid_slot_status (ccid_driver_t handle, int *statusbits);
int ccid_transceive (ccid_driver_t handle,
const unsigned char *apdu, size_t apdulen,
unsigned char *resp, size_t maxresplen, size_t *nresp);

View File

@ -56,14 +56,11 @@ has_option (const char *line, const char *name)
}
/* Note, that this reset_notify is also used for cleanup purposes. */
/* Reset the card and free the application context. With DO_CLOSE set
to true, close the reader and don't do just a reset. */
static void
reset_notify (ASSUAN_CONTEXT ctx)
do_reset (ctrl_t ctrl, int do_close)
{
CTRL ctrl = assuan_get_pointer (ctx);
if (ctrl->card_ctx)
{
card_close (ctrl->card_ctx);
@ -73,11 +70,26 @@ reset_notify (ASSUAN_CONTEXT ctx)
}
if (ctrl->app_ctx)
{
int slot = ctrl->app_ctx->slot;
release_application (ctrl->app_ctx);
ctrl->app_ctx = NULL;
apdu_close_reader (slot);
}
if (ctrl->reader_slot != -1)
{
if (do_close || apdu_reset (ctrl->reader_slot))
{
apdu_close_reader (ctrl->reader_slot);
ctrl->reader_slot = -1;
}
}
}
static void
reset_notify (ASSUAN_CONTEXT ctx)
{
CTRL ctrl = assuan_get_pointer (ctx);
do_reset (ctrl, 0);
}
@ -92,7 +104,7 @@ option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value)
function returns an Assuan error, so don't map the error a second
time */
static AssuanError
open_card (CTRL ctrl, const char *apptype)
open_card (ctrl_t ctrl, const char *apptype)
{
int slot;
@ -101,13 +113,13 @@ open_card (CTRL ctrl, const char *apptype)
if (ctrl->card_ctx)
return 0; /* Already initialized using a card context. */
slot = apdu_open_reader (opt.reader_port);
if (ctrl->reader_slot != -1)
slot = ctrl->reader_slot;
else
slot = apdu_open_reader (opt.reader_port);
ctrl->reader_slot = slot;
if (slot != -1)
{
ctrl->app_ctx = select_application (ctrl, slot, apptype);
if (!ctrl->app_ctx)
apdu_close_reader (slot);
}
ctrl->app_ctx = select_application (ctrl, slot, apptype);
if (!ctrl->app_ctx)
{ /* No application found - fall back to old mode. */
/* Note that we should rework the old code to use the
@ -1084,6 +1096,12 @@ scd_command_handler (int listen_fd)
if (DBG_ASSUAN)
assuan_set_log_stream (ctx, log_get_stream ());
/* We open the reader right at startup so that the ticker is able to
update the status file. */
if (ctrl.reader_slot == -1)
ctrl.reader_slot = apdu_open_reader (opt.reader_port);
/* Command processing loop. */
for (;;)
{
rc = assuan_accept (ctx);
@ -1104,7 +1122,7 @@ scd_command_handler (int listen_fd)
continue;
}
}
reset_notify (ctx); /* used for cleanup */
do_reset (&ctrl, 1); /* Cleanup. */
assuan_deinit_server (ctx);
}
@ -1156,3 +1174,22 @@ send_status_info (CTRL ctrl, const char *keyword, ...)
va_end (arg_ptr);
}
void
scd_update_reader_status_file (void)
{
int slot;
int used;
unsigned int status, changed;
/* Note, that we only try to get the status, becuase it does not
make sense to wait here for a operation to complete. If we are
so busy working with the card, delays in the status file updated
are should be acceptable. */
for (slot=0; !apdu_enum_reader (slot, &used); slot++)
if (used && !apdu_get_status (slot, 0, &status, &changed))
{
log_info ("status of slot %d is %u\n", slot, status);
}
}

View File

@ -82,6 +82,9 @@ map_sw (int sw)
case SW_HOST_OUT_OF_CORE: ec = GPG_ERR_ENOMEM; break;
case SW_HOST_INV_VALUE: ec = GPG_ERR_INV_VALUE; break;
case SW_HOST_INCOMPLETE_CARD_RESPONSE: ec = GPG_ERR_CARD; break;
case SW_HOST_NOT_SUPPORTED: ec = GPG_ERR_NOT_SUPPORTED; break;
case SW_HOST_LOCKING_FAILED: ec = GPG_ERR_BUG; break;
case SW_HOST_BUSY: ec = GPG_ERR_EBUSY; break;
default:
if ((sw & 0x010000))
ec = GPG_ERR_GENERAL; /* Should not happen. */

631
scd/pcsc-wrapper.c Normal file
View File

@ -0,0 +1,631 @@
/* pcsc-wrapper.c - Wrapper for ccessing the PC/SC service
* Copyright (C) 2003, 2004 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GnuPG 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
/*
This wrapper is required to handle problems with the libpscslite
library. That library assumes that pthreads are used and fails
badly if one tries to use it with a procerss using Pth.
The operation model is pretty simple: It reads requests from stdin
and returns the answer on stdout. There is no direct mapping to the
pcsc interface but to a higher level one which resembles the code
used in scdaemon (apdu.c) when not using Pth or while running under
Windows.
The interface is binary consisting of a command tag and the length
of the parameter list. The calling process needs to pass the
version number of the interface on the command line to make sure
that both agree on the same interface. For each port a separate
instance of this process needs to be started.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <assert.h>
#include <dlfcn.h>
#define PGM "pcsc-wrapper"
/* Allow for a standalone build. */
#ifdef VERSION
#define MYVERSION_LINE PGM " (GnuPG) " VERSION
#define BUGREPORT_LINE "\nReport bugs to <bug-gnupg@gnu.org>.\n"
#else
#define MYVERSION_LINE PGM
#define BUGREPORT_LINE ""
#endif
#define DEFAULT_PCSC_DRIVER "libpcsclite.so"
static int verbose;
/* PC/SC constants and function pointer. */
#define PCSC_SCOPE_USER 0
#define PCSC_SCOPE_TERMINAL 1
#define PCSC_SCOPE_SYSTEM 2
#define PCSC_SCOPE_GLOBAL 3
#define PCSC_PROTOCOL_T0 1
#define PCSC_PROTOCOL_T1 2
#define PCSC_PROTOCOL_RAW 4
#define PCSC_SHARE_EXCLUSIVE 1
#define PCSC_SHARE_SHARED 2
#define PCSC_SHARE_DIRECT 3
#define PCSC_LEAVE_CARD 0
#define PCSC_RESET_CARD 1
#define PCSC_UNPOWER_CARD 2
#define PCSC_EJECT_CARD 3
struct pcsc_io_request_s {
unsigned long protocol;
unsigned long pci_len;
};
typedef struct pcsc_io_request_s *pcsc_io_request_t;
static int driver_is_open; /* True if the PC/SC driver has been
initialzied and is ready for
operations. The follwoing variables
are then valid. */
static unsigned long pcsc_context; /* The current PC/CS context. */
static unsigned long pcsc_card;
static unsigned long pcsc_protocol;
static unsigned char current_atr[33];
static size_t current_atrlen;
long (* pcsc_establish_context) (unsigned long scope,
const void *reserved1,
const void *reserved2,
unsigned long *r_context);
long (* pcsc_release_context) (unsigned long context);
long (* pcsc_list_readers) (unsigned long context,
const char *groups,
char *readers, unsigned long*readerslen);
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_disconnect) (unsigned long card,
unsigned long disposition);
long (* pcsc_status) (unsigned long card,
char *reader, unsigned long *readerlen,
unsigned long *r_state,
unsigned long *r_protocol,
unsigned char *atr, unsigned long *atrlen);
long (* pcsc_begin_transaction) (unsigned long card);
long (* pcsc_end_transaction) (unsigned long card);
long (* pcsc_transmit) (unsigned long card,
const pcsc_io_request_t send_pci,
const unsigned char *send_buffer,
unsigned long send_len,
pcsc_io_request_t recv_pci,
unsigned char *recv_buffer,
unsigned long *recv_len);
long (* pcsc_set_timeout) (unsigned long context,
unsigned long timeout);
static void
bad_request (const char *type)
{
fprintf (stderr, PGM ": bad `%s' request\n", type);
exit (1);
}
static void
request_failed (int err)
{
if (!err)
err = -1;
putchar (0x81); /* Simple error/success response. */
putchar (0);
putchar (0);
putchar (0);
putchar (4);
putchar ((err >> 24) & 0xff);
putchar ((err >> 16) & 0xff);
putchar ((err >> 8) & 0xff);
putchar ((err ) & 0xff);
fflush (stdout);
}
static void
request_succeeded (const void *buffer, size_t buflen)
{
size_t len;
putchar (0x81); /* Simple error/success response. */
len = 4 + buflen;
putchar ((len >> 24) & 0xff);
putchar ((len >> 16) & 0xff);
putchar ((len >> 8) & 0xff);
putchar ((len ) & 0xff);
/* Error code. */
putchar (0);
putchar (0);
putchar (0);
putchar (0);
/* Optional reponse string. */
if (buffer)
fwrite (buffer, buflen, 1, stdout);
fflush (stdout);
}
static unsigned long
read_32 (FILE *fp)
{
int c1, c2, c3, c4;
c1 = getc (stdin);
c2 = getc (stdin);
c3 = getc (stdin);
c4 = getc (stdin);
if (c1 == EOF || c2 == EOF || c3 == EOF || c4 == EOF)
{
fprintf (stderr, PGM ": premature EOF while parsing request\n");
exit (1);
}
return (c1 << 24) | (c2 << 16) | (c3 << 8) | c4;
}
static const char *
pcsc_error_string (long err)
{
const char *s;
if (!err)
return "okay";
if ((err & 0x80100000) != 0x80100000)
return "invalid PC/SC error code";
err &= 0xffff;
switch (err)
{
case 0x0002: s = "cancelled"; break;
case 0x000e: s = "can't dispose"; break;
case 0x0008: s = "insufficient buffer"; break;
case 0x0015: s = "invalid ATR"; break;
case 0x0003: s = "invalid handle"; break;
case 0x0004: s = "invalid parameter"; break;
case 0x0005: s = "invalid target"; break;
case 0x0011: s = "invalid value"; break;
case 0x0006: s = "no memory"; break;
case 0x0013: s = "comm error"; break;
case 0x0001: s = "internal error"; break;
case 0x0014: s = "unknown error"; break;
case 0x0007: s = "waited too long"; break;
case 0x0009: s = "unknown reader"; break;
case 0x000a: s = "timeout"; break;
case 0x000b: s = "sharing violation"; break;
case 0x000c: s = "no smartcard"; break;
case 0x000d: s = "unknown card"; break;
case 0x000f: s = "proto mismatch"; break;
case 0x0010: s = "not ready"; break;
case 0x0012: s = "system cancelled"; break;
case 0x0016: s = "not transacted"; break;
case 0x0017: s = "reader unavailable"; break;
case 0x0065: s = "unsupported card"; break;
case 0x0066: s = "unresponsive card"; break;
case 0x0067: s = "unpowered card"; break;
case 0x0068: s = "reset card"; break;
case 0x0069: s = "removed card"; break;
case 0x006a: s = "inserted card"; break;
case 0x001f: s = "unsupported feature"; break;
case 0x0019: s = "PCI too small"; break;
case 0x001a: s = "reader unsupported"; break;
case 0x001b: s = "duplicate reader"; break;
case 0x001c: s = "card unsupported"; break;
case 0x001d: s = "no service"; break;
case 0x001e: s = "service stopped"; break;
default: s = "unknown PC/SC error code"; break;
}
return s;
}
static void
load_pcsc_driver (const char *libname)
{
void *handle;
handle = dlopen (libname, RTLD_LAZY);
if (!handle)
{
fprintf (stderr, PGM ": failed to open driver `%s': %s",
libname, dlerror ());
exit (1);
}
pcsc_establish_context = dlsym (handle, "SCardEstablishContext");
pcsc_release_context = dlsym (handle, "SCardReleaseContext");
pcsc_list_readers = dlsym (handle, "SCardListReaders");
pcsc_connect = dlsym (handle, "SCardConnect");
pcsc_disconnect = dlsym (handle, "SCardDisconnect");
pcsc_status = dlsym (handle, "SCardStatus");
pcsc_begin_transaction = dlsym (handle, "SCardBeginTransaction");
pcsc_end_transaction = dlsym (handle, "SCardEndTransaction");
pcsc_transmit = dlsym (handle, "SCardTransmit");
pcsc_set_timeout = dlsym (handle, "SCardSetTimeout");
if (!pcsc_establish_context
|| !pcsc_release_context
|| !pcsc_list_readers
|| !pcsc_connect
|| !pcsc_disconnect
|| !pcsc_status
|| !pcsc_begin_transaction
|| !pcsc_end_transaction
|| !pcsc_transmit
/* || !pcsc_set_timeout */)
{
/* Note that set_timeout is currently not used and also not
available under Windows. */
fprintf (stderr,
"apdu_open_reader: invalid PC/SC driver "
"(%d%d%d%d%d%d%d%d%d%d)\n",
!!pcsc_establish_context,
!!pcsc_release_context,
!!pcsc_list_readers,
!!pcsc_connect,
!!pcsc_disconnect,
!!pcsc_status,
!!pcsc_begin_transaction,
!!pcsc_end_transaction,
!!pcsc_transmit,
!!pcsc_set_timeout );
dlclose (handle);
exit (1);
}
}
/* 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
ARGBUF. */
static void
handle_open (unsigned char *argbuf, size_t arglen)
{
long err;
const char * portstr;
char *list = NULL;
unsigned long nreader, listlen, atrlen;
char *p;
unsigned long card_state, card_protocol;
unsigned char atr[33];
/* Make sure there is only the port string */
if (arglen != strlen (argbuf))
bad_request ("OPEN");
portstr = argbuf;
if (driver_is_open)
{
fprintf (stderr, PGM ": PC/SC has already been opened\n");
request_failed (-1);
}
err = pcsc_establish_context (PCSC_SCOPE_SYSTEM, NULL, NULL, &pcsc_context);
if (err)
{
fprintf (stderr, PGM": pcsc_establish_context failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
request_failed (err);
return;
}
err = pcsc_list_readers (pcsc_context, NULL, NULL, &nreader);
if (!err)
{
list = malloc (nreader+1); /* Better add 1 for safety reasons. */
if (!list)
{
fprintf (stderr, PGM": error allocating memory for reader list\n");
exit (1);
}
err = pcsc_list_readers (pcsc_context, NULL, list, &nreader);
}
if (err)
{
fprintf (stderr, PGM": pcsc_list_readers failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
pcsc_release_context (pcsc_context);
free (list);
request_failed (err);
return;
}
listlen = nreader;
p = list;
while (nreader)
{
if (!*p && !p[1])
break;
fprintf (stderr, PGM": detected reader `%s'\n", p);
if (nreader < (strlen (p)+1))
{
fprintf (stderr, PGM": invalid response from pcsc_list_readers\n");
break;
}
nreader -= strlen (p)+1;
p += strlen (p) + 1;
}
err = pcsc_connect (pcsc_context,
portstr && *portstr? portstr : list,
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_release_context (pcsc_context);
free (list);
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)
{
fprintf (stderr, PGM": ATR returned by pcsc_status is too large\n");
exit (4);
}
memcpy (current_atr, atr, atrlen);
current_atrlen = atrlen;
driver_is_open = 1;
request_succeeded (current_atr, current_atrlen);
}
/* Handle a close request. We expect no arguments. We may modifiy
ARGBUF. */
static void
handle_close (unsigned char *argbuf, size_t arglen)
{
if (!driver_is_open)
{
fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
request_failed (-1);
}
pcsc_release_context (pcsc_context);
request_succeeded (NULL, 0);
}
/* Handle a transmit request. The argument is expected to be a bufer
with the APDU. We may modifiy ARGBUF. */
static void
handle_transmit (unsigned char *argbuf, size_t arglen)
{
long err;
struct pcsc_io_request_s send_pci;
unsigned long recv_len;
unsigned char buffer[1024];
/* The apdu should at least be one byte. */
if (!arglen)
bad_request ("TRANSMIT");
if (!driver_is_open)
{
fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
request_failed (-1);
}
if ((pcsc_protocol & PCSC_PROTOCOL_T1))
send_pci.protocol = PCSC_PROTOCOL_T1;
else
send_pci.protocol = PCSC_PROTOCOL_T0;
send_pci.pci_len = sizeof send_pci;
recv_len = sizeof (buffer);
err = pcsc_transmit (pcsc_card, &send_pci, argbuf, arglen,
NULL, buffer, &recv_len);
if (err)
{
if (verbose)
fprintf (stderr, PGM": pcsc_transmit failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
request_failed (err);
return;
}
request_succeeded (buffer, recv_len);
}
static void
print_version (int with_help)
{
fputs (MYVERSION_LINE "\n"
"Copyright (C) 2004 Free Software Foundation, Inc.\n"
"This program comes with ABSOLUTELY NO WARRANTY.\n"
"This is free software, and you are welcome to redistribute it\n"
"under certain conditions. See the file COPYING for details.\n",
stdout);
if (with_help)
fputs ("\n"
"Usage: " PGM " [OPTIONS] API-NUMBER [LIBNAME]\n"
"Helper to connect scdaemon to the PC/SC library\n"
"\n"
" --verbose enable extra informational output\n"
" --version print version of the program and exit\n"
" --help display this help and exit\n"
BUGREPORT_LINE, stdout );
exit (0);
}
int
main (int argc, char **argv)
{
int last_argc = -1;
int api_number = 0;
int c;
if (argc)
{
argc--; argv++;
}
while (argc && last_argc != argc )
{
last_argc = argc;
if (!strcmp (*argv, "--"))
{
argc--; argv++;
break;
}
else if (!strcmp (*argv, "--version"))
print_version (0);
else if (!strcmp (*argv, "--help"))
print_version (1);
else if (!strcmp (*argv, "--verbose"))
{
verbose = 1;
argc--; argv++;
}
}
if (argc != 1 && argc != 2)
{
fprintf (stderr, "usage: " PGM " API-NUMBER [LIBNAME]\n");
exit (1);
}
api_number = atoi (*argv);
argv++; argc--;
if (api_number != 1)
{
fprintf (stderr, PGM ": api-number %d is not valid\n", api_number);
exit (1);
}
load_pcsc_driver (argc? *argv : DEFAULT_PCSC_DRIVER);
while ((c = getc (stdin)) != EOF)
{
size_t arglen;
unsigned char argbuffer[2048];
arglen = read_32 (stdin);
if (arglen >= sizeof argbuffer - 1)
{
fprintf (stderr, PGM ": request too long\n");
exit (1);
}
if (arglen && fread (argbuffer, arglen, 1, stdin) != 1)
{
fprintf (stderr, PGM ": error reading request: %s\n",
strerror (errno));
exit (1);
}
argbuffer[arglen] = 0;
switch (c)
{
case 1:
handle_open (argbuffer, arglen);
break;
case 2:
handle_close (argbuffer, arglen);
exit (0);
break;
case 3:
handle_transmit (argbuffer, arglen);
break;
default:
fprintf (stderr, PGM ": invalid request 0x%02X\n", c);
exit (1);
}
free (argbuffer);
}
return 0;
}
/*
Local Variables:
compile-command: "gcc -Wall -g -o pcsc-wrapper pcsc-wrapper.c -ldl"
End:
*/

View File

@ -33,6 +33,9 @@
#include <sys/un.h>
#include <unistd.h>
#include <signal.h>
#ifdef USE_GNU_PTH
# include <pth.h>
#endif
#define JNLIB_NEED_LOG_LOGV
#include "scdaemon.h"
@ -131,12 +134,24 @@ static ARGPARSE_OPTS opts[] = {
static volatile int caught_fatal_sig = 0;
/* Flag to indicate that a shutdown was requested. */
static int shutdown_pending;
/* It is possible that we are currently running under setuid permissions */
static int maybe_setuid = 1;
/* Name of the communication socket */
static char socket_name[128];
#ifdef USE_GNU_PTH
/* Pth wrapper function definitions. */
GCRY_THREAD_OPTION_PTH_IMPL;
static void *ticker_thread (void *arg);
#endif /*USE_GNU_PTH*/
static const char *
my_strusage (int level)
{
@ -287,6 +302,7 @@ main (int argc, char **argv )
{
ARGPARSE_ARGS pargs;
int orig_argc;
gpg_error_t err;
int may_coredump;
char **orig_argv;
FILE *configfp = NULL;
@ -318,7 +334,18 @@ main (int argc, char **argv )
i18n_init ();
/* check that the libraries are suitable. Do it here because
/* Libgcrypt requires us to register the threading model first.
Note that this will also do the pth_init. */
#ifdef USE_GNU_PTH
err = gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth);
if (err)
{
log_fatal ("can't register GNU Pth with Libgcrypt: %s\n",
gpg_strerror (err));
}
#endif /*USE_GNU_PTH*/
/* Check that the libraries are suitable. Do it here because
the option parsing may need services of the library */
if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
{
@ -568,7 +595,21 @@ main (int argc, char **argv )
if (pipe_server)
{ /* this is the simple pipe based server */
{ /* This is the simple pipe based server */
#ifdef USE_GNU_PTH
pth_attr_t tattr;
tattr = pth_attr_new();
pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0);
pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 64*1024);
pth_attr_set (tattr, PTH_ATTR_NAME, "ticker");
if (!pth_spawn (tattr, ticker_thread, NULL))
{
log_error ("error spawning ticker thread: %s\n", strerror (errno));
scd_exit (2);
}
#endif /*USE_GNU_PTH*/
scd_command_handler (-1);
}
else if (!is_daemon)
@ -780,6 +821,115 @@ scd_exit (int rc)
void
scd_init_default_ctrl (CTRL ctrl)
{
ctrl->reader_slot = -1;
}
#ifdef USE_GNU_PTH
static void
handle_signal (int signo)
{
switch (signo)
{
case SIGHUP:
log_info ("SIGHUP received - "
"re-reading configuration and resetting cards\n");
/* reread_configuration (); */
break;
case SIGUSR1:
if (opt.verbose < 5)
opt.verbose++;
log_info ("SIGUSR1 received - verbosity set to %d\n", opt.verbose);
break;
case SIGUSR2:
if (opt.verbose)
opt.verbose--;
log_info ("SIGUSR2 received - verbosity set to %d\n", opt.verbose );
break;
case SIGTERM:
if (!shutdown_pending)
log_info ("SIGTERM received - shutting down ...\n");
else
log_info ("SIGTERM received - still %ld running threads\n",
pth_ctrl( PTH_CTRL_GETTHREADS ));
shutdown_pending++;
if (shutdown_pending > 2)
{
log_info ("shutdown forced\n");
log_info ("%s %s stopped\n", strusage(11), strusage(13) );
cleanup ();
scd_exit (0);
}
break;
case SIGINT:
log_info ("SIGINT received - immediate shutdown\n");
log_info( "%s %s stopped\n", strusage(11), strusage(13));
cleanup ();
scd_exit (0);
break;
default:
log_info ("signal %d received - no action defined\n", signo);
}
}
static void
handle_tick (void)
{
scd_update_reader_status_file ();
}
static void *
ticker_thread (void *dummy_arg)
{
pth_event_t sigs_ev, time_ev = NULL;
sigset_t sigs;
int signo;
sigemptyset (&sigs );
sigaddset (&sigs, SIGHUP);
sigaddset (&sigs, SIGUSR1);
sigaddset (&sigs, SIGUSR2);
sigaddset (&sigs, SIGINT);
sigaddset (&sigs, SIGTERM);
sigs_ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo);
for (;;)
{
if (!time_ev)
{
time_ev = pth_event (PTH_EVENT_TIME, pth_timeout (2, 0));
if (time_ev)
pth_event_concat (sigs_ev, time_ev, NULL);
}
if (pth_wait (sigs_ev) < 1)
continue;
if (
#ifdef PTH_STATUS_OCCURRED /* This is Pth 2 */
pth_event_status (sigs_ev) == PTH_STATUS_OCCURRED
#else
pth_event_occurred (sigs_ev)
#endif
)
handle_signal (signo);
/* Always run the ticker. */
if (!shutdown_pending)
{
pth_event_isolate (sigs_ev);
pth_event_free (time_ev, PTH_FREE_ALL);
time_ev = NULL;
handle_tick ();
}
}
pth_event_free (sigs_ev, PTH_FREE_ALL);
}
#endif /*USE_GNU_PTH*/

View File

@ -79,6 +79,7 @@ struct app_ctx_s;
struct server_control_s {
struct server_local_s *server_local;
int reader_slot; /* Slot of the open reader or -1 if not open. */
struct card_ctx_s *card_ctx;
struct app_ctx_s *app_ctx;
struct {
@ -101,6 +102,7 @@ void scd_init_default_ctrl (CTRL ctrl);
/*-- command.c --*/
void scd_command_handler (int);
void send_status_info (CTRL ctrl, const char *keyword, ...);
void scd_update_reader_status_file (void);
/*-- card.c --*/
int card_open (CARD *rcard);