mirror of
git://git.gnupg.org/gnupg.git
synced 2025-01-03 12:11:33 +01:00
* 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:
parent
d70bc68c30
commit
e950b01ff5
@ -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>
|
2004-03-23 Werner Koch <wk@gnupg.org>
|
||||||
|
|
||||||
* scdaemon.c (main) <gpgconf_list>: Fixed output for pcsc_driver.
|
* scdaemon.c (main) <gpgconf_list>: Fixed output for pcsc_driver.
|
||||||
|
@ -19,13 +19,14 @@
|
|||||||
## Process this file with automake to produce Makefile.in
|
## Process this file with automake to produce Makefile.in
|
||||||
|
|
||||||
bin_PROGRAMS = scdaemon sc-investigate sc-copykeys
|
bin_PROGRAMS = scdaemon sc-investigate sc-copykeys
|
||||||
|
pkglib_PROGRAMS = pcsc-wrapper
|
||||||
|
|
||||||
AM_CPPFLAGS = -I$(top_srcdir)/intl -I$(top_srcdir)/common
|
AM_CPPFLAGS = -I$(top_srcdir)/intl -I$(top_srcdir)/common
|
||||||
|
|
||||||
include $(top_srcdir)/am/cmacros.am
|
include $(top_srcdir)/am/cmacros.am
|
||||||
|
|
||||||
AM_CFLAGS = $(OPENSC_CFLAGS) $(LIBGCRYPT_CFLAGS) \
|
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
|
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 \
|
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
|
$(LIBUSB_LIBS) -lgpg-error @INTLLIBS@ -ldl
|
||||||
|
|
||||||
sc_investigate_SOURCES = \
|
sc_investigate_SOURCES = \
|
||||||
@ -57,7 +59,8 @@ sc_investigate_SOURCES = \
|
|||||||
|
|
||||||
sc_investigate_LDADD = \
|
sc_investigate_LDADD = \
|
||||||
../jnlib/libjnlib.a ../common/libcommon.a \
|
../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
|
@INTLLIBS@ -lgpg-error -ldl
|
||||||
|
|
||||||
|
|
||||||
@ -73,5 +76,10 @@ sc_copykeys_SOURCES = \
|
|||||||
sc_copykeys_LDADD = \
|
sc_copykeys_LDADD = \
|
||||||
../jnlib/libjnlib.a ../common/libcommon.a \
|
../jnlib/libjnlib.a ../common/libcommon.a \
|
||||||
../common/libsimple-pwquery.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
|
-lgpg-error @INTLLIBS@ -ldl
|
||||||
|
|
||||||
|
pcsc_wrapper_SOURCES = pcsc-wrapper.c
|
||||||
|
pcsc_wrapper_LDADD = -ldl
|
||||||
|
pcsc_wrapper_CFLAGS =
|
688
scd/apdu.c
688
scd/apdu.c
@ -1,5 +1,5 @@
|
|||||||
/* apdu.c - ISO 7816 APDU functions and low level I/O
|
/* 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.
|
* This file is part of GnuPG.
|
||||||
*
|
*
|
||||||
@ -24,6 +24,11 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#ifdef USE_GNU_PTH
|
||||||
|
# include <pth.h>
|
||||||
|
# include <unistd.h>
|
||||||
|
# include <fcntl.h>
|
||||||
|
#endif
|
||||||
#ifdef HAVE_OPENSC
|
#ifdef HAVE_OPENSC
|
||||||
# include <opensc/opensc.h>
|
# include <opensc/opensc.h>
|
||||||
#endif
|
#endif
|
||||||
@ -48,6 +53,11 @@
|
|||||||
#include "dynload.h"
|
#include "dynload.h"
|
||||||
#include "ccid-driver.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 MAX_READER 4 /* Number of readers we support concurrently. */
|
||||||
#define CARD_CONNECT_TIMEOUT 1 /* Number of seconds to wait for
|
#define CARD_CONNECT_TIMEOUT 1 /* Number of seconds to wait for
|
||||||
insertion of the card (1 = don't wait). */
|
insertion of the card (1 = don't wait). */
|
||||||
@ -59,6 +69,12 @@
|
|||||||
#define DLSTDCALL
|
#define DLSTDCALL
|
||||||
#endif
|
#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
|
/* A structure to collect information pertaining to one reader
|
||||||
slot. */
|
slot. */
|
||||||
@ -74,6 +90,11 @@ struct reader_table_s {
|
|||||||
unsigned long context;
|
unsigned long context;
|
||||||
unsigned long card;
|
unsigned long card;
|
||||||
unsigned long protocol;
|
unsigned long protocol;
|
||||||
|
#ifdef NEED_PCSC_WRAPPER
|
||||||
|
int req_fd;
|
||||||
|
int rsp_fd;
|
||||||
|
pid_t pid;
|
||||||
|
#endif /*NEED_PCSC_WRAPPER*/
|
||||||
} pcsc;
|
} pcsc;
|
||||||
#ifdef HAVE_OPENSC
|
#ifdef HAVE_OPENSC
|
||||||
int is_osc; /* We are using the OpenSC driver layer. */
|
int is_osc; /* We are using the OpenSC driver layer. */
|
||||||
@ -85,6 +106,11 @@ struct reader_table_s {
|
|||||||
int status;
|
int status;
|
||||||
unsigned char atr[33];
|
unsigned char atr[33];
|
||||||
size_t atrlen;
|
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;
|
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");
|
log_error ("new_reader_slot: out of slots\n");
|
||||||
return -1;
|
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].used = 1;
|
||||||
reader_table[reader].is_ccid = 0;
|
reader_table[reader].is_ccid = 0;
|
||||||
reader_table[reader].is_ctapi = 0;
|
reader_table[reader].is_ctapi = 0;
|
||||||
#ifdef HAVE_OPENSC
|
#ifdef HAVE_OPENSC
|
||||||
reader_table[reader].is_osc = 0;
|
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
|
#endif
|
||||||
return reader;
|
return reader;
|
||||||
}
|
}
|
||||||
@ -370,6 +412,18 @@ close_ct_reader (int slot)
|
|||||||
return 0;
|
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
|
/* Actually send the APDU of length APDULEN to SLOT and return a
|
||||||
maximum of *BUFLEN data in BUFFER, the actual retruned size will be
|
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 *
|
static const char *
|
||||||
pcsc_error_string (long err)
|
pcsc_error_string (long err)
|
||||||
{
|
{
|
||||||
@ -457,6 +571,172 @@ pcsc_error_string (long err)
|
|||||||
static int
|
static int
|
||||||
open_pcsc_reader (const char *portstr)
|
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;
|
long err;
|
||||||
int slot;
|
int slot;
|
||||||
char *list = NULL;
|
char *list = NULL;
|
||||||
@ -559,9 +839,16 @@ open_pcsc_reader (const char *portstr)
|
|||||||
|
|
||||||
dump_reader_status (slot);
|
dump_reader_status (slot);
|
||||||
return 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
|
/* Actually send the APDU of length APDULEN to SLOT and return a
|
||||||
maximum of *BUFLEN data in BUFFER, the actual returned size will be
|
maximum of *BUFLEN data in BUFFER, the actual returned size will be
|
||||||
set to BUFLEN. Returns: CT API error code. */
|
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,
|
pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
|
||||||
unsigned char *buffer, size_t *buflen)
|
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;
|
long err;
|
||||||
struct pcsc_io_request_s send_pci;
|
struct pcsc_io_request_s send_pci;
|
||||||
unsigned long recv_len;
|
unsigned long recv_len;
|
||||||
@ -591,14 +980,87 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
|
|||||||
pcsc_error_string (err), err);
|
pcsc_error_string (err), err);
|
||||||
|
|
||||||
return err? -1:0; /* FIXME: Return appropriate error code. */
|
return err? -1:0; /* FIXME: Return appropriate error code. */
|
||||||
|
#endif /*!NEED_PCSC_WRAPPER*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
close_pcsc_reader (int slot)
|
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);
|
pcsc_release_context (reader_table[slot].pcsc.context);
|
||||||
reader_table[slot].used = 0;
|
reader_table[slot].used = 0;
|
||||||
return 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
|
/* Actually send the APDU of length APDULEN to SLOT and return a
|
||||||
maximum of *BUFLEN data in BUFFER, the actual returned size will be
|
maximum of *BUFLEN data in BUFFER, the actual returned size will be
|
||||||
@ -798,6 +1300,18 @@ close_osc_reader (int slot)
|
|||||||
return 0;
|
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
|
/* 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
|
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
|
/* 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:
|
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
|
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);
|
handle = dlopen (opt.ctapi_driver, RTLD_LAZY);
|
||||||
if (!handle)
|
if (!handle)
|
||||||
{
|
{
|
||||||
log_error ("apdu_open_reader: failed to open driver: %s",
|
log_error ("apdu_open_reader: failed to open driver: %s\n",
|
||||||
dlerror ());
|
dlerror ());
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -959,12 +1512,13 @@ apdu_open_reader (const char *portstr)
|
|||||||
/* No ctAPI configured, so lets try the PC/SC API */
|
/* No ctAPI configured, so lets try the PC/SC API */
|
||||||
if (!pcsc_api_loaded)
|
if (!pcsc_api_loaded)
|
||||||
{
|
{
|
||||||
|
#ifndef NEED_PCSC_WRAPPER
|
||||||
void *handle;
|
void *handle;
|
||||||
|
|
||||||
handle = dlopen (opt.pcsc_driver, RTLD_LAZY);
|
handle = dlopen (opt.pcsc_driver, RTLD_LAZY);
|
||||||
if (!handle)
|
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 ());
|
opt.pcsc_driver, dlerror ());
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -1020,9 +1574,10 @@ apdu_open_reader (const char *portstr)
|
|||||||
dlclose (handle);
|
dlclose (handle);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
#endif /*!NEED_PCSC_WRAPPER*/
|
||||||
pcsc_api_loaded = 1;
|
pcsc_api_loaded = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return open_pcsc_reader (portstr);
|
return open_pcsc_reader (portstr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1046,6 +1601,47 @@ apdu_close_reader (int slot)
|
|||||||
return close_pcsc_reader (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 *
|
unsigned char *
|
||||||
apdu_get_atr (int slot, size_t *atrlen)
|
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;
|
*atrlen = reader_table[slot].atrlen;
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static const char *
|
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
|
static int
|
||||||
send_apdu (int slot, unsigned char *apdu, size_t apdulen,
|
send_apdu (int slot, unsigned char *apdu, size_t apdulen,
|
||||||
unsigned char *buffer, size_t *buflen)
|
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;
|
int sw;
|
||||||
long rc; /* we need a long here due to PC/SC. */
|
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)
|
if (DBG_CARD_IO)
|
||||||
log_debug ("send apdu: c=%02X i=%02X p0=%02X p1=%02X lc=%d le=%d\n",
|
log_debug ("send apdu: c=%02X i=%02X p0=%02X p1=%02X lc=%d le=%d\n",
|
||||||
class, ins, p0, p1, lc, le);
|
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))
|
if ((!data && lc != -1) || (data && lc == -1))
|
||||||
return SW_HOST_INV_VALUE;
|
return SW_HOST_INV_VALUE;
|
||||||
|
|
||||||
|
if ((sw = lock_slot (slot)))
|
||||||
|
return sw;
|
||||||
|
|
||||||
apdulen = 0;
|
apdulen = 0;
|
||||||
apdu[apdulen++] = class;
|
apdu[apdulen++] = class;
|
||||||
apdu[apdulen++] = ins;
|
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",
|
log_error ("apdu_send_simple(%d) failed: %s\n",
|
||||||
slot, error_string (slot, rc));
|
slot, error_string (slot, rc));
|
||||||
|
unlock_slot (slot);
|
||||||
return SW_HOST_INCOMPLETE_CARD_RESPONSE;
|
return SW_HOST_INCOMPLETE_CARD_RESPONSE;
|
||||||
}
|
}
|
||||||
sw = (result[resultlen-2] << 8) | result[resultlen-1];
|
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);
|
*retbuf = xtrymalloc (resultlen? resultlen : 1);
|
||||||
if (!*retbuf)
|
if (!*retbuf)
|
||||||
return SW_HOST_OUT_OF_CORE;
|
{
|
||||||
|
unlock_slot (slot);
|
||||||
|
return SW_HOST_OUT_OF_CORE;
|
||||||
|
}
|
||||||
*retbuflen = resultlen;
|
*retbuflen = resultlen;
|
||||||
memcpy (*retbuf, result, 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);
|
*retbuf = p = xtrymalloc (bufsize);
|
||||||
if (!*retbuf)
|
if (!*retbuf)
|
||||||
return SW_HOST_OUT_OF_CORE;
|
{
|
||||||
|
unlock_slot (slot);
|
||||||
|
return SW_HOST_OUT_OF_CORE;
|
||||||
|
}
|
||||||
assert (resultlen < bufsize);
|
assert (resultlen < bufsize);
|
||||||
memcpy (p, result, resultlen);
|
memcpy (p, result, resultlen);
|
||||||
p += 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",
|
log_error ("apdu_send_simple(%d) for get response failed: %s\n",
|
||||||
slot, error_string (slot, rc));
|
slot, error_string (slot, rc));
|
||||||
|
unlock_slot (slot);
|
||||||
return SW_HOST_INCOMPLETE_CARD_RESPONSE;
|
return SW_HOST_INCOMPLETE_CARD_RESPONSE;
|
||||||
}
|
}
|
||||||
sw = (result[resultlen-2] << 8) | result[resultlen-1];
|
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;
|
bufsize += resultlen > 4096? resultlen: 4096;
|
||||||
tmp = xtryrealloc (*retbuf, bufsize);
|
tmp = xtryrealloc (*retbuf, bufsize);
|
||||||
if (!tmp)
|
if (!tmp)
|
||||||
return SW_HOST_OUT_OF_CORE;
|
{
|
||||||
|
unlock_slot (slot);
|
||||||
|
return SW_HOST_OUT_OF_CORE;
|
||||||
|
}
|
||||||
p = tmp + (p - *retbuf);
|
p = tmp + (p - *retbuf);
|
||||||
*retbuf = tmp;
|
*retbuf = tmp;
|
||||||
}
|
}
|
||||||
@ -1259,6 +1928,9 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
|
|||||||
*retbuf = tmp;
|
*retbuf = tmp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unlock_slot (slot);
|
||||||
|
|
||||||
if (DBG_CARD_IO && retbuf && sw == SW_SUCCESS)
|
if (DBG_CARD_IO && retbuf && sw == SW_SUCCESS)
|
||||||
log_printhex (" dump: ", *retbuf, *retbuflen);
|
log_printhex (" dump: ", *retbuf, *retbuflen);
|
||||||
|
|
||||||
|
11
scd/apdu.h
11
scd/apdu.h
@ -48,9 +48,12 @@ enum {
|
|||||||
those values can't be issued by a card. */
|
those values can't be issued by a card. */
|
||||||
SW_HOST_OUT_OF_CORE = 0x10001, /* No way yet to differentiate
|
SW_HOST_OUT_OF_CORE = 0x10001, /* No way yet to differentiate
|
||||||
between errnos on a failed malloc. */
|
between errnos on a failed malloc. */
|
||||||
SW_HOST_INV_VALUE = 0x10002,
|
SW_HOST_INV_VALUE = 0x10002,
|
||||||
SW_HOST_INCOMPLETE_CARD_RESPONSE = 0x10003,
|
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. */
|
/* Note , that apdu_open_reader returns no status word but -1 on error. */
|
||||||
int apdu_open_reader (const char *portstr);
|
int apdu_open_reader (const char *portstr);
|
||||||
int apdu_close_reader (int slot);
|
int apdu_close_reader (int slot);
|
||||||
|
int apdu_enum_reader (int slot, int *used);
|
||||||
unsigned char *apdu_get_atr (int slot, size_t *atrlen);
|
unsigned char *apdu_get_atr (int slot, size_t *atrlen);
|
||||||
|
|
||||||
|
|
||||||
/* The apdu send functions do return status words. */
|
/* 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 apdu_send_simple (int slot, int class, int ins, int p0, int p1,
|
||||||
int lc, const char *data);
|
int lc, const char *data);
|
||||||
int apdu_send (int slot, int class, int ins, int p0, int p1,
|
int apdu_send (int slot, int class, int ins, int p0, int p1,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* ccid-driver.c - USB ChipCardInterfaceDevices driver
|
/* 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.
|
* Written by Werner Koch.
|
||||||
*
|
*
|
||||||
* This file is part of GnuPG.
|
* This file is part of GnuPG.
|
||||||
@ -108,7 +108,10 @@
|
|||||||
|
|
||||||
/* Disable all debugging output for now. */
|
/* Disable all debugging output for now. */
|
||||||
#undef DBG_CARD_IO
|
#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) \
|
# define DEBUGOUT(t) do { if (DBG_CARD_IO) \
|
||||||
@ -145,8 +148,6 @@
|
|||||||
#endif /* This source not used by scdaemon. */
|
#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;
|
int seqno;
|
||||||
unsigned char t1_ns;
|
unsigned char t1_ns;
|
||||||
unsigned char t1_nr;
|
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_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,
|
static int bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
|
||||||
size_t *nread, int expected_type, int seqno);
|
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
|
Note, that this code is based on the one in lsusb.c of the
|
||||||
usb-utils package, I wrote on 2003-09-01. -wk. */
|
usb-utils package, I wrote on 2003-09-01. -wk. */
|
||||||
static int
|
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 i;
|
||||||
unsigned int us;
|
unsigned int us;
|
||||||
int have_t1 = 0, have_tpdu=0, have_auto_conf = 0;
|
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)
|
if (buflen < 54 || buf[0] < 54)
|
||||||
{
|
{
|
||||||
DEBUGOUT ("CCID device descriptor is too short\n");
|
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);
|
us = convert_le_u32(buf+28);
|
||||||
DEBUGOUT_1 (" dwMaxIFSD %5u\n", us);
|
DEBUGOUT_1 (" dwMaxIFSD %5u\n", us);
|
||||||
|
handle->max_ifsd = us;
|
||||||
|
|
||||||
us = convert_le_u32(buf+32);
|
us = convert_le_u32(buf+32);
|
||||||
DEBUGOUT_1 (" dwSyncProtocols %08X ", us);
|
DEBUGOUT_1 (" dwSyncProtocols %08X ", us);
|
||||||
@ -320,9 +333,15 @@ parse_ccid_descriptor (const unsigned char *buf, size_t buflen)
|
|||||||
if ((us & 0x0100))
|
if ((us & 0x0100))
|
||||||
DEBUGOUT (" CCID can set ICC in clock stop mode\n");
|
DEBUGOUT (" CCID can set ICC in clock stop mode\n");
|
||||||
if ((us & 0x0200))
|
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))
|
if ((us & 0x0400))
|
||||||
DEBUGOUT (" Auto IFSD exchange\n");
|
{
|
||||||
|
DEBUGOUT (" Auto IFSD exchange\n");
|
||||||
|
handle->auto_ifsd = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if ((us & 0x00010000))
|
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
|
that the device is usable for us. Returns 0 on success or an error
|
||||||
code. */
|
code. */
|
||||||
static int
|
static int
|
||||||
read_device_info (struct usb_device *dev)
|
read_device_info (ccid_driver_t handle, struct usb_device *dev)
|
||||||
{
|
{
|
||||||
int cfg_no;
|
int cfg_no;
|
||||||
|
|
||||||
@ -414,8 +433,9 @@ read_device_info (struct usb_device *dev)
|
|||||||
{
|
{
|
||||||
if (ifcdesc->extra)
|
if (ifcdesc->extra)
|
||||||
{
|
{
|
||||||
if (!parse_ccid_descriptor (ifcdesc->extra,
|
if (!parse_ccid_descriptor (handle,
|
||||||
ifcdesc->extralen))
|
ifcdesc->extra,
|
||||||
|
ifcdesc->extralen))
|
||||||
return 0; /* okay. we can use it. */
|
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);
|
dev->descriptor->idVendor, dev->descriptor->idProduct);
|
||||||
if (!readerno)
|
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)
|
if (rc)
|
||||||
{
|
{
|
||||||
DEBUGOUT ("device not supported\n");
|
DEBUGOUT ("device not supported\n");
|
||||||
|
free (*handle);
|
||||||
|
*handle = NULL;
|
||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -469,6 +501,8 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
|
|||||||
if (rc)
|
if (rc)
|
||||||
{
|
{
|
||||||
DEBUGOUT_1 ("usb_open failed: %d\n", rc);
|
DEBUGOUT_1 ("usb_open failed: %d\n", rc);
|
||||||
|
free (*handle);
|
||||||
|
*handle = NULL;
|
||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -479,16 +513,11 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
|
|||||||
if (rc)
|
if (rc)
|
||||||
{
|
{
|
||||||
DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
|
DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
|
||||||
|
free (*handle);
|
||||||
|
*handle = NULL;
|
||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
|
|
||||||
*handle = calloc (1, sizeof **handle);
|
|
||||||
if (!*handle)
|
|
||||||
{
|
|
||||||
DEBUGOUT ("out of memory\n");
|
|
||||||
rc = -1;
|
|
||||||
goto leave;
|
|
||||||
}
|
|
||||||
(*handle)->idev = idev;
|
(*handle)->idev = idev;
|
||||||
idev = NULL;
|
idev = NULL;
|
||||||
/* FIXME: Do we need to get the endpoint addresses from the
|
/* 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);
|
usb_free_match (match);
|
||||||
|
|
||||||
if (!rc && !*handle)
|
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;
|
return rc;
|
||||||
}
|
}
|
||||||
@ -592,6 +621,7 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
|
|||||||
int i, rc;
|
int i, rc;
|
||||||
size_t msglen;
|
size_t msglen;
|
||||||
|
|
||||||
|
retry:
|
||||||
rc = usb_bulk_read (handle->idev,
|
rc = usb_bulk_read (handle->idev,
|
||||||
0x82,
|
0x82,
|
||||||
buffer, length,
|
buffer, length,
|
||||||
@ -628,6 +658,14 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
|
|||||||
return -1;
|
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"
|
DEBUGOUT_3 ("status: %02X error: %02X octet[9]: %02X\n"
|
||||||
" data:", buffer[7], buffer[8], buffer[9] );
|
" data:", buffer[7], buffer[8], buffer[9] );
|
||||||
for (i=10; i < msglen; i++)
|
for (i=10; i < msglen; i++)
|
||||||
@ -695,7 +733,7 @@ ccid_poll (ccid_driver_t handle)
|
|||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
ccid_slot_status (ccid_driver_t handle)
|
ccid_slot_status (ccid_driver_t handle, int *statusbits)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
unsigned char msg[100];
|
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);
|
rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, seqno);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
*statusbits = (msg[7] & 3);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -727,8 +766,12 @@ ccid_get_atr (ccid_driver_t handle,
|
|||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
unsigned char msg[100];
|
unsigned char msg[100];
|
||||||
size_t msglen;
|
unsigned char *tpdu;
|
||||||
|
size_t msglen, tpdulen;
|
||||||
unsigned char seqno;
|
unsigned char seqno;
|
||||||
|
int use_crc = 0;
|
||||||
|
unsigned int edc;
|
||||||
|
int i;
|
||||||
|
|
||||||
msg[0] = PC_to_RDR_IccPowerOn;
|
msg[0] = PC_to_RDR_IccPowerOn;
|
||||||
msg[5] = 0; /* slot */
|
msg[5] = 0; /* slot */
|
||||||
@ -756,11 +799,135 @@ ccid_get_atr (ccid_driver_t handle,
|
|||||||
*atrlen = n;
|
*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;
|
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
|
Protocol T=1 overview
|
||||||
|
|
||||||
@ -819,17 +986,19 @@ ccid_transceive (ccid_driver_t handle,
|
|||||||
unsigned char *resp, size_t maxresplen, size_t *nresp)
|
unsigned char *resp, size_t maxresplen, size_t *nresp)
|
||||||
{
|
{
|
||||||
int rc;
|
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;
|
const unsigned char *apdu;
|
||||||
size_t apdulen;
|
size_t apdulen;
|
||||||
unsigned char *msg, *tpdu, *p;
|
unsigned char *msg, *tpdu, *p;
|
||||||
size_t msglen, tpdulen, n;
|
size_t msglen, tpdulen, last_tpdulen, n;
|
||||||
unsigned char seqno;
|
unsigned char seqno;
|
||||||
int i;
|
int i;
|
||||||
unsigned char crc;
|
unsigned int edc;
|
||||||
|
int use_crc = 0;
|
||||||
size_t dummy_nresp;
|
size_t dummy_nresp;
|
||||||
int next_chunk = 1;
|
int next_chunk = 1;
|
||||||
int sending = 1;
|
int sending = 1;
|
||||||
|
int retries = 0;
|
||||||
|
|
||||||
if (!nresp)
|
if (!nresp)
|
||||||
nresp = &dummy_nresp;
|
nresp = &dummy_nresp;
|
||||||
@ -852,7 +1021,8 @@ ccid_transceive (ccid_driver_t handle,
|
|||||||
return -1; /* Invalid length. */
|
return -1; /* Invalid length. */
|
||||||
|
|
||||||
tpdu = msg+10;
|
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 */
|
tpdu[1] = ((handle->t1_ns & 1) << 6); /* I-block */
|
||||||
if (apdulen > 128 /* fixme: replace by ifsc */)
|
if (apdulen > 128 /* fixme: replace by ifsc */)
|
||||||
{
|
{
|
||||||
@ -863,12 +1033,11 @@ ccid_transceive (ccid_driver_t handle,
|
|||||||
}
|
}
|
||||||
tpdu[2] = apdulen;
|
tpdu[2] = apdulen;
|
||||||
memcpy (tpdu+3, apdu, apdulen);
|
memcpy (tpdu+3, apdu, apdulen);
|
||||||
crc = 0;
|
tpdulen = 3 + apdulen;
|
||||||
for (i=0,p=tpdu; i < apdulen+3; i++)
|
edc = compute_edc (tpdu, tpdulen, use_crc);
|
||||||
crc ^= *p++;
|
if (use_crc)
|
||||||
tpdu[3+apdulen] = crc;
|
tpdu[tpdulen++] = (edc >> 8);
|
||||||
|
tpdu[tpdulen++] = edc;
|
||||||
tpdulen = apdulen + 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
msg[0] = PC_to_RDR_XfrBlock;
|
msg[0] = PC_to_RDR_XfrBlock;
|
||||||
@ -879,6 +1048,7 @@ ccid_transceive (ccid_driver_t handle,
|
|||||||
msg[9] = 0; /* RFU */
|
msg[9] = 0; /* RFU */
|
||||||
set_msg_len (msg, tpdulen);
|
set_msg_len (msg, tpdulen);
|
||||||
msglen = 10 + tpdulen;
|
msglen = 10 + tpdulen;
|
||||||
|
last_tpdulen = tpdulen;
|
||||||
|
|
||||||
DEBUGOUT ("sending");
|
DEBUGOUT ("sending");
|
||||||
for (i=0; i < msglen; i++)
|
for (i=0; i < msglen; i++)
|
||||||
@ -926,7 +1096,7 @@ ccid_transceive (ccid_driver_t handle,
|
|||||||
|
|
||||||
if (!(tpdu[1] & 0x80))
|
if (!(tpdu[1] & 0x80))
|
||||||
{ /* This is an I-block. */
|
{ /* This is an I-block. */
|
||||||
|
retries = 0;
|
||||||
if (sending)
|
if (sending)
|
||||||
{ /* last block sent was successful. */
|
{ /* last block sent was successful. */
|
||||||
handle->t1_ns ^= 1;
|
handle->t1_ns ^= 1;
|
||||||
@ -937,13 +1107,15 @@ ccid_transceive (ccid_driver_t handle,
|
|||||||
{ /* Reponse does not match our sequence number. */
|
{ /* Reponse does not match our sequence number. */
|
||||||
msg = send_buffer;
|
msg = send_buffer;
|
||||||
tpdu = msg+10;
|
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[1] = (0x80 | (handle->t1_nr & 1) << 4 | 2); /* R-block */
|
||||||
tpdu[2] = 0;
|
tpdu[2] = 0;
|
||||||
tpdulen = 3;
|
tpdulen = 3;
|
||||||
for (crc=0,i=0,p=tpdu; i < tpdulen; i++)
|
edc = compute_edc (tpdu, tpdulen, use_crc);
|
||||||
crc ^= *p++;
|
if (use_crc)
|
||||||
tpdu[tpdulen++] = crc;
|
tpdu[tpdulen++] = (edc >> 8);
|
||||||
|
tpdu[tpdulen++] = edc;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -974,20 +1146,27 @@ ccid_transceive (ccid_driver_t handle,
|
|||||||
|
|
||||||
msg = send_buffer;
|
msg = send_buffer;
|
||||||
tpdu = msg+10;
|
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[1] = (0x80 | (handle->t1_nr & 1) << 4); /* R-block */
|
||||||
tpdu[2] = 0;
|
tpdu[2] = 0;
|
||||||
tpdulen = 3;
|
tpdulen = 3;
|
||||||
for (crc=0,i=0,p=tpdu; i < tpdulen; i++)
|
edc = compute_edc (tpdu, tpdulen, use_crc);
|
||||||
crc ^= *p++;
|
if (use_crc)
|
||||||
tpdu[tpdulen++] = crc;
|
tpdu[tpdulen++] = (edc >> 8);
|
||||||
|
tpdu[tpdulen++] = edc;
|
||||||
}
|
}
|
||||||
else if ((tpdu[1] & 0xc0) == 0x80)
|
else if ((tpdu[1] & 0xc0) == 0x80)
|
||||||
{ /* This is a R-block. */
|
{ /* This is a R-block. */
|
||||||
if ( (tpdu[1] & 0x0f))
|
if ( (tpdu[1] & 0x0f))
|
||||||
{ /* Error: repeat last block */
|
{ /* Error: repeat last block */
|
||||||
|
if (++retries > 3)
|
||||||
|
{
|
||||||
|
DEBUGOUT ("3 failed retries\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
msg = send_buffer;
|
msg = send_buffer;
|
||||||
|
tpdulen = last_tpdulen;
|
||||||
}
|
}
|
||||||
else if (sending && !!(tpdu[1] & 0x40) == handle->t1_ns)
|
else if (sending && !!(tpdu[1] & 0x40) == handle->t1_ns)
|
||||||
{ /* Reponse does not match our sequence number. */
|
{ /* Reponse does not match our sequence number. */
|
||||||
@ -996,6 +1175,7 @@ ccid_transceive (ccid_driver_t handle,
|
|||||||
}
|
}
|
||||||
else if (sending)
|
else if (sending)
|
||||||
{ /* Send next chunk. */
|
{ /* Send next chunk. */
|
||||||
|
retries = 0;
|
||||||
msg = send_buffer;
|
msg = send_buffer;
|
||||||
next_chunk = 1;
|
next_chunk = 1;
|
||||||
handle->t1_ns ^= 1;
|
handle->t1_ns ^= 1;
|
||||||
@ -1008,6 +1188,7 @@ ccid_transceive (ccid_driver_t handle,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{ /* This is a S-block. */
|
{ /* This is a S-block. */
|
||||||
|
retries = 0;
|
||||||
DEBUGOUT_2 ("T1 S-block %s received cmd=%d\n",
|
DEBUGOUT_2 ("T1 S-block %s received cmd=%d\n",
|
||||||
(tpdu[1] & 0x20)? "response": "request",
|
(tpdu[1] & 0x20)? "response": "request",
|
||||||
(tpdu[1] & 0x1f));
|
(tpdu[1] & 0x1f));
|
||||||
@ -1016,14 +1197,16 @@ ccid_transceive (ccid_driver_t handle,
|
|||||||
unsigned char bwi = tpdu[3];
|
unsigned char bwi = tpdu[3];
|
||||||
msg = send_buffer;
|
msg = send_buffer;
|
||||||
tpdu = msg+10;
|
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[1] = (0xc0 | 0x20 | 3); /* S-block response */
|
||||||
tpdu[2] = 1;
|
tpdu[2] = 1;
|
||||||
tpdu[3] = bwi;
|
tpdu[3] = bwi;
|
||||||
tpdulen = 4;
|
tpdulen = 4;
|
||||||
for (crc=0,i=0,p=tpdu; i < tpdulen; i++)
|
edc = compute_edc (tpdu, tpdulen, use_crc);
|
||||||
crc ^= *p++;
|
if (use_crc)
|
||||||
tpdu[tpdulen++] = crc;
|
tpdu[tpdulen++] = (edc >> 8);
|
||||||
|
tpdu[tpdulen++] = edc;
|
||||||
DEBUGOUT_1 ("T1 waittime extension of bwi=%d\n", bwi);
|
DEBUGOUT_1 ("T1 waittime extension of bwi=%d\n", bwi);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1043,6 +1226,7 @@ main (int argc, char **argv)
|
|||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
ccid_driver_t ccid;
|
ccid_driver_t ccid;
|
||||||
|
unsigned int slotstat;
|
||||||
|
|
||||||
rc = ccid_open_reader (&ccid, 0);
|
rc = ccid_open_reader (&ccid, 0);
|
||||||
if (rc)
|
if (rc)
|
||||||
@ -1056,7 +1240,7 @@ main (int argc, char **argv)
|
|||||||
|
|
||||||
ccid_poll (ccid);
|
ccid_poll (ccid);
|
||||||
fputs ("getting slot status ...\n", stderr);
|
fputs ("getting slot status ...\n", stderr);
|
||||||
rc = ccid_slot_status (ccid);
|
rc = ccid_slot_status (ccid, &slotstat);
|
||||||
if (rc)
|
if (rc)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
@ -63,6 +63,7 @@ int ccid_open_reader (ccid_driver_t *handle, int readerno);
|
|||||||
int ccid_close_reader (ccid_driver_t handle);
|
int ccid_close_reader (ccid_driver_t handle);
|
||||||
int ccid_get_atr (ccid_driver_t handle,
|
int ccid_get_atr (ccid_driver_t handle,
|
||||||
unsigned char *atr, size_t maxatrlen, size_t *atrlen);
|
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,
|
int ccid_transceive (ccid_driver_t handle,
|
||||||
const unsigned char *apdu, size_t apdulen,
|
const unsigned char *apdu, size_t apdulen,
|
||||||
unsigned char *resp, size_t maxresplen, size_t *nresp);
|
unsigned char *resp, size_t maxresplen, size_t *nresp);
|
||||||
|
@ -56,14 +56,11 @@ has_option (const char *line, const char *name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Reset the card and free the application context. With DO_CLOSE set
|
||||||
|
to true, close the reader and don't do just a reset. */
|
||||||
/* Note, that this reset_notify is also used for cleanup purposes. */
|
|
||||||
static void
|
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)
|
if (ctrl->card_ctx)
|
||||||
{
|
{
|
||||||
card_close (ctrl->card_ctx);
|
card_close (ctrl->card_ctx);
|
||||||
@ -73,11 +70,26 @@ reset_notify (ASSUAN_CONTEXT ctx)
|
|||||||
}
|
}
|
||||||
if (ctrl->app_ctx)
|
if (ctrl->app_ctx)
|
||||||
{
|
{
|
||||||
int slot = ctrl->app_ctx->slot;
|
|
||||||
release_application (ctrl->app_ctx);
|
release_application (ctrl->app_ctx);
|
||||||
ctrl->app_ctx = NULL;
|
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
|
function returns an Assuan error, so don't map the error a second
|
||||||
time */
|
time */
|
||||||
static AssuanError
|
static AssuanError
|
||||||
open_card (CTRL ctrl, const char *apptype)
|
open_card (ctrl_t ctrl, const char *apptype)
|
||||||
{
|
{
|
||||||
int slot;
|
int slot;
|
||||||
|
|
||||||
@ -101,13 +113,13 @@ open_card (CTRL ctrl, const char *apptype)
|
|||||||
if (ctrl->card_ctx)
|
if (ctrl->card_ctx)
|
||||||
return 0; /* Already initialized using a card context. */
|
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)
|
if (slot != -1)
|
||||||
{
|
ctrl->app_ctx = select_application (ctrl, slot, apptype);
|
||||||
ctrl->app_ctx = select_application (ctrl, slot, apptype);
|
|
||||||
if (!ctrl->app_ctx)
|
|
||||||
apdu_close_reader (slot);
|
|
||||||
}
|
|
||||||
if (!ctrl->app_ctx)
|
if (!ctrl->app_ctx)
|
||||||
{ /* No application found - fall back to old mode. */
|
{ /* No application found - fall back to old mode. */
|
||||||
/* Note that we should rework the old code to use the
|
/* Note that we should rework the old code to use the
|
||||||
@ -1084,6 +1096,12 @@ scd_command_handler (int listen_fd)
|
|||||||
if (DBG_ASSUAN)
|
if (DBG_ASSUAN)
|
||||||
assuan_set_log_stream (ctx, log_get_stream ());
|
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 (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
rc = assuan_accept (ctx);
|
rc = assuan_accept (ctx);
|
||||||
@ -1104,7 +1122,7 @@ scd_command_handler (int listen_fd)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reset_notify (ctx); /* used for cleanup */
|
do_reset (&ctrl, 1); /* Cleanup. */
|
||||||
|
|
||||||
assuan_deinit_server (ctx);
|
assuan_deinit_server (ctx);
|
||||||
}
|
}
|
||||||
@ -1156,3 +1174,22 @@ send_status_info (CTRL ctrl, const char *keyword, ...)
|
|||||||
va_end (arg_ptr);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -82,6 +82,9 @@ map_sw (int sw)
|
|||||||
case SW_HOST_OUT_OF_CORE: ec = GPG_ERR_ENOMEM; break;
|
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_INV_VALUE: ec = GPG_ERR_INV_VALUE; break;
|
||||||
case SW_HOST_INCOMPLETE_CARD_RESPONSE: ec = GPG_ERR_CARD; 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:
|
default:
|
||||||
if ((sw & 0x010000))
|
if ((sw & 0x010000))
|
||||||
ec = GPG_ERR_GENERAL; /* Should not happen. */
|
ec = GPG_ERR_GENERAL; /* Should not happen. */
|
||||||
|
631
scd/pcsc-wrapper.c
Normal file
631
scd/pcsc-wrapper.c
Normal 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:
|
||||||
|
*/
|
156
scd/scdaemon.c
156
scd/scdaemon.c
@ -33,6 +33,9 @@
|
|||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#ifdef USE_GNU_PTH
|
||||||
|
# include <pth.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define JNLIB_NEED_LOG_LOGV
|
#define JNLIB_NEED_LOG_LOGV
|
||||||
#include "scdaemon.h"
|
#include "scdaemon.h"
|
||||||
@ -131,12 +134,24 @@ static ARGPARSE_OPTS opts[] = {
|
|||||||
|
|
||||||
static volatile int caught_fatal_sig = 0;
|
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 */
|
/* It is possible that we are currently running under setuid permissions */
|
||||||
static int maybe_setuid = 1;
|
static int maybe_setuid = 1;
|
||||||
|
|
||||||
/* Name of the communication socket */
|
/* Name of the communication socket */
|
||||||
static char socket_name[128];
|
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 *
|
static const char *
|
||||||
my_strusage (int level)
|
my_strusage (int level)
|
||||||
{
|
{
|
||||||
@ -287,6 +302,7 @@ main (int argc, char **argv )
|
|||||||
{
|
{
|
||||||
ARGPARSE_ARGS pargs;
|
ARGPARSE_ARGS pargs;
|
||||||
int orig_argc;
|
int orig_argc;
|
||||||
|
gpg_error_t err;
|
||||||
int may_coredump;
|
int may_coredump;
|
||||||
char **orig_argv;
|
char **orig_argv;
|
||||||
FILE *configfp = NULL;
|
FILE *configfp = NULL;
|
||||||
@ -318,7 +334,18 @@ main (int argc, char **argv )
|
|||||||
|
|
||||||
i18n_init ();
|
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 */
|
the option parsing may need services of the library */
|
||||||
if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
|
if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
|
||||||
{
|
{
|
||||||
@ -568,7 +595,21 @@ main (int argc, char **argv )
|
|||||||
|
|
||||||
|
|
||||||
if (pipe_server)
|
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);
|
scd_command_handler (-1);
|
||||||
}
|
}
|
||||||
else if (!is_daemon)
|
else if (!is_daemon)
|
||||||
@ -780,6 +821,115 @@ scd_exit (int rc)
|
|||||||
void
|
void
|
||||||
scd_init_default_ctrl (CTRL ctrl)
|
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*/
|
||||||
|
@ -79,6 +79,7 @@ struct app_ctx_s;
|
|||||||
|
|
||||||
struct server_control_s {
|
struct server_control_s {
|
||||||
struct server_local_s *server_local;
|
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 card_ctx_s *card_ctx;
|
||||||
struct app_ctx_s *app_ctx;
|
struct app_ctx_s *app_ctx;
|
||||||
struct {
|
struct {
|
||||||
@ -101,6 +102,7 @@ void scd_init_default_ctrl (CTRL ctrl);
|
|||||||
/*-- command.c --*/
|
/*-- command.c --*/
|
||||||
void scd_command_handler (int);
|
void scd_command_handler (int);
|
||||||
void send_status_info (CTRL ctrl, const char *keyword, ...);
|
void send_status_info (CTRL ctrl, const char *keyword, ...);
|
||||||
|
void scd_update_reader_status_file (void);
|
||||||
|
|
||||||
/*-- card.c --*/
|
/*-- card.c --*/
|
||||||
int card_open (CARD *rcard);
|
int card_open (CARD *rcard);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user