mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
* sc-investigate: Removed.
* Makefile.am (sc_investigate): Removed. * pcsc-wrapper.c (load_pcsc_driver): Load get_status_change func. (handle_open): Succeed even without a present card. (handle_status, handle_reset): New. * apdu.c (apdu_open_reader): Load pcsc_get_status_change fucntion. (pcsc_get_status): Implemented. (reset_pcsc_reader): Implemented. (open_pcsc_reader): Succeed even with no card inserted. (open_ccid_reader): Set LAST_STATUS. * iso7816.c (iso7816_select_application): Always use 0 for P1.
This commit is contained in:
parent
837c74f7e4
commit
d33703e5fe
4
NEWS
4
NEWS
@ -1,6 +1,10 @@
|
||||
Noteworthy changes in version 1.9.12
|
||||
-------------------------------------------------
|
||||
|
||||
* [scdaemon] Partly rewrote the PC/SC code.
|
||||
|
||||
* Removed the sc-investigate tool. It is now in the separate
|
||||
gscutils package.
|
||||
|
||||
|
||||
Noteworthy changes in version 1.9.11 (2004-10-01)
|
||||
|
2
README
2
README
@ -472,7 +472,7 @@ HOW TO EXPORT A PRIVATE KEY
|
||||
There is also limited support to export a private key in PKCS-12
|
||||
format. However there is no MAC applied.
|
||||
|
||||
gpgsm --export-secret-key-p12 userIDey >foo.p12
|
||||
gpgsm --export-secret-key-p12 userID >foo.p12
|
||||
|
||||
|
||||
SMARTCARD INTRO
|
||||
|
@ -1,3 +1,8 @@
|
||||
2004-10-05 Werner Koch <wk@g10code.com>
|
||||
|
||||
* gpg-agent.texi (Invoking GPG-AGENT): Tell that GPG_TTY needs to
|
||||
be set in all cases.
|
||||
|
||||
2004-09-30 Werner Koch <wk@g10code.com>
|
||||
|
||||
* gpg.texi: New.
|
||||
|
@ -43,10 +43,8 @@ fi
|
||||
@end smallexample
|
||||
|
||||
@noindent
|
||||
If you want to use a curses based pinentry (which is usually also the
|
||||
fallback mode for a GUI based pinentry), you should add these lines to
|
||||
your @code{.bashrc} or whatever initialization file is used for all shell
|
||||
invocations:
|
||||
You should aleays add the following lines to your @code{.bashrc} or
|
||||
whatever initialization file is used for all shell invocations:
|
||||
|
||||
@smallexample
|
||||
GPG_TTY=`tty`
|
||||
|
@ -170,7 +170,8 @@ default is 32768 (first USB device).
|
||||
|
||||
@item --ctapi-driver @var{library}
|
||||
Use @var{library} to access the smartcard reader. The current default
|
||||
is @code{libtowitoko.so}.
|
||||
is @code{libtowitoko.so}. Note that the use of this interface is
|
||||
deprecated; it may be removed in future releases.
|
||||
|
||||
|
||||
@item --allow-admin
|
||||
|
@ -1,3 +1,24 @@
|
||||
2004-10-20 Werner Koch <wk@g10code.com>
|
||||
|
||||
* sc-investigate: Removed.
|
||||
* Makefile.am (sc_investigate): Removed.
|
||||
|
||||
* pcsc-wrapper.c (load_pcsc_driver): Load get_status_change func.
|
||||
(handle_open): Succeed even without a present card.
|
||||
(handle_status, handle_reset): New.
|
||||
|
||||
* apdu.c (apdu_open_reader): Load pcsc_get_status_change fucntion.
|
||||
(pcsc_get_status): Implemented.
|
||||
(reset_pcsc_reader): Implemented.
|
||||
(open_pcsc_reader): Succeed even with no card inserted.
|
||||
(open_ccid_reader): Set LAST_STATUS.
|
||||
|
||||
* iso7816.c (iso7816_select_application): Always use 0 for P1.
|
||||
|
||||
2004-10-18 Werner Koch <wk@g10code.com>
|
||||
|
||||
* ccid-driver.c (ccid_get_atr): Reset T=1 state info.
|
||||
|
||||
2004-10-14 Werner Koch <wk@g10code.com>
|
||||
|
||||
* app-openpgp.c (parse_login_data): New.
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
bin_PROGRAMS = scdaemon sc-investigate sc-copykeys
|
||||
bin_PROGRAMS = scdaemon sc-copykeys
|
||||
pkglib_PROGRAMS = pcsc-wrapper
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/intl -I$(top_srcdir)/common
|
||||
@ -56,22 +56,6 @@ scdaemon_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \
|
||||
$(LIBUSB_LIBS) $(OPENSC_LIBS) -lgpg-error @LIBINTL@ \
|
||||
@DL_LIBS@
|
||||
|
||||
sc_investigate_SOURCES = \
|
||||
sc-investigate.c scdaemon.h \
|
||||
apdu.c apdu.h \
|
||||
ccid-driver.c ccid-driver.h \
|
||||
iso7816.c iso7816.h \
|
||||
tlv.c tlv.h \
|
||||
atr.c atr.h \
|
||||
app.c app-common.h app-help.c $(card_apps)
|
||||
|
||||
sc_investigate_LDADD = \
|
||||
../jnlib/libjnlib.a ../common/libcommon.a \
|
||||
$(LIBGCRYPT_LIBS) $(pth_libs) \
|
||||
$(KSBA_LIBS) $(LIBUSB_LIBS) $(OPENSC_LIBS) \
|
||||
@LIBINTL@ -lgpg-error @DL_LIBS@
|
||||
|
||||
|
||||
sc_copykeys_SOURCES = \
|
||||
sc-copykeys.c scdaemon.h \
|
||||
apdu.c apdu.h \
|
||||
|
512
scd/apdu.c
512
scd/apdu.c
@ -125,6 +125,8 @@ struct reader_table_s {
|
||||
rapdu_t handle;
|
||||
} rapdu;
|
||||
#endif /*USE_G10CODE_RAPDU*/
|
||||
char *rdrname; /* Name of the connected reader or NULL if unknown. */
|
||||
int last_status;
|
||||
int status;
|
||||
unsigned char atr[33];
|
||||
size_t atrlen; /* A zero length indicates that the ATR has
|
||||
@ -169,13 +171,47 @@ static char (* DLSTDCALL CT_close) (unsigned short ctn);
|
||||
#define PCSC_UNPOWER_CARD 2
|
||||
#define PCSC_EJECT_CARD 3
|
||||
|
||||
struct pcsc_io_request_s {
|
||||
#define PCSC_UNKNOWN 0x0001
|
||||
#define PCSC_ABSENT 0x0002 /* Card is absent. */
|
||||
#define PCSC_PRESENT 0x0004 /* Card is present. */
|
||||
#define PCSC_SWALLOWED 0x0008 /* Card is present and electrical connected. */
|
||||
#define PCSC_POWERED 0x0010 /* Card is powered. */
|
||||
#define PCSC_NEGOTIABLE 0x0020 /* Card is awaiting PTS. */
|
||||
#define PCSC_SPECIFIC 0x0040 /* Card is ready for use. */
|
||||
|
||||
#define PCSC_STATE_UNAWARE 0x0000 /* Want status. */
|
||||
#define PCSC_STATE_IGNORE 0x0001 /* Ignore this reader. */
|
||||
#define PCSC_STATE_CHANGED 0x0002 /* State has changed. */
|
||||
#define PCSC_STATE_UNKNOWN 0x0004 /* Reader unknown. */
|
||||
#define PCSC_STATE_UNAVAILABLE 0x0008 /* Status unavailable. */
|
||||
#define PCSC_STATE_EMPTY 0x0010 /* Card removed. */
|
||||
#define PCSC_STATE_PRESENT 0x0020 /* Card inserted. */
|
||||
#define PCSC_STATE_ATRMATCH 0x0040 /* ATR matches card. */
|
||||
#define PCSC_STATE_EXCLUSIVE 0x0080 /* Exclusive Mode. */
|
||||
#define PCSC_STATE_INUSE 0x0100 /* Shared mode. */
|
||||
#define PCSC_STATE_MUTE 0x0200 /* Unresponsive card. */
|
||||
|
||||
|
||||
struct pcsc_io_request_s
|
||||
{
|
||||
unsigned long protocol;
|
||||
unsigned long pci_len;
|
||||
};
|
||||
|
||||
typedef struct pcsc_io_request_s *pcsc_io_request_t;
|
||||
|
||||
struct pcsc_readerstate_s
|
||||
{
|
||||
const char *reader;
|
||||
void *user_data;
|
||||
unsigned long current_state;
|
||||
unsigned long event_state;
|
||||
unsigned long atrlen;
|
||||
unsigned char atr[33];
|
||||
};
|
||||
|
||||
typedef struct pcsc_readerstate_s *pcsc_readerstate_t;
|
||||
|
||||
long (* DLSTDCALL pcsc_establish_context) (unsigned long scope,
|
||||
const void *reserved1,
|
||||
const void *reserved2,
|
||||
@ -184,12 +220,21 @@ long (* DLSTDCALL pcsc_release_context) (unsigned long context);
|
||||
long (* DLSTDCALL pcsc_list_readers) (unsigned long context,
|
||||
const char *groups,
|
||||
char *readers, unsigned long*readerslen);
|
||||
long (* DLSTDCALL pcsc_get_status_change) (unsigned long context,
|
||||
unsigned long timeout,
|
||||
pcsc_readerstate_t readerstates,
|
||||
unsigned long nreaderstates);
|
||||
long (* DLSTDCALL 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 (* DLSTDCALL pcsc_reconnect) (unsigned long card,
|
||||
unsigned long share_mode,
|
||||
unsigned long preferred_protocols,
|
||||
unsigned long initialization,
|
||||
unsigned long *r_active_protocol);
|
||||
long (* DLSTDCALL pcsc_disconnect) (unsigned long card,
|
||||
unsigned long disposition);
|
||||
long (* DLSTDCALL pcsc_status) (unsigned long card,
|
||||
@ -211,7 +256,6 @@ long (* DLSTDCALL pcsc_set_timeout) (unsigned long context,
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Helper
|
||||
@ -254,11 +298,13 @@ new_reader_slot (void)
|
||||
reader_table[reader].dump_status_reader = NULL;
|
||||
|
||||
reader_table[reader].used = 1;
|
||||
reader_table[reader].last_status = 0;
|
||||
#ifdef NEED_PCSC_WRAPPER
|
||||
reader_table[reader].pcsc.req_fd = -1;
|
||||
reader_table[reader].pcsc.rsp_fd = -1;
|
||||
reader_table[reader].pcsc.pid = (pid_t)(-1);
|
||||
#endif
|
||||
|
||||
return reader;
|
||||
}
|
||||
|
||||
@ -662,18 +708,293 @@ dump_pcsc_reader_status (int slot)
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
reset_pcsc_reader (int slot)
|
||||
{
|
||||
#ifdef NEED_PCSC_WRAPPER
|
||||
long err;
|
||||
reader_table_t slotp;
|
||||
size_t len;
|
||||
int i, n;
|
||||
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 ("pcsc_get_status: pcsc-wrapper not running\n");
|
||||
return SW_HOST_CARD_IO_ERROR;
|
||||
}
|
||||
|
||||
msgbuf[0] = 0x05; /* RESET 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 RESET 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 RESET 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 RESET failed: %s\n", pcsc_error_string (err));
|
||||
goto command_failed;
|
||||
}
|
||||
|
||||
/* The open fucntion may return a zero for the ATR length to
|
||||
indicate that no card is present. */
|
||||
n = len;
|
||||
if (n)
|
||||
{
|
||||
if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
|
||||
{
|
||||
log_error ("error receiving PC/SC RESET response: %s\n",
|
||||
i? strerror (errno) : "premature EOF");
|
||||
goto command_failed;
|
||||
}
|
||||
}
|
||||
slotp->atrlen = len;
|
||||
|
||||
return 0;
|
||||
|
||||
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;
|
||||
char reader[250];
|
||||
unsigned long nreader, atrlen;
|
||||
unsigned long card_state, card_protocol;
|
||||
|
||||
if (reader_table[slot].pcsc.card)
|
||||
{
|
||||
err = pcsc_disconnect (reader_table[slot].pcsc.card, PCSC_LEAVE_CARD);
|
||||
if (err)
|
||||
{
|
||||
log_error ("pcsc_disconnect failed: %s (0x%lx)\n",
|
||||
pcsc_error_string (err), err);
|
||||
return SW_HOST_CARD_IO_ERROR;
|
||||
}
|
||||
reader_table[slot].pcsc.card = 0;
|
||||
}
|
||||
|
||||
err = pcsc_connect (reader_table[slot].pcsc.context,
|
||||
reader_table[slot].rdrname,
|
||||
PCSC_SHARE_EXCLUSIVE,
|
||||
PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
|
||||
&reader_table[slot].pcsc.card,
|
||||
&reader_table[slot].pcsc.protocol);
|
||||
if (err)
|
||||
{
|
||||
log_error ("pcsc_connect failed: %s (0x%lx)\n",
|
||||
pcsc_error_string (err), err);
|
||||
reader_table[slot].pcsc.card = 0;
|
||||
return SW_HOST_CARD_IO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
atrlen = 33;
|
||||
nreader = sizeof reader - 1;
|
||||
err = pcsc_status (reader_table[slot].pcsc.card,
|
||||
reader, &nreader,
|
||||
&card_state, &card_protocol,
|
||||
reader_table[slot].atr, &atrlen);
|
||||
if (err)
|
||||
{
|
||||
log_error ("pcsc_status failed: %s (0x%lx)\n",
|
||||
pcsc_error_string (err), err);
|
||||
reader_table[slot].atrlen = 0;
|
||||
return SW_HOST_CARD_IO_ERROR;
|
||||
}
|
||||
if (atrlen >= DIM (reader_table[0].atr))
|
||||
log_bug ("ATR returned by pcsc_status is too large\n");
|
||||
reader_table[slot].atrlen = atrlen;
|
||||
|
||||
return 0;
|
||||
#endif /* !NEED_PCSC_WRAPPER */
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
pcsc_get_status (int slot, unsigned int *status)
|
||||
{
|
||||
*status = 1|2|4; /* FIXME!!!! */
|
||||
return 0;
|
||||
}
|
||||
#ifdef NEED_PCSC_WRAPPER
|
||||
long err;
|
||||
reader_table_t slotp;
|
||||
size_t len, full_len;
|
||||
int i, n;
|
||||
unsigned char msgbuf[9];
|
||||
unsigned char buffer[12];
|
||||
|
||||
static int
|
||||
reset_pcsc_reader (int slot)
|
||||
{
|
||||
return SW_HOST_NOT_SUPPORTED;
|
||||
slotp = reader_table + slot;
|
||||
|
||||
if (slotp->pcsc.req_fd == -1
|
||||
|| slotp->pcsc.rsp_fd == -1
|
||||
|| slotp->pcsc.pid == (pid_t)(-1) )
|
||||
{
|
||||
log_error ("pcsc_get_status: pcsc-wrapper not running\n");
|
||||
return SW_HOST_CARD_IO_ERROR;
|
||||
}
|
||||
|
||||
msgbuf[0] = 0x04; /* STATUS 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 STATUS 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 STATUS 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_status failed: %s (0x%lx)\n",
|
||||
pcsc_error_string (err), err);
|
||||
return SW_HOST_CARD_IO_ERROR;
|
||||
}
|
||||
|
||||
full_len = len;
|
||||
|
||||
n = 8 < len ? 8 : len;
|
||||
if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != 8)
|
||||
{
|
||||
log_error ("error receiving PC/SC STATUS response: %s\n",
|
||||
i? strerror (errno) : "premature EOF");
|
||||
goto command_failed;
|
||||
}
|
||||
|
||||
full_len -= len;
|
||||
/* Newer versions of the wrapper might send more status bytes.
|
||||
Read them. */
|
||||
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;
|
||||
}
|
||||
|
||||
/* We are lucky: The wrapper already returns the data in the
|
||||
required format. */
|
||||
*status = buffer[3];
|
||||
|
||||
return 0;
|
||||
|
||||
command_failed:
|
||||
close (slotp->pcsc.req_fd);
|
||||
close (slotp->pcsc.rsp_fd);
|
||||
slotp->pcsc.req_fd = -1;
|
||||
slotp->pcsc.rsp_fd = -1;
|
||||
kill (slotp->pcsc.pid, SIGTERM);
|
||||
slotp->pcsc.pid = (pid_t)(-1);
|
||||
slotp->used = 0;
|
||||
return -1;
|
||||
|
||||
#else /*!NEED_PCSC_WRAPPER*/
|
||||
|
||||
long err;
|
||||
struct pcsc_readerstate_s rdrstates[1];
|
||||
|
||||
memset (rdrstates, 0, sizeof *rdrstates);
|
||||
rdrstates[0].reader = reader_table[slot].rdrname;
|
||||
rdrstates[0].current_state = PCSC_STATE_UNAWARE;
|
||||
err = pcsc_get_status_change (reader_table[slot].pcsc.context,
|
||||
0,
|
||||
rdrstates, 1);
|
||||
if (err == 0x8010000a) /* Timeout. */
|
||||
err = 0;
|
||||
if (err)
|
||||
{
|
||||
log_error ("pcsc_get_status_change failed: %s (0x%lx)\n",
|
||||
pcsc_error_string (err), err);
|
||||
return SW_HOST_CARD_IO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/* log_debug */
|
||||
/* ("pcsc_get_status_change: %s%s%s%s%s%s%s%s%s%s\n", */
|
||||
/* (rdrstates[0].event_state & PCSC_STATE_IGNORE)? " ignore":"", */
|
||||
/* (rdrstates[0].event_state & PCSC_STATE_CHANGED)? " changed":"", */
|
||||
/* (rdrstates[0].event_state & PCSC_STATE_UNKNOWN)? " unknown":"", */
|
||||
/* (rdrstates[0].event_state & PCSC_STATE_UNAVAILABLE)?" unavail":"", */
|
||||
/* (rdrstates[0].event_state & PCSC_STATE_EMPTY)? " empty":"", */
|
||||
/* (rdrstates[0].event_state & PCSC_STATE_PRESENT)? " present":"", */
|
||||
/* (rdrstates[0].event_state & PCSC_STATE_ATRMATCH)? " atr":"", */
|
||||
/* (rdrstates[0].event_state & PCSC_STATE_EXCLUSIVE)? " excl":"", */
|
||||
/* (rdrstates[0].event_state & PCSC_STATE_INUSE)? " unuse":"", */
|
||||
/* (rdrstates[0].event_state & PCSC_STATE_MUTE)? " mute":"" ); */
|
||||
|
||||
*status = 0;
|
||||
if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) )
|
||||
*status |= 2;
|
||||
if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
|
||||
*status |= 4;
|
||||
/* We indicate a useful card if it is not in use by another
|
||||
application. This is because we only use exclusive access
|
||||
mode. */
|
||||
if ( (*status & 6) == 6
|
||||
&& !(rdrstates[0].event_state & PCSC_STATE_INUSE) )
|
||||
*status |= 1;
|
||||
|
||||
return 0;
|
||||
#endif /*!NEED_PCSC_WRAPPER*/
|
||||
}
|
||||
|
||||
|
||||
@ -889,6 +1210,8 @@ close_pcsc_reader (int slot)
|
||||
#else /*!NEED_PCSC_WRAPPER*/
|
||||
|
||||
pcsc_release_context (reader_table[slot].pcsc.context);
|
||||
xfree (reader_table[slot].rdrname);
|
||||
reader_table[slot].rdrname = NULL;
|
||||
reader_table[slot].used = 0;
|
||||
return 0;
|
||||
#endif /*!NEED_PCSC_WRAPPER*/
|
||||
@ -999,8 +1322,14 @@ open_pcsc_reader (const char *portstr)
|
||||
slotp->pcsc.rsp_fd = rp[0];
|
||||
|
||||
/* Wait for the intermediate child to terminate. */
|
||||
while ( (i=pth_waitpid (pid, NULL, 0)) == -1 && errno == EINTR)
|
||||
#ifdef USE_GNU_PTH
|
||||
#define WAIT pth_waitpid
|
||||
#else
|
||||
#define WAIT waitpid
|
||||
#endif
|
||||
while ( (i=WAIT (pid, NULL, 0)) == -1 && errno == EINTR)
|
||||
;
|
||||
#undef X
|
||||
|
||||
/* Now send the open request. */
|
||||
msgbuf[0] = 0x01; /* OPEN command. */
|
||||
@ -1041,12 +1370,23 @@ open_pcsc_reader (const char *portstr)
|
||||
log_error ("PC/SC OPEN failed: %s\n", pcsc_error_string (err));
|
||||
goto command_failed;
|
||||
}
|
||||
|
||||
slotp->last_status = 0;
|
||||
|
||||
/* The open fucntion may return a zero for the ATR length to
|
||||
indicate that no card is present. */
|
||||
n = len;
|
||||
if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
|
||||
if (n)
|
||||
{
|
||||
log_error ("error receiving PC/SC OPEN response: %s\n",
|
||||
i? strerror (errno) : "premature EOF");
|
||||
goto command_failed;
|
||||
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;
|
||||
}
|
||||
/* If we got to here we know that a card is present
|
||||
and usable. Thus remember this. */
|
||||
slotp->last_status = (1|2|4| 0x8000);
|
||||
}
|
||||
slotp->atrlen = len;
|
||||
|
||||
@ -1132,41 +1472,63 @@ open_pcsc_reader (const char *portstr)
|
||||
p += strlen (p) + 1;
|
||||
}
|
||||
|
||||
err = pcsc_connect (reader_table[slot].pcsc.context,
|
||||
portstr? portstr : list,
|
||||
PCSC_SHARE_EXCLUSIVE,
|
||||
PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
|
||||
&reader_table[slot].pcsc.card,
|
||||
&reader_table[slot].pcsc.protocol);
|
||||
if (err)
|
||||
reader_table[slot].rdrname = xtrymalloc (strlen (portstr? portstr : list)+1);
|
||||
if (!reader_table[slot].rdrname)
|
||||
{
|
||||
log_error ("pcsc_connect failed: %s (0x%lx)\n",
|
||||
pcsc_error_string (err), err);
|
||||
pcsc_release_context (reader_table[slot].pcsc.context);
|
||||
reader_table[slot].used = 0;
|
||||
xfree (list);
|
||||
return -1;
|
||||
}
|
||||
|
||||
atrlen = 32;
|
||||
/* (We need to pass a dummy buffer. We use LIST because it ought to
|
||||
be large enough.) */
|
||||
err = pcsc_status (reader_table[slot].pcsc.card,
|
||||
list, &listlen,
|
||||
&card_state, &card_protocol,
|
||||
reader_table[slot].atr, &atrlen);
|
||||
xfree (list);
|
||||
if (err)
|
||||
{
|
||||
log_error ("pcsc_status failed: %s (0x%lx)\n",
|
||||
pcsc_error_string (err), err);
|
||||
log_error ("error allocating memory for reader name\n");
|
||||
pcsc_release_context (reader_table[slot].pcsc.context);
|
||||
reader_table[slot].used = 0;
|
||||
return -1;
|
||||
}
|
||||
if (atrlen >= DIM (reader_table[0].atr))
|
||||
log_bug ("ATR returned by pcsc_status is too large\n");
|
||||
reader_table[slot].atrlen = atrlen;
|
||||
strcpy (reader_table[slot].rdrname, portstr? portstr : list);
|
||||
xfree (list);
|
||||
|
||||
err = pcsc_connect (reader_table[slot].pcsc.context,
|
||||
reader_table[slot].rdrname,
|
||||
PCSC_SHARE_EXCLUSIVE,
|
||||
PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
|
||||
&reader_table[slot].pcsc.card,
|
||||
&reader_table[slot].pcsc.protocol);
|
||||
if (err == 0x8010000c) /* No smartcard. */
|
||||
reader_table[slot].pcsc.card = 0;
|
||||
else if (err)
|
||||
{
|
||||
log_error ("pcsc_connect failed: %s (0x%lx)\n",
|
||||
pcsc_error_string (err), err);
|
||||
pcsc_release_context (reader_table[slot].pcsc.context);
|
||||
xfree (reader_table[slot].rdrname);
|
||||
reader_table[slot].rdrname = NULL;
|
||||
reader_table[slot].used = 0;
|
||||
xfree (list);
|
||||
return -1;
|
||||
}
|
||||
|
||||
reader_table[slot].atrlen = 0;
|
||||
reader_table[slot].last_status = 0;
|
||||
if (!err)
|
||||
{
|
||||
char reader[250];
|
||||
unsigned long readerlen;
|
||||
|
||||
atrlen = 32;
|
||||
readerlen = sizeof reader -1 ;
|
||||
err = pcsc_status (reader_table[slot].pcsc.card,
|
||||
reader, &readerlen,
|
||||
&card_state, &card_protocol,
|
||||
reader_table[slot].atr, &atrlen);
|
||||
if (err)
|
||||
log_error ("pcsc_status failed: %s (0x%lx) %lu\n",
|
||||
pcsc_error_string (err), err, readerlen);
|
||||
else
|
||||
{
|
||||
if (atrlen >= DIM (reader_table[0].atr))
|
||||
log_bug ("ATR returned by pcsc_status is too large\n");
|
||||
reader_table[slot].atrlen = atrlen;
|
||||
/* If we got to here we know that a card is present
|
||||
and usable. Thus remember this. */
|
||||
reader_table[slot].last_status = (1|2|4| 0x8000);
|
||||
}
|
||||
}
|
||||
|
||||
reader_table[slot].close_reader = close_pcsc_reader;
|
||||
reader_table[slot].reset_reader = reset_pcsc_reader;
|
||||
@ -1311,6 +1673,12 @@ open_ccid_reader (const char *portstr)
|
||||
slotp->atrlen = 0;
|
||||
err = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If we got to here we know that a card is present
|
||||
and usable. Thus remember this. */
|
||||
reader_table[slot].last_status = (1|2|4| 0x8000);
|
||||
}
|
||||
|
||||
reader_table[slot].close_reader = close_ccid_reader;
|
||||
reader_table[slot].shutdown_reader = shutdown_ccid_reader;
|
||||
@ -1970,11 +2338,21 @@ apdu_open_reader (const char *portstr)
|
||||
#ifdef _WIN32
|
||||
if (!pcsc_list_readers)
|
||||
pcsc_list_readers = dlsym (handle, "SCardListReadersA");
|
||||
#endif
|
||||
pcsc_get_status_change = dlsym (handle, "SCardGetStatusChange");
|
||||
#ifdef _WIN32
|
||||
if (!pcsc_get_status_change)
|
||||
pcsc_get_status_change = dlsym (handle, "SCardGetStatusChangeA");
|
||||
#endif
|
||||
pcsc_connect = dlsym (handle, "SCardConnect");
|
||||
#ifdef _WIN32
|
||||
if (!pcsc_connect)
|
||||
pcsc_connect = dlsym (handle, "SCardConnectA");
|
||||
#endif
|
||||
pcsc_reconnect = dlsym (handle, "SCardReconnect");
|
||||
#ifdef _WIN32
|
||||
if (!pcsc_reconnect)
|
||||
pcsc_reconnect = dlsym (handle, "SCardReconnectA");
|
||||
#endif
|
||||
pcsc_disconnect = dlsym (handle, "SCardDisconnect");
|
||||
pcsc_status = dlsym (handle, "SCardStatus");
|
||||
@ -1990,7 +2368,9 @@ apdu_open_reader (const char *portstr)
|
||||
if (!pcsc_establish_context
|
||||
|| !pcsc_release_context
|
||||
|| !pcsc_list_readers
|
||||
|| !pcsc_get_status_change
|
||||
|| !pcsc_connect
|
||||
|| !pcsc_reconnect
|
||||
|| !pcsc_disconnect
|
||||
|| !pcsc_status
|
||||
|| !pcsc_begin_transaction
|
||||
@ -2001,11 +2381,13 @@ apdu_open_reader (const char *portstr)
|
||||
/* Note that set_timeout is currently not used and also not
|
||||
available under Windows. */
|
||||
log_error ("apdu_open_reader: invalid PC/SC driver "
|
||||
"(%d%d%d%d%d%d%d%d%d%d)\n",
|
||||
"(%d%d%d%d%d%d%d%d%d%d%d%d)\n",
|
||||
!!pcsc_establish_context,
|
||||
!!pcsc_release_context,
|
||||
!!pcsc_list_readers,
|
||||
!!pcsc_get_status_change,
|
||||
!!pcsc_connect,
|
||||
!!pcsc_reconnect,
|
||||
!!pcsc_disconnect,
|
||||
!!pcsc_status,
|
||||
!!pcsc_begin_transaction,
|
||||
@ -2048,8 +2430,12 @@ apdu_open_remote_reader (const char *portstr,
|
||||
readfnc, readfnc_value,
|
||||
writefnc, writefnc_value,
|
||||
closefnc, closefnc_value);
|
||||
#else
|
||||
#ifdef _WIN32
|
||||
errno = ENOENT;
|
||||
#else
|
||||
errno = ENOSYS;
|
||||
#endif
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
@ -2102,9 +2488,17 @@ apdu_reset (int slot)
|
||||
if ((sw = lock_slot (slot)))
|
||||
return sw;
|
||||
|
||||
reader_table[slot].last_status = 0;
|
||||
if (reader_table[slot].reset_reader)
|
||||
sw = reader_table[slot].reset_reader (slot);
|
||||
|
||||
if (!sw)
|
||||
{
|
||||
/* If we got to here we know that a card is present
|
||||
and usable. Thus remember this. */
|
||||
reader_table[slot].last_status = (1|2|4| 0x8000);
|
||||
}
|
||||
|
||||
unlock_slot (slot);
|
||||
return sw;
|
||||
}
|
||||
@ -2139,7 +2533,16 @@ apdu_activate (int slot)
|
||||
/* We don't have an ATR or a card is present though inactive:
|
||||
do a reset now. */
|
||||
if (reader_table[slot].reset_reader)
|
||||
sw = reader_table[slot].reset_reader (slot);
|
||||
{
|
||||
reader_table[slot].last_status = 0;
|
||||
sw = reader_table[slot].reset_reader (slot);
|
||||
if (!sw)
|
||||
{
|
||||
/* If we got to here we know that a card is present
|
||||
and usable. Thus remember this. */
|
||||
reader_table[slot].last_status = (1|2|4| 0x8000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2201,7 +2604,22 @@ apdu_get_status (int slot, int hang,
|
||||
unlock_slot (slot);
|
||||
|
||||
if (sw)
|
||||
return sw;
|
||||
{
|
||||
reader_table[slot].last_status = 0;
|
||||
return sw;
|
||||
}
|
||||
|
||||
/* Keep track of changes. We use one extra bit to test whether we
|
||||
have checked the status at least once. */
|
||||
if ( s != (reader_table[slot].last_status & 0x07ff)
|
||||
|| !reader_table[slot].last_status )
|
||||
{
|
||||
reader_table[slot].change_counter++;
|
||||
/* Make sure that the ATR is invalid so that a reset will be by
|
||||
activate. */
|
||||
reader_table[slot].atrlen = 0;
|
||||
}
|
||||
reader_table[slot].last_status = (s | 0x8000);
|
||||
|
||||
if (status)
|
||||
*status = s;
|
||||
|
@ -1307,6 +1307,8 @@ ccid_get_atr (ccid_driver_t handle,
|
||||
/* Note that we ignore the error code on purpose. */
|
||||
bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters, seqno);
|
||||
|
||||
handle->t1_ns = 0;
|
||||
handle->t1_nr = 0;
|
||||
|
||||
/* Send an S-Block with our maximun IFSD to the CCID. */
|
||||
if (!handle->auto_ifsd)
|
||||
|
@ -126,15 +126,8 @@ iso7816_map_sw (int sw)
|
||||
gpg_error_t
|
||||
iso7816_select_application (int slot, const char *aid, size_t aidlen)
|
||||
{
|
||||
static char const openpgp_aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 };
|
||||
int sw;
|
||||
int p1 = 0x0C; /* No FCI to be returned. */
|
||||
|
||||
if (aidlen == sizeof openpgp_aid
|
||||
&& !memcmp (aid, openpgp_aid, sizeof openpgp_aid))
|
||||
p1 = 0; /* The current openpgp cards don't allow 0x0c. */
|
||||
|
||||
sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, p1, aidlen, aid);
|
||||
sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, 0, aidlen, aid);
|
||||
return map_sw (sw);
|
||||
}
|
||||
|
||||
|
@ -86,6 +86,26 @@ static int verbose;
|
||||
#define PCSC_UNPOWER_CARD 2
|
||||
#define PCSC_EJECT_CARD 3
|
||||
|
||||
#define PCSC_UNKNOWN 0x0001
|
||||
#define PCSC_ABSENT 0x0002 /* Card is absent. */
|
||||
#define PCSC_PRESENT 0x0004 /* Card is present. */
|
||||
#define PCSC_SWALLOWED 0x0008 /* Card is present and electrical connected. */
|
||||
#define PCSC_POWERED 0x0010 /* Card is powered. */
|
||||
#define PCSC_NEGOTIABLE 0x0020 /* Card is awaiting PTS. */
|
||||
#define PCSC_SPECIFIC 0x0040 /* Card is ready for use. */
|
||||
|
||||
#define PCSC_STATE_UNAWARE 0x0000 /* Want status. */
|
||||
#define PCSC_STATE_IGNORE 0x0001 /* Ignore this reader. */
|
||||
#define PCSC_STATE_CHANGED 0x0002 /* State has changed. */
|
||||
#define PCSC_STATE_UNKNOWN 0x0004 /* Reader unknown. */
|
||||
#define PCSC_STATE_UNAVAILABLE 0x0008 /* Status unavailable. */
|
||||
#define PCSC_STATE_EMPTY 0x0010 /* Card removed. */
|
||||
#define PCSC_STATE_PRESENT 0x0020 /* Card inserted. */
|
||||
#define PCSC_STATE_ATRMATCH 0x0040 /* ATR matches card. */
|
||||
#define PCSC_STATE_EXCLUSIVE 0x0080 /* Exclusive Mode. */
|
||||
#define PCSC_STATE_INUSE 0x0100 /* Shared mode. */
|
||||
#define PCSC_STATE_MUTE 0x0200 /* Unresponsive card. */
|
||||
|
||||
struct pcsc_io_request_s {
|
||||
unsigned long protocol;
|
||||
unsigned long pci_len;
|
||||
@ -93,12 +113,25 @@ struct pcsc_io_request_s {
|
||||
|
||||
typedef struct pcsc_io_request_s *pcsc_io_request_t;
|
||||
|
||||
struct pcsc_readerstate_s
|
||||
{
|
||||
const char *reader;
|
||||
void *user_data;
|
||||
unsigned long current_state;
|
||||
unsigned long event_state;
|
||||
unsigned long atrlen;
|
||||
unsigned char atr[33];
|
||||
};
|
||||
|
||||
typedef struct pcsc_readerstate_s *pcsc_readerstate_t;
|
||||
|
||||
|
||||
static int driver_is_open; /* True if the PC/SC driver has been
|
||||
initialzied and is ready for
|
||||
operations. The follwoing variables
|
||||
operations. The following variables
|
||||
are then valid. */
|
||||
static unsigned long pcsc_context; /* The current PC/CS context. */
|
||||
static char *current_rdrname;
|
||||
static unsigned long pcsc_card;
|
||||
static unsigned long pcsc_protocol;
|
||||
static unsigned char current_atr[33];
|
||||
@ -112,12 +145,21 @@ long (* pcsc_release_context) (unsigned long context);
|
||||
long (* pcsc_list_readers) (unsigned long context,
|
||||
const char *groups,
|
||||
char *readers, unsigned long*readerslen);
|
||||
long (* pcsc_get_status_change) (unsigned long context,
|
||||
unsigned long timeout,
|
||||
pcsc_readerstate_t readerstates,
|
||||
unsigned long nreaderstates);
|
||||
long (* pcsc_connect) (unsigned long context,
|
||||
const char *reader,
|
||||
unsigned long share_mode,
|
||||
unsigned long preferred_protocols,
|
||||
unsigned long *r_card,
|
||||
unsigned long *r_active_protocol);
|
||||
long (* pcsc_reconnect) (unsigned long card,
|
||||
unsigned long share_mode,
|
||||
unsigned long preferred_protocols,
|
||||
unsigned long initialization,
|
||||
unsigned long *r_active_protocol);
|
||||
long (* pcsc_disconnect) (unsigned long card,
|
||||
unsigned long disposition);
|
||||
long (* pcsc_status) (unsigned long card,
|
||||
@ -284,7 +326,9 @@ load_pcsc_driver (const char *libname)
|
||||
pcsc_establish_context = dlsym (handle, "SCardEstablishContext");
|
||||
pcsc_release_context = dlsym (handle, "SCardReleaseContext");
|
||||
pcsc_list_readers = dlsym (handle, "SCardListReaders");
|
||||
pcsc_get_status_change = dlsym (handle, "SCardGetStatusChange");
|
||||
pcsc_connect = dlsym (handle, "SCardConnect");
|
||||
pcsc_reconnect = dlsym (handle, "SCardReconnect");
|
||||
pcsc_disconnect = dlsym (handle, "SCardDisconnect");
|
||||
pcsc_status = dlsym (handle, "SCardStatus");
|
||||
pcsc_begin_transaction = dlsym (handle, "SCardBeginTransaction");
|
||||
@ -295,7 +339,9 @@ load_pcsc_driver (const char *libname)
|
||||
if (!pcsc_establish_context
|
||||
|| !pcsc_release_context
|
||||
|| !pcsc_list_readers
|
||||
|| !pcsc_get_status_change
|
||||
|| !pcsc_connect
|
||||
|| !pcsc_reconnect
|
||||
|| !pcsc_disconnect
|
||||
|| !pcsc_status
|
||||
|| !pcsc_begin_transaction
|
||||
@ -307,11 +353,13 @@ load_pcsc_driver (const char *libname)
|
||||
available under Windows. */
|
||||
fprintf (stderr,
|
||||
"apdu_open_reader: invalid PC/SC driver "
|
||||
"(%d%d%d%d%d%d%d%d%d%d)\n",
|
||||
"(%d%d%d%d%d%d%d%d%d%d%d%d)\n",
|
||||
!!pcsc_establish_context,
|
||||
!!pcsc_release_context,
|
||||
!!pcsc_list_readers,
|
||||
!!pcsc_get_status_change,
|
||||
!!pcsc_connect,
|
||||
!!pcsc_reconnect,
|
||||
!!pcsc_disconnect,
|
||||
!!pcsc_status,
|
||||
!!pcsc_begin_transaction,
|
||||
@ -327,8 +375,8 @@ load_pcsc_driver (const char *libname)
|
||||
|
||||
|
||||
/* Handle a open request. The argument is expected to be a string
|
||||
with the port indentification. ARGBUF is always guaranteed to be
|
||||
terminted by a 0 which is not counted in ARGLEN. We may modifiy
|
||||
with the port identification. ARGBUF is always guaranteed to be
|
||||
terminted by a 0 which is not counted in ARGLEN. We may modifiy
|
||||
ARGBUF. */
|
||||
static void
|
||||
handle_open (unsigned char *argbuf, size_t arglen)
|
||||
@ -350,6 +398,7 @@ handle_open (unsigned char *argbuf, size_t arglen)
|
||||
{
|
||||
fprintf (stderr, PGM ": PC/SC has already been opened\n");
|
||||
request_failed (-1);
|
||||
return;
|
||||
}
|
||||
|
||||
err = pcsc_establish_context (PCSC_SCOPE_SYSTEM, NULL, NULL, &pcsc_context);
|
||||
@ -398,45 +447,64 @@ handle_open (unsigned char *argbuf, size_t arglen)
|
||||
p += strlen (p) + 1;
|
||||
}
|
||||
|
||||
current_rdrname = malloc (strlen (portstr && *portstr? portstr:list)+1);
|
||||
if (!current_rdrname)
|
||||
{
|
||||
fprintf (stderr, PGM": error allocating memory for reader name\n");
|
||||
exit (1);
|
||||
}
|
||||
strcpy (current_rdrname, portstr && *portstr? portstr:list);
|
||||
free (list);
|
||||
|
||||
err = pcsc_connect (pcsc_context,
|
||||
portstr && *portstr? portstr : list,
|
||||
current_rdrname,
|
||||
PCSC_SHARE_EXCLUSIVE,
|
||||
PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
|
||||
&pcsc_card,
|
||||
&pcsc_protocol);
|
||||
if (err)
|
||||
if (err == 0x8010000c) /* No smartcard. */
|
||||
{
|
||||
pcsc_card = 0;
|
||||
}
|
||||
else if (err)
|
||||
{
|
||||
fprintf (stderr, PGM": pcsc_connect failed: %s (0x%lx)\n",
|
||||
pcsc_error_string (err), err);
|
||||
pcsc_release_context (pcsc_context);
|
||||
free (list);
|
||||
free (current_rdrname);
|
||||
current_rdrname = NULL;
|
||||
request_failed (err);
|
||||
return;
|
||||
}
|
||||
|
||||
atrlen = 32;
|
||||
/* (We need to pass a dummy buffer. We use LIST because it ought to
|
||||
be large enough.) */
|
||||
err = pcsc_status (pcsc_card,
|
||||
list, &listlen,
|
||||
&card_state, &card_protocol,
|
||||
atr, &atrlen);
|
||||
free (list);
|
||||
if (err)
|
||||
current_atrlen = 0;
|
||||
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;
|
||||
char reader[250];
|
||||
unsigned long readerlen;
|
||||
|
||||
atrlen = 33;
|
||||
readerlen = sizeof reader -1;
|
||||
err = pcsc_status (pcsc_card,
|
||||
reader, &readerlen,
|
||||
&card_state, &card_protocol,
|
||||
atr, &atrlen);
|
||||
if (err)
|
||||
fprintf (stderr, PGM": pcsc_status failed: %s (0x%lx)\n",
|
||||
pcsc_error_string (err), err);
|
||||
else
|
||||
{
|
||||
if (atrlen >= sizeof atr || atrlen >= sizeof current_atr)
|
||||
{
|
||||
fprintf (stderr, PGM": ATR returned by pcsc_status"
|
||||
" is too large\n");
|
||||
exit (4);
|
||||
}
|
||||
memcpy (current_atr, atr, atrlen);
|
||||
current_atrlen = atrlen;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
@ -452,8 +520,11 @@ handle_close (unsigned char *argbuf, size_t arglen)
|
||||
{
|
||||
fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
|
||||
request_failed (-1);
|
||||
return;
|
||||
}
|
||||
|
||||
free (current_rdrname);
|
||||
current_rdrname = NULL;
|
||||
pcsc_release_context (pcsc_context);
|
||||
|
||||
request_succeeded (NULL, 0);
|
||||
@ -461,7 +532,133 @@ handle_close (unsigned char *argbuf, size_t arglen)
|
||||
|
||||
|
||||
|
||||
/* Handle a transmit request. The argument is expected to be a bufer
|
||||
/* Handle a status request. We expect no arguments. We may modifiy
|
||||
ARGBUF. */
|
||||
static void
|
||||
handle_status (unsigned char *argbuf, size_t arglen)
|
||||
{
|
||||
long err;
|
||||
struct pcsc_readerstate_s rdrstates[1];
|
||||
int status;
|
||||
unsigned char buf[20];
|
||||
|
||||
if (!driver_is_open)
|
||||
{
|
||||
fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
|
||||
request_failed (-1);
|
||||
return;
|
||||
}
|
||||
|
||||
memset (rdrstates, 0, sizeof *rdrstates);
|
||||
rdrstates[0].reader = current_rdrname;
|
||||
rdrstates[0].current_state = PCSC_STATE_UNAWARE;
|
||||
err = pcsc_get_status_change (pcsc_context,
|
||||
0,
|
||||
rdrstates, 1);
|
||||
if (err == 0x8010000a) /* Timeout. */
|
||||
err = 0;
|
||||
if (err)
|
||||
{
|
||||
fprintf (stderr, PGM": pcsc_get_status_change failed: %s (0x%lx)\n",
|
||||
pcsc_error_string (err), err);
|
||||
request_failed (err);
|
||||
return;
|
||||
}
|
||||
|
||||
status = 0;
|
||||
if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) )
|
||||
status |= 2;
|
||||
if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
|
||||
status |= 4;
|
||||
/* We indicate a useful card if it is not in use by another
|
||||
application. This is because we only use exclusive access
|
||||
mode. */
|
||||
if ( (status & 6) == 6
|
||||
&& !(rdrstates[0].event_state & PCSC_STATE_INUSE) )
|
||||
status |= 1;
|
||||
|
||||
/* First word is identical to the one used by apdu.c. */
|
||||
buf[0] = 0;
|
||||
buf[1] = 0;
|
||||
buf[2] = 0;
|
||||
buf[3] = status;
|
||||
/* The second word is the native PCSC state. */
|
||||
buf[4] = (rdrstates[0].event_state >> 24);
|
||||
buf[5] = (rdrstates[0].event_state >> 16);
|
||||
buf[6] = (rdrstates[0].event_state >> 8);
|
||||
buf[7] = (rdrstates[0].event_state >> 0);
|
||||
|
||||
request_succeeded (buf, 8);
|
||||
}
|
||||
|
||||
|
||||
/* Handle a reset request. We expect no arguments. We may modifiy
|
||||
ARGBUF. */
|
||||
static void
|
||||
handle_reset (unsigned char *argbuf, size_t arglen)
|
||||
{
|
||||
long err;
|
||||
char reader[250];
|
||||
unsigned long nreader, atrlen;
|
||||
unsigned long card_state, card_protocol;
|
||||
|
||||
if (!driver_is_open)
|
||||
{
|
||||
fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
|
||||
request_failed (-1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pcsc_card)
|
||||
{
|
||||
err = pcsc_disconnect (pcsc_card, PCSC_LEAVE_CARD);
|
||||
if (err)
|
||||
{
|
||||
fprintf (stderr, PGM": pcsc_disconnect failed: %s (0x%lx)\n",
|
||||
pcsc_error_string (err), err);
|
||||
request_failed (err);
|
||||
return;
|
||||
}
|
||||
pcsc_card = 0;
|
||||
}
|
||||
|
||||
err = pcsc_connect (pcsc_context,
|
||||
current_rdrname,
|
||||
PCSC_SHARE_EXCLUSIVE,
|
||||
PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
|
||||
&pcsc_card,
|
||||
&pcsc_protocol);
|
||||
if (err)
|
||||
{
|
||||
fprintf (stderr, PGM": pcsc_connect failed: %s (0x%lx)\n",
|
||||
pcsc_error_string (err), err);
|
||||
pcsc_card = 0;
|
||||
request_failed (err);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
atrlen = 33;
|
||||
nreader = sizeof reader - 1;
|
||||
err = pcsc_status (pcsc_card,
|
||||
reader, &nreader,
|
||||
&card_state, &card_protocol,
|
||||
current_atr, &atrlen);
|
||||
if (err)
|
||||
{
|
||||
fprintf (stderr, PGM": pcsc_status failed: %s (0x%lx)\n",
|
||||
pcsc_error_string (err), err);
|
||||
current_atrlen = 0;
|
||||
request_failed (err);
|
||||
return;
|
||||
}
|
||||
|
||||
request_succeeded (current_atr, current_atrlen);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Handle a transmit request. The argument is expected to be a buffer
|
||||
with the APDU. We may modifiy ARGBUF. */
|
||||
static void
|
||||
handle_transmit (unsigned char *argbuf, size_t arglen)
|
||||
@ -479,8 +676,8 @@ handle_transmit (unsigned char *argbuf, size_t arglen)
|
||||
{
|
||||
fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
|
||||
request_failed (-1);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((pcsc_protocol & PCSC_PROTOCOL_T1))
|
||||
send_pci.protocol = PCSC_PROTOCOL_T1;
|
||||
else
|
||||
@ -502,15 +699,6 @@ handle_transmit (unsigned char *argbuf, size_t arglen)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static void
|
||||
print_version (int with_help)
|
||||
{
|
||||
@ -613,6 +801,14 @@ main (int argc, char **argv)
|
||||
handle_transmit (argbuffer, arglen);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
handle_status (argbuffer, arglen);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
handle_reset (argbuffer, arglen);
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf (stderr, PGM ": invalid request 0x%02X\n", c);
|
||||
exit (1);
|
||||
|
@ -1,812 +0,0 @@
|
||||
/* sc-investigate.c - A tool to look around on smartcards.
|
||||
* Copyright (C) 2003 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
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
#ifdef USE_GNU_PTH
|
||||
# include <pth.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_READLINE_READLINE_H
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
#endif
|
||||
|
||||
#define JNLIB_NEED_LOG_LOGV
|
||||
#include "scdaemon.h"
|
||||
#include <gcrypt.h>
|
||||
|
||||
#include "apdu.h" /* for open_reader */
|
||||
#include "atr.h"
|
||||
#include "app-common.h"
|
||||
#include "iso7816.h"
|
||||
|
||||
#define _(a) (a)
|
||||
|
||||
#define CONTROL_D ('D' - 'A' + 1)
|
||||
|
||||
|
||||
enum cmd_and_opt_values
|
||||
{
|
||||
oInteractive = 'i',
|
||||
oVerbose = 'v',
|
||||
oQuiet = 'q',
|
||||
oReaderPort = 500,
|
||||
octapiDriver,
|
||||
|
||||
oDebug,
|
||||
oDebugAll,
|
||||
|
||||
oDisableCCID,
|
||||
|
||||
|
||||
oGenRandom,
|
||||
|
||||
aTest };
|
||||
|
||||
|
||||
static ARGPARSE_OPTS opts[] = {
|
||||
|
||||
{ 301, NULL, 0, "@Options:\n " },
|
||||
|
||||
{ oInteractive, "interactive", 0, "start in interactive explorer mode"},
|
||||
{ oQuiet, "quiet", 0, "quiet" },
|
||||
{ oVerbose, "verbose", 0, "verbose" },
|
||||
{ oReaderPort, "reader-port", 2, "|N|connect to reader at port N"},
|
||||
{ octapiDriver, "ctapi-driver", 2, "|NAME|use NAME as ctAPI driver"},
|
||||
{ oDisableCCID, "disable-ccid", 0,
|
||||
#ifdef HAVE_LIBUSB
|
||||
"do not use the internal CCID driver"
|
||||
#else
|
||||
"@"
|
||||
#endif
|
||||
},
|
||||
{ oDebug, "debug" ,4|16, "set debugging flags"},
|
||||
{ oDebugAll, "debug-all" ,0, "enable full debugging"},
|
||||
{ oGenRandom, "gen-random", 4, "|N|generate N bytes of random"},
|
||||
{0}
|
||||
};
|
||||
|
||||
#ifndef HAVE_OPENSC
|
||||
#ifdef USE_GNU_PTH
|
||||
/* Pth wrapper function definitions. */
|
||||
GCRY_THREAD_OPTION_PTH_IMPL;
|
||||
#endif /*USE_GNU_PTH*/
|
||||
#endif /*!HAVE_OPENSC*/
|
||||
|
||||
static void interactive_shell (int slot);
|
||||
static void dump_other_cards (int slot);
|
||||
|
||||
static const char *
|
||||
my_strusage (int level)
|
||||
{
|
||||
const char *p;
|
||||
switch (level)
|
||||
{
|
||||
case 11: p = "sc-investigate (GnuPG)";
|
||||
break;
|
||||
case 13: p = VERSION; break;
|
||||
case 17: p = PRINTABLE_OS_NAME; break;
|
||||
case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
|
||||
break;
|
||||
case 1:
|
||||
case 40: p = _("Usage: sc-investigate [options] (-h for help)\n");
|
||||
break;
|
||||
case 41: p = _("Syntax: sc-investigate [options] [args]]\n"
|
||||
"Have a look at smartcards\n");
|
||||
break;
|
||||
|
||||
default: p = NULL;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Used by gcry for logging */
|
||||
static void
|
||||
my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
|
||||
{
|
||||
/* translate the log levels */
|
||||
switch (level)
|
||||
{
|
||||
case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break;
|
||||
case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break;
|
||||
case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break;
|
||||
case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break;
|
||||
case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break;
|
||||
case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break;
|
||||
case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break;
|
||||
default: level = JNLIB_LOG_ERROR; break;
|
||||
}
|
||||
log_logv (level, fmt, arg_ptr);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char **argv )
|
||||
{
|
||||
ARGPARSE_ARGS pargs;
|
||||
int slot, rc;
|
||||
const char *reader_port = NULL;
|
||||
unsigned long gen_random = 0;
|
||||
int interactive = 0;
|
||||
|
||||
set_strusage (my_strusage);
|
||||
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
|
||||
log_set_prefix ("sc-investigate", 1);
|
||||
|
||||
/* Try to auto set the character set. */
|
||||
set_native_charset (NULL);
|
||||
|
||||
/* Libgcrypt requires us to register the threading model first. We
|
||||
can't use pth at all if we are using OpenSC becuase OpenSC uses
|
||||
ptreads. Note that this will also do the pth_init. */
|
||||
#ifndef HAVE_OPENSC
|
||||
#ifdef USE_GNU_PTH
|
||||
rc = gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth);
|
||||
if (rc)
|
||||
{
|
||||
log_fatal ("can't register GNU Pth with Libgcrypt: %s\n",
|
||||
gpg_strerror (rc));
|
||||
}
|
||||
#endif /*USE_GNU_PTH*/
|
||||
#endif /*!HAVE_OPENSC*/
|
||||
|
||||
/* Check that the libraries are suitable. Do it here because
|
||||
the option parsing may need services of the library */
|
||||
if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
|
||||
{
|
||||
log_fatal( _("libgcrypt is too old (need %s, have %s)\n"),
|
||||
NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
|
||||
}
|
||||
|
||||
|
||||
gcry_set_log_handler (my_gcry_logger, NULL);
|
||||
/* FIXME? gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);*/
|
||||
|
||||
pargs.argc = &argc;
|
||||
pargs.argv = &argv;
|
||||
pargs.flags= 1; /* do not remove the args */
|
||||
while (arg_parse (&pargs, opts) )
|
||||
{
|
||||
switch (pargs.r_opt)
|
||||
{
|
||||
case oVerbose: opt.verbose++; break;
|
||||
case oQuiet: opt.quiet++; break;
|
||||
case oDebug: opt.debug |= pargs.r.ret_ulong; break;
|
||||
case oDebugAll: opt.debug = ~0; break;
|
||||
case oReaderPort: reader_port = pargs.r.ret_str; break;
|
||||
case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break;
|
||||
case oDisableCCID: opt.disable_ccid = 1; break;
|
||||
case oGenRandom: gen_random = pargs.r.ret_ulong; break;
|
||||
case oInteractive: interactive = 1; break;
|
||||
default : pargs.err = 2; break;
|
||||
}
|
||||
}
|
||||
if (log_get_errorcount(0))
|
||||
exit(2);
|
||||
|
||||
if (opt.verbose < 2)
|
||||
opt.verbose = 2; /* Hack to let select_openpgp print some info. */
|
||||
|
||||
if (argc)
|
||||
usage (1);
|
||||
|
||||
slot = apdu_open_reader (reader_port);
|
||||
if (slot == -1)
|
||||
exit (1);
|
||||
|
||||
if (!gen_random && !opt.quiet)
|
||||
{
|
||||
rc = atr_dump (slot, stdout);
|
||||
if (rc)
|
||||
log_error ("can't dump ATR: %s\n", gpg_strerror (rc));
|
||||
}
|
||||
|
||||
if (interactive)
|
||||
interactive_shell (slot);
|
||||
else
|
||||
{
|
||||
struct app_ctx_s appbuf;
|
||||
|
||||
/* Fixme: We better use app.c directly. */
|
||||
memset (&appbuf, 0, sizeof appbuf);
|
||||
appbuf.slot = slot;
|
||||
rc = app_select_openpgp (&appbuf);
|
||||
if (rc)
|
||||
{
|
||||
if (!opt.quiet)
|
||||
log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc));
|
||||
memset (&appbuf, 0, sizeof appbuf);
|
||||
appbuf.slot = slot;
|
||||
rc = app_select_dinsig (&appbuf);
|
||||
if (rc)
|
||||
{
|
||||
if (!opt.quiet)
|
||||
log_info ("selecting dinsig failed: %s\n", gpg_strerror (rc));
|
||||
dump_other_cards (slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
appbuf.initialized = 1;
|
||||
log_info ("dinsig application selected\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
appbuf.initialized = 1;
|
||||
log_info ("openpgp application selected\n");
|
||||
|
||||
if (gen_random)
|
||||
{
|
||||
size_t nbytes;
|
||||
unsigned char *buffer;
|
||||
|
||||
buffer = xmalloc (4096);
|
||||
do
|
||||
{
|
||||
nbytes = gen_random > 4096? 4096 : gen_random;
|
||||
rc = app_get_challenge (&appbuf, nbytes, buffer);
|
||||
if (rc)
|
||||
log_error ("app_get_challenge failed: %s\n",gpg_strerror (rc));
|
||||
else
|
||||
{
|
||||
if (fwrite (buffer, nbytes, 1, stdout) != 1)
|
||||
log_error ("writing to stdout failed: %s\n",
|
||||
strerror (errno));
|
||||
gen_random -= nbytes;
|
||||
}
|
||||
}
|
||||
while (gen_random && !log_get_errorcount (0));
|
||||
xfree (buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return log_get_errorcount (0)? 2:0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
send_status_info (CTRL ctrl, const char *keyword, ...)
|
||||
{
|
||||
/* DUMMY */
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Dump BUFFER of length NBYTES in a nicely human readable format. */
|
||||
static void
|
||||
dump_buffer (const unsigned char *buffer, size_t nbytes)
|
||||
{
|
||||
int i;
|
||||
|
||||
while (nbytes)
|
||||
{
|
||||
for (i=0; i < 16 && i < nbytes; i++)
|
||||
printf ("%02X%s ", buffer[i], i==8? " ":"");
|
||||
for (; i < 16; i++)
|
||||
printf (" %s ", i==8? " ":"");
|
||||
putchar (' ');
|
||||
putchar (' ');
|
||||
for (i=0; i < 16 && i < nbytes; i++)
|
||||
if (isprint (buffer[i]))
|
||||
putchar (buffer[i]);
|
||||
else
|
||||
putchar ('.');
|
||||
nbytes -= i;
|
||||
buffer += i;
|
||||
for (; i < 16; i++)
|
||||
putchar (' ');
|
||||
putchar ('\n');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
dump_or_store_buffer (const char *arg,
|
||||
const unsigned char *buffer, size_t nbytes)
|
||||
{
|
||||
const char *s = strchr (arg, '>');
|
||||
int append;
|
||||
FILE *fp;
|
||||
|
||||
if (!s)
|
||||
{
|
||||
dump_buffer (buffer, nbytes);
|
||||
return;
|
||||
}
|
||||
if ((append = (*++s == '>')))
|
||||
s++;
|
||||
fp = fopen (s, append? "ab":"wb");
|
||||
if (!fp)
|
||||
{
|
||||
log_error ("failed to create `%s': %s\n", s, strerror (errno));
|
||||
return;
|
||||
}
|
||||
if (nbytes && fwrite (buffer, nbytes, 1, fp) != 1)
|
||||
log_error ("failed to write to `%s': %s\n", s, strerror (errno));
|
||||
if (fclose (fp))
|
||||
log_error ("failed to close `%s': %s\n", s, strerror (errno));
|
||||
}
|
||||
|
||||
|
||||
/* Convert STRING into a a newly allocated buffer and return the
|
||||
length of the buffer in R_LENGTH. Detect xx:xx:xx... sequence and
|
||||
unhexify that one. */
|
||||
static unsigned char *
|
||||
pin_to_buffer (const char *string, size_t *r_length)
|
||||
{
|
||||
unsigned char *buffer = xmalloc (strlen (string)+1);
|
||||
const char *s;
|
||||
size_t n;
|
||||
|
||||
for (s=string, n=0; *s; s += 3)
|
||||
{
|
||||
if (hexdigitp (s) && hexdigitp (s+1) && (s[2]==':'||!s[2]))
|
||||
{
|
||||
buffer[n++] = xtoi_2 (s);
|
||||
if (!s[2])
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy (buffer, string, strlen (string));
|
||||
*r_length = strlen (string);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
*r_length = n;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
my_read_line (int use_readline, char *prompt)
|
||||
{
|
||||
static char buf[256];
|
||||
|
||||
#ifdef HAVE_READLINE
|
||||
if (use_readline)
|
||||
{
|
||||
char *line = readline (prompt);
|
||||
if (line)
|
||||
trim_spaces (line);
|
||||
if (line && strlen (line) > 2 )
|
||||
add_history (line);
|
||||
return line;
|
||||
}
|
||||
#endif
|
||||
/* Either we don't have readline or we are not running
|
||||
interactively */
|
||||
#ifndef HAVE_READLINE
|
||||
printf ("%s", prompt );
|
||||
#endif
|
||||
fflush(stdout);
|
||||
if (!fgets(buf, sizeof(buf), stdin))
|
||||
return NULL;
|
||||
if (!strlen(buf))
|
||||
return NULL;
|
||||
if (buf[strlen (buf)-1] == '\n')
|
||||
buf[strlen (buf)-1] = 0;
|
||||
trim_spaces (buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Run a shell for interactive exploration of the card. */
|
||||
static void
|
||||
interactive_shell (int slot)
|
||||
{
|
||||
enum cmdids
|
||||
{
|
||||
cmdNOP = 0,
|
||||
cmdQUIT, cmdHELP,
|
||||
cmdSELECT,
|
||||
cmdCHDIR,
|
||||
cmdLS,
|
||||
cmdAPP,
|
||||
cmdREAD,
|
||||
cmdREADREC,
|
||||
cmdREADSHORTREC,
|
||||
cmdDEBUG,
|
||||
cmdVERIFY,
|
||||
cmdCHANGEREF,
|
||||
cmdREADPK,
|
||||
|
||||
cmdINVCMD
|
||||
};
|
||||
static struct
|
||||
{
|
||||
const char *name;
|
||||
enum cmdids id;
|
||||
const char *desc;
|
||||
} cmds[] = {
|
||||
{ "quit" , cmdQUIT , "quit this menu" },
|
||||
{ "q" , cmdQUIT , NULL },
|
||||
{ "help" , cmdHELP , "show this help" },
|
||||
{ "?" , cmdHELP , NULL },
|
||||
{ "debug" , cmdDEBUG, "set debugging flags" },
|
||||
{ "select" , cmdSELECT, "select file (EF)" },
|
||||
{ "s" , cmdSELECT, NULL },
|
||||
{ "chdir" , cmdCHDIR, "change directory (select DF)"},
|
||||
{ "cd" , cmdCHDIR, NULL },
|
||||
{ "ls" , cmdLS, "list directory (some cards only)"},
|
||||
{ "app" , cmdAPP, "select application"},
|
||||
{ "read" , cmdREAD, "read binary" },
|
||||
{ "rb" , cmdREAD, NULL },
|
||||
{ "readrec", cmdREADREC, "read record(s)" },
|
||||
{ "rr" , cmdREADREC, NULL },
|
||||
{ "rsr" , cmdREADSHORTREC, "readshortrec RECNO SHORT_EF" },
|
||||
{ "verify" , cmdVERIFY, "verify CHVNO PIN" },
|
||||
{ "ver" , cmdVERIFY, NULL },
|
||||
{ "changeref", cmdCHANGEREF, "change reference data" },
|
||||
{ "readpk", cmdREADPK, "read a public key" },
|
||||
{ NULL, cmdINVCMD }
|
||||
};
|
||||
enum cmdids cmd = cmdNOP;
|
||||
int use_readline = isatty (fileno(stdin));
|
||||
char *line;
|
||||
gpg_error_t err = 0;
|
||||
unsigned char *result = NULL;
|
||||
size_t resultlen;
|
||||
|
||||
#ifdef HAVE_READLINE
|
||||
if (use_readline)
|
||||
using_history ();
|
||||
#endif
|
||||
|
||||
for (;;)
|
||||
{
|
||||
int arg_number;
|
||||
const char *arg_string = "";
|
||||
const char *arg_next = "";
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
if (err)
|
||||
printf ("command failed: %s\n", gpg_strerror (err));
|
||||
err = 0;
|
||||
xfree (result);
|
||||
result = NULL;
|
||||
|
||||
printf ("\n");
|
||||
do
|
||||
{
|
||||
line = my_read_line (use_readline, "cmd> ");
|
||||
}
|
||||
while ( line && *line == '#' );
|
||||
|
||||
arg_number = 0;
|
||||
if (!line || *line == CONTROL_D)
|
||||
cmd = cmdQUIT;
|
||||
else if (!*line)
|
||||
cmd = cmdNOP;
|
||||
else {
|
||||
if ((p=strchr (line,' ')))
|
||||
{
|
||||
char *endp;
|
||||
|
||||
*p++ = 0;
|
||||
trim_spaces (line);
|
||||
trim_spaces (p);
|
||||
arg_number = strtol (p, &endp, 0);
|
||||
arg_string = p;
|
||||
if (endp != p)
|
||||
{
|
||||
arg_next = endp;
|
||||
while ( spacep (arg_next) )
|
||||
arg_next++;
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; cmds[i].name; i++ )
|
||||
if (!ascii_strcasecmp (line, cmds[i].name ))
|
||||
break;
|
||||
|
||||
cmd = cmds[i].id;
|
||||
}
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case cmdHELP:
|
||||
for (i=0; cmds[i].name; i++ )
|
||||
if (cmds[i].desc)
|
||||
printf("%-10s %s\n", cmds[i].name, _(cmds[i].desc) );
|
||||
break;
|
||||
|
||||
case cmdQUIT:
|
||||
goto leave;
|
||||
|
||||
case cmdNOP:
|
||||
break;
|
||||
|
||||
case cmdDEBUG:
|
||||
if (!*arg_string)
|
||||
opt.debug = opt.debug? 0 : 2048;
|
||||
else
|
||||
opt.debug = arg_number;
|
||||
break;
|
||||
|
||||
case cmdSELECT:
|
||||
err = iso7816_select_file (slot, arg_number, 0, NULL, NULL);
|
||||
break;
|
||||
|
||||
case cmdCHDIR:
|
||||
err = iso7816_select_file (slot, arg_number, 1, NULL, NULL);
|
||||
break;
|
||||
|
||||
case cmdLS:
|
||||
err = iso7816_list_directory (slot, 1, &result, &resultlen);
|
||||
if (!err || gpg_err_code (err) == GPG_ERR_ENOENT)
|
||||
err = iso7816_list_directory (slot, 0, &result, &resultlen);
|
||||
/* FIXME: Do something with RESULT. */
|
||||
break;
|
||||
|
||||
case cmdAPP:
|
||||
{
|
||||
app_t app;
|
||||
|
||||
app = select_application (NULL, slot, *arg_string? arg_string:NULL);
|
||||
if (app)
|
||||
{
|
||||
char *sn;
|
||||
|
||||
app_get_serial_and_stamp (app, &sn, NULL);
|
||||
log_info ("application `%s' ready; sn=%s\n",
|
||||
app->apptype?app->apptype:"?", sn? sn:"[none]");
|
||||
release_application (app);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case cmdREAD:
|
||||
err = iso7816_read_binary (slot, 0, 0, &result, &resultlen);
|
||||
if (!err)
|
||||
dump_or_store_buffer (arg_string, result, resultlen);
|
||||
break;
|
||||
|
||||
case cmdREADREC:
|
||||
if (*arg_string == '*' && (!arg_string[1] || arg_string[1] == ' '))
|
||||
{
|
||||
/* Fixme: Can't write to a file yet. */
|
||||
for (i=1, err=0; !err; i++)
|
||||
{
|
||||
xfree (result); result = NULL;
|
||||
err = iso7816_read_record (slot, i, 1, 0,
|
||||
&result, &resultlen);
|
||||
if (!err)
|
||||
dump_buffer (result, resultlen);
|
||||
}
|
||||
if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
|
||||
err = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
err = iso7816_read_record (slot, arg_number, 1, 0,
|
||||
&result, &resultlen);
|
||||
if (!err)
|
||||
dump_or_store_buffer (arg_string, result, resultlen);
|
||||
}
|
||||
break;
|
||||
|
||||
case cmdREADSHORTREC:
|
||||
{
|
||||
int short_ef;
|
||||
|
||||
short_ef = strtol (arg_next, NULL, 0);
|
||||
|
||||
if (short_ef < 1 || short_ef > 254)
|
||||
printf ("error: short EF must be between 1 and 254\n");
|
||||
else
|
||||
{
|
||||
err = iso7816_read_record (slot, arg_number, 1, short_ef,
|
||||
&result, &resultlen);
|
||||
if (!err)
|
||||
dump_or_store_buffer (arg_string, result, resultlen);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case cmdVERIFY:
|
||||
if (arg_number < 0 || arg_number > 255 || (arg_number & 127) > 31)
|
||||
printf ("error: invalid CHVNO\n");
|
||||
else
|
||||
{
|
||||
unsigned char *pin;
|
||||
size_t pinlen;
|
||||
|
||||
pin = pin_to_buffer (arg_next, &pinlen);
|
||||
err = iso7816_verify (slot, arg_number, pin, pinlen);
|
||||
xfree (pin);
|
||||
}
|
||||
break;
|
||||
|
||||
case cmdCHANGEREF:
|
||||
{
|
||||
const char *newpin = arg_next;
|
||||
|
||||
while ( *newpin && !spacep (newpin) )
|
||||
newpin++;
|
||||
while ( spacep (newpin) )
|
||||
newpin++;
|
||||
|
||||
if (arg_number < 0 || arg_number > 255 || (arg_number & 127) > 31)
|
||||
printf ("error: invalid CHVNO\n");
|
||||
else if (!*arg_next || !*newpin || newpin == arg_next)
|
||||
printf ("usage: changeref CHVNO OLDPIN NEWPIN\n");
|
||||
else
|
||||
{
|
||||
char *oldpin = xstrdup (arg_next);
|
||||
unsigned char *oldpin_buf, *newpin_buf;
|
||||
size_t oldpin_len, newpin_len;
|
||||
|
||||
for (p=oldpin; *p && !spacep (p); p++ )
|
||||
;
|
||||
*p = 0;
|
||||
oldpin_buf = pin_to_buffer (oldpin, &oldpin_len);
|
||||
newpin_buf = pin_to_buffer (newpin, &newpin_len);
|
||||
|
||||
err = iso7816_change_reference_data (slot, arg_number,
|
||||
oldpin_buf, oldpin_len,
|
||||
newpin_buf, newpin_len);
|
||||
|
||||
xfree (newpin_buf);
|
||||
xfree (oldpin_buf);
|
||||
xfree (oldpin);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case cmdREADPK:
|
||||
if (arg_number < 1 || arg_number > 255)
|
||||
printf ("usage: readpk CRTBYTE1\n");
|
||||
else
|
||||
{
|
||||
unsigned char crt[2];
|
||||
|
||||
crt[0] = arg_number;
|
||||
crt[1] = 0;
|
||||
err = iso7816_read_public_key(slot, crt, 2,
|
||||
&result, &resultlen);
|
||||
if (!err)
|
||||
dump_or_store_buffer (arg_string, result, resultlen);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case cmdINVCMD:
|
||||
default:
|
||||
printf ("\n");
|
||||
printf ("Invalid command (try \"help\")\n");
|
||||
break;
|
||||
} /* End command switch. */
|
||||
} /* End of main menu loop. */
|
||||
|
||||
leave:
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Figure out whether the current card is a German Geldkarte and print
|
||||
what we know about it. */
|
||||
static int
|
||||
dump_geldkarte (int slot)
|
||||
{
|
||||
unsigned char *r = NULL;
|
||||
size_t rlen;
|
||||
const char *t;
|
||||
|
||||
if (iso7816_read_record (slot, 1, 1, 0xbc, &r, &rlen))
|
||||
return -1;
|
||||
/* We require that the record is at least 24 bytes, the first byte
|
||||
is 0x67 and the filler byte is correct. */
|
||||
if (rlen < 24 || *r != 0x67 || r[22])
|
||||
return -1;
|
||||
|
||||
/* The short Bankleitzahl consists of 3 bytes at offset 1. */
|
||||
switch (r[1])
|
||||
{
|
||||
case 0x21: t = "Oeffentlich-rechtliche oder private Bank"; break;
|
||||
case 0x22: t = "Privat- oder Geschäftsbank"; break;
|
||||
case 0x25: t = "Sparkasse"; break;
|
||||
case 0x26:
|
||||
case 0x29: t = "Genossenschaftsbank"; break;
|
||||
default:
|
||||
xfree (r);
|
||||
return -1; /* Probably not a Geldkarte. */
|
||||
}
|
||||
|
||||
printf ("KBLZ .....: %02X-%02X%02X (%s)\n", r[1], r[2], r[3], t);
|
||||
printf ("Card-No ..: %02X%02X%02X%02X%02X\n", r[4], r[5], r[6], r[7], r[8]);
|
||||
|
||||
/* Byte 10 enthält im linken Halbbyte eine Prüfziffer, die nach dem */
|
||||
/* Verfahren 'Luhn formula for computing modulus 10' über die Ziffern der */
|
||||
/* ersten 9 Byte berechnet ist. */
|
||||
|
||||
/* Das rechte Halbbyte wird zu 'D' gesetzt. */
|
||||
|
||||
/* Für die Berechnung der Luhn-Prüfziffer sind die folgenden Schritte */
|
||||
/* durchzuführen: */
|
||||
|
||||
/* Schritt 1: Mit der rechtesten Ziffer beginnend ist einschließlich dieser */
|
||||
/* Ziffer jede übernächste Ziffer zu verdoppeln (mit 2 multiplizieren). */
|
||||
|
||||
/* Schritt 2: Die einzelnen Ziffern der Produkte aus Schritt 1 und die bei */
|
||||
/* diesen Multiplikationen unberührt gebliebenen Ziffern sind zu addieren. */
|
||||
|
||||
/* Schritt 3: Das Ergebnis der Addition aus Schritt 2 ist von dem auf die */
|
||||
/* nächst höhere Zahl mit der Einerstelle 0 aufgerundeten Ergebnis der */
|
||||
/* Addition aus Schritt 2 abzuziehen. Wenn das Ergebnis der Addition aus */
|
||||
/* Schritt 2 bereits eine Zahl mit der Einerstelle 0 ergibt (z.B. 30, 40, */
|
||||
/* usw.), ist die Prüfziffer 0. */
|
||||
|
||||
/* Beispiel: Kartennummer ohne Prüfziffer: 992 839 871 */
|
||||
|
||||
/* 9 9 2 8 3 9 8 7 1 */
|
||||
|
||||
/* x 2 x 2 x 2 x 2 x 2 Schritt 1 */
|
||||
|
||||
/* 18 4 6 16 2 */
|
||||
|
||||
/* 1+8 +9 +4 +8 +6 +9 +1+6 +7 +2 = 61 Schritt 2 */
|
||||
|
||||
/* 70-61 = 9 Schritt 3 */
|
||||
|
||||
/* Prüfziffer zu 992 839 871 = 9 */
|
||||
|
||||
|
||||
printf ("Expires at: %02X/%02X\n", r[11], r[10] );
|
||||
printf ("Valid from: %02X.%02X.%02X\n", r[14], r[13], r[12]);
|
||||
printf ("Country ..: %02X%02X\n", r[15], r[16]);
|
||||
printf ("Currency .: %c%c%c\n", isascii (r[17])? r[17]:' ',
|
||||
isascii (r[18])? r[18]:' ', isascii (r[19])? r[19]:' ');
|
||||
printf ("Cur.-Mult : %s\n",
|
||||
r[20] == 0x01? "0.01":
|
||||
r[20] == 0x02? "0.1":
|
||||
r[20] == 0x04? "1":
|
||||
r[20] == 0x08? "10":
|
||||
r[20] == 0x10? "100":
|
||||
r[20] == 0x20? "1000": "?");
|
||||
printf ("ZKA ChipID: %02X\n", r[21]);
|
||||
printf ("OS version: %02X\n", r[23]);
|
||||
|
||||
xfree (r);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Try to figure out the type of teh card and dump its contents. */
|
||||
static void
|
||||
dump_other_cards (int slot)
|
||||
{
|
||||
|
||||
if (!dump_geldkarte (slot))
|
||||
return;
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user