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
|
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)
|
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
|
There is also limited support to export a private key in PKCS-12
|
||||||
format. However there is no MAC applied.
|
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
|
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>
|
2004-09-30 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
* gpg.texi: New.
|
* gpg.texi: New.
|
||||||
|
@ -43,10 +43,8 @@ fi
|
|||||||
@end smallexample
|
@end smallexample
|
||||||
|
|
||||||
@noindent
|
@noindent
|
||||||
If you want to use a curses based pinentry (which is usually also the
|
You should aleays add the following lines to your @code{.bashrc} or
|
||||||
fallback mode for a GUI based pinentry), you should add these lines to
|
whatever initialization file is used for all shell invocations:
|
||||||
your @code{.bashrc} or whatever initialization file is used for all shell
|
|
||||||
invocations:
|
|
||||||
|
|
||||||
@smallexample
|
@smallexample
|
||||||
GPG_TTY=`tty`
|
GPG_TTY=`tty`
|
||||||
|
@ -170,7 +170,8 @@ default is 32768 (first USB device).
|
|||||||
|
|
||||||
@item --ctapi-driver @var{library}
|
@item --ctapi-driver @var{library}
|
||||||
Use @var{library} to access the smartcard reader. The current default
|
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
|
@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>
|
2004-10-14 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
* app-openpgp.c (parse_login_data): New.
|
* app-openpgp.c (parse_login_data): New.
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
## 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-copykeys
|
||||||
pkglib_PROGRAMS = pcsc-wrapper
|
pkglib_PROGRAMS = pcsc-wrapper
|
||||||
|
|
||||||
AM_CPPFLAGS = -I$(top_srcdir)/intl -I$(top_srcdir)/common
|
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@ \
|
$(LIBUSB_LIBS) $(OPENSC_LIBS) -lgpg-error @LIBINTL@ \
|
||||||
@DL_LIBS@
|
@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_SOURCES = \
|
||||||
sc-copykeys.c scdaemon.h \
|
sc-copykeys.c scdaemon.h \
|
||||||
apdu.c apdu.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_t handle;
|
||||||
} rapdu;
|
} rapdu;
|
||||||
#endif /*USE_G10CODE_RAPDU*/
|
#endif /*USE_G10CODE_RAPDU*/
|
||||||
|
char *rdrname; /* Name of the connected reader or NULL if unknown. */
|
||||||
|
int last_status;
|
||||||
int status;
|
int status;
|
||||||
unsigned char atr[33];
|
unsigned char atr[33];
|
||||||
size_t atrlen; /* A zero length indicates that the ATR has
|
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_UNPOWER_CARD 2
|
||||||
#define PCSC_EJECT_CARD 3
|
#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 protocol;
|
||||||
unsigned long pci_len;
|
unsigned long pci_len;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct pcsc_io_request_s *pcsc_io_request_t;
|
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,
|
long (* DLSTDCALL pcsc_establish_context) (unsigned long scope,
|
||||||
const void *reserved1,
|
const void *reserved1,
|
||||||
const void *reserved2,
|
const void *reserved2,
|
||||||
@ -184,12 +220,21 @@ long (* DLSTDCALL pcsc_release_context) (unsigned long context);
|
|||||||
long (* DLSTDCALL pcsc_list_readers) (unsigned long context,
|
long (* DLSTDCALL pcsc_list_readers) (unsigned long context,
|
||||||
const char *groups,
|
const char *groups,
|
||||||
char *readers, unsigned long*readerslen);
|
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,
|
long (* DLSTDCALL pcsc_connect) (unsigned long context,
|
||||||
const char *reader,
|
const char *reader,
|
||||||
unsigned long share_mode,
|
unsigned long share_mode,
|
||||||
unsigned long preferred_protocols,
|
unsigned long preferred_protocols,
|
||||||
unsigned long *r_card,
|
unsigned long *r_card,
|
||||||
unsigned long *r_active_protocol);
|
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,
|
long (* DLSTDCALL pcsc_disconnect) (unsigned long card,
|
||||||
unsigned long disposition);
|
unsigned long disposition);
|
||||||
long (* DLSTDCALL pcsc_status) (unsigned long card,
|
long (* DLSTDCALL pcsc_status) (unsigned long card,
|
||||||
@ -211,7 +256,6 @@ long (* DLSTDCALL pcsc_set_timeout) (unsigned long context,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Helper
|
Helper
|
||||||
@ -254,11 +298,13 @@ new_reader_slot (void)
|
|||||||
reader_table[reader].dump_status_reader = NULL;
|
reader_table[reader].dump_status_reader = NULL;
|
||||||
|
|
||||||
reader_table[reader].used = 1;
|
reader_table[reader].used = 1;
|
||||||
|
reader_table[reader].last_status = 0;
|
||||||
#ifdef NEED_PCSC_WRAPPER
|
#ifdef NEED_PCSC_WRAPPER
|
||||||
reader_table[reader].pcsc.req_fd = -1;
|
reader_table[reader].pcsc.req_fd = -1;
|
||||||
reader_table[reader].pcsc.rsp_fd = -1;
|
reader_table[reader].pcsc.rsp_fd = -1;
|
||||||
reader_table[reader].pcsc.pid = (pid_t)(-1);
|
reader_table[reader].pcsc.pid = (pid_t)(-1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return reader;
|
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
|
static int
|
||||||
pcsc_get_status (int slot, unsigned int *status)
|
pcsc_get_status (int slot, unsigned int *status)
|
||||||
{
|
{
|
||||||
*status = 1|2|4; /* FIXME!!!! */
|
#ifdef NEED_PCSC_WRAPPER
|
||||||
return 0;
|
long err;
|
||||||
}
|
reader_table_t slotp;
|
||||||
|
size_t len, full_len;
|
||||||
|
int i, n;
|
||||||
|
unsigned char msgbuf[9];
|
||||||
|
unsigned char buffer[12];
|
||||||
|
|
||||||
static int
|
slotp = reader_table + slot;
|
||||||
reset_pcsc_reader (int slot)
|
|
||||||
{
|
if (slotp->pcsc.req_fd == -1
|
||||||
return SW_HOST_NOT_SUPPORTED;
|
|| 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*/
|
#else /*!NEED_PCSC_WRAPPER*/
|
||||||
|
|
||||||
pcsc_release_context (reader_table[slot].pcsc.context);
|
pcsc_release_context (reader_table[slot].pcsc.context);
|
||||||
|
xfree (reader_table[slot].rdrname);
|
||||||
|
reader_table[slot].rdrname = NULL;
|
||||||
reader_table[slot].used = 0;
|
reader_table[slot].used = 0;
|
||||||
return 0;
|
return 0;
|
||||||
#endif /*!NEED_PCSC_WRAPPER*/
|
#endif /*!NEED_PCSC_WRAPPER*/
|
||||||
@ -999,8 +1322,14 @@ open_pcsc_reader (const char *portstr)
|
|||||||
slotp->pcsc.rsp_fd = rp[0];
|
slotp->pcsc.rsp_fd = rp[0];
|
||||||
|
|
||||||
/* Wait for the intermediate child to terminate. */
|
/* 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. */
|
/* Now send the open request. */
|
||||||
msgbuf[0] = 0x01; /* OPEN command. */
|
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));
|
log_error ("PC/SC OPEN failed: %s\n", pcsc_error_string (err));
|
||||||
goto command_failed;
|
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;
|
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",
|
if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
|
||||||
i? strerror (errno) : "premature EOF");
|
{
|
||||||
goto command_failed;
|
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;
|
slotp->atrlen = len;
|
||||||
|
|
||||||
@ -1132,41 +1472,63 @@ open_pcsc_reader (const char *portstr)
|
|||||||
p += strlen (p) + 1;
|
p += strlen (p) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = pcsc_connect (reader_table[slot].pcsc.context,
|
reader_table[slot].rdrname = xtrymalloc (strlen (portstr? portstr : list)+1);
|
||||||
portstr? portstr : list,
|
if (!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",
|
log_error ("error allocating memory for reader name\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);
|
|
||||||
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 -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (atrlen >= DIM (reader_table[0].atr))
|
strcpy (reader_table[slot].rdrname, portstr? portstr : list);
|
||||||
log_bug ("ATR returned by pcsc_status is too large\n");
|
xfree (list);
|
||||||
reader_table[slot].atrlen = atrlen;
|
|
||||||
|
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].close_reader = close_pcsc_reader;
|
||||||
reader_table[slot].reset_reader = reset_pcsc_reader;
|
reader_table[slot].reset_reader = reset_pcsc_reader;
|
||||||
@ -1311,6 +1673,12 @@ open_ccid_reader (const char *portstr)
|
|||||||
slotp->atrlen = 0;
|
slotp->atrlen = 0;
|
||||||
err = 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].close_reader = close_ccid_reader;
|
||||||
reader_table[slot].shutdown_reader = shutdown_ccid_reader;
|
reader_table[slot].shutdown_reader = shutdown_ccid_reader;
|
||||||
@ -1970,11 +2338,21 @@ apdu_open_reader (const char *portstr)
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (!pcsc_list_readers)
|
if (!pcsc_list_readers)
|
||||||
pcsc_list_readers = dlsym (handle, "SCardListReadersA");
|
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
|
#endif
|
||||||
pcsc_connect = dlsym (handle, "SCardConnect");
|
pcsc_connect = dlsym (handle, "SCardConnect");
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (!pcsc_connect)
|
if (!pcsc_connect)
|
||||||
pcsc_connect = dlsym (handle, "SCardConnectA");
|
pcsc_connect = dlsym (handle, "SCardConnectA");
|
||||||
|
#endif
|
||||||
|
pcsc_reconnect = dlsym (handle, "SCardReconnect");
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (!pcsc_reconnect)
|
||||||
|
pcsc_reconnect = dlsym (handle, "SCardReconnectA");
|
||||||
#endif
|
#endif
|
||||||
pcsc_disconnect = dlsym (handle, "SCardDisconnect");
|
pcsc_disconnect = dlsym (handle, "SCardDisconnect");
|
||||||
pcsc_status = dlsym (handle, "SCardStatus");
|
pcsc_status = dlsym (handle, "SCardStatus");
|
||||||
@ -1990,7 +2368,9 @@ apdu_open_reader (const char *portstr)
|
|||||||
if (!pcsc_establish_context
|
if (!pcsc_establish_context
|
||||||
|| !pcsc_release_context
|
|| !pcsc_release_context
|
||||||
|| !pcsc_list_readers
|
|| !pcsc_list_readers
|
||||||
|
|| !pcsc_get_status_change
|
||||||
|| !pcsc_connect
|
|| !pcsc_connect
|
||||||
|
|| !pcsc_reconnect
|
||||||
|| !pcsc_disconnect
|
|| !pcsc_disconnect
|
||||||
|| !pcsc_status
|
|| !pcsc_status
|
||||||
|| !pcsc_begin_transaction
|
|| !pcsc_begin_transaction
|
||||||
@ -2001,11 +2381,13 @@ apdu_open_reader (const char *portstr)
|
|||||||
/* Note that set_timeout is currently not used and also not
|
/* Note that set_timeout is currently not used and also not
|
||||||
available under Windows. */
|
available under Windows. */
|
||||||
log_error ("apdu_open_reader: invalid PC/SC driver "
|
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_establish_context,
|
||||||
!!pcsc_release_context,
|
!!pcsc_release_context,
|
||||||
!!pcsc_list_readers,
|
!!pcsc_list_readers,
|
||||||
|
!!pcsc_get_status_change,
|
||||||
!!pcsc_connect,
|
!!pcsc_connect,
|
||||||
|
!!pcsc_reconnect,
|
||||||
!!pcsc_disconnect,
|
!!pcsc_disconnect,
|
||||||
!!pcsc_status,
|
!!pcsc_status,
|
||||||
!!pcsc_begin_transaction,
|
!!pcsc_begin_transaction,
|
||||||
@ -2048,8 +2430,12 @@ apdu_open_remote_reader (const char *portstr,
|
|||||||
readfnc, readfnc_value,
|
readfnc, readfnc_value,
|
||||||
writefnc, writefnc_value,
|
writefnc, writefnc_value,
|
||||||
closefnc, closefnc_value);
|
closefnc, closefnc_value);
|
||||||
|
#else
|
||||||
|
#ifdef _WIN32
|
||||||
|
errno = ENOENT;
|
||||||
#else
|
#else
|
||||||
errno = ENOSYS;
|
errno = ENOSYS;
|
||||||
|
#endif
|
||||||
return -1;
|
return -1;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -2102,9 +2488,17 @@ apdu_reset (int slot)
|
|||||||
if ((sw = lock_slot (slot)))
|
if ((sw = lock_slot (slot)))
|
||||||
return sw;
|
return sw;
|
||||||
|
|
||||||
|
reader_table[slot].last_status = 0;
|
||||||
if (reader_table[slot].reset_reader)
|
if (reader_table[slot].reset_reader)
|
||||||
sw = reader_table[slot].reset_reader (slot);
|
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);
|
unlock_slot (slot);
|
||||||
return sw;
|
return sw;
|
||||||
}
|
}
|
||||||
@ -2139,7 +2533,16 @@ apdu_activate (int slot)
|
|||||||
/* We don't have an ATR or a card is present though inactive:
|
/* We don't have an ATR or a card is present though inactive:
|
||||||
do a reset now. */
|
do a reset now. */
|
||||||
if (reader_table[slot].reset_reader)
|
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);
|
unlock_slot (slot);
|
||||||
|
|
||||||
if (sw)
|
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)
|
if (status)
|
||||||
*status = s;
|
*status = s;
|
||||||
|
@ -1307,6 +1307,8 @@ ccid_get_atr (ccid_driver_t handle,
|
|||||||
/* Note that we ignore the error code on purpose. */
|
/* Note that we ignore the error code on purpose. */
|
||||||
bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters, seqno);
|
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. */
|
/* Send an S-Block with our maximun IFSD to the CCID. */
|
||||||
if (!handle->auto_ifsd)
|
if (!handle->auto_ifsd)
|
||||||
|
@ -126,15 +126,8 @@ iso7816_map_sw (int sw)
|
|||||||
gpg_error_t
|
gpg_error_t
|
||||||
iso7816_select_application (int slot, const char *aid, size_t aidlen)
|
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 sw;
|
||||||
int p1 = 0x0C; /* No FCI to be returned. */
|
sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, 0, aidlen, aid);
|
||||||
|
|
||||||
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);
|
|
||||||
return map_sw (sw);
|
return map_sw (sw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,6 +86,26 @@ static int verbose;
|
|||||||
#define PCSC_UNPOWER_CARD 2
|
#define PCSC_UNPOWER_CARD 2
|
||||||
#define PCSC_EJECT_CARD 3
|
#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 {
|
struct pcsc_io_request_s {
|
||||||
unsigned long protocol;
|
unsigned long protocol;
|
||||||
unsigned long pci_len;
|
unsigned long pci_len;
|
||||||
@ -93,12 +113,25 @@ struct pcsc_io_request_s {
|
|||||||
|
|
||||||
typedef struct pcsc_io_request_s *pcsc_io_request_t;
|
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
|
static int driver_is_open; /* True if the PC/SC driver has been
|
||||||
initialzied and is ready for
|
initialzied and is ready for
|
||||||
operations. The follwoing variables
|
operations. The following variables
|
||||||
are then valid. */
|
are then valid. */
|
||||||
static unsigned long pcsc_context; /* The current PC/CS context. */
|
static unsigned long pcsc_context; /* The current PC/CS context. */
|
||||||
|
static char *current_rdrname;
|
||||||
static unsigned long pcsc_card;
|
static unsigned long pcsc_card;
|
||||||
static unsigned long pcsc_protocol;
|
static unsigned long pcsc_protocol;
|
||||||
static unsigned char current_atr[33];
|
static unsigned char current_atr[33];
|
||||||
@ -112,12 +145,21 @@ long (* pcsc_release_context) (unsigned long context);
|
|||||||
long (* pcsc_list_readers) (unsigned long context,
|
long (* pcsc_list_readers) (unsigned long context,
|
||||||
const char *groups,
|
const char *groups,
|
||||||
char *readers, unsigned long*readerslen);
|
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,
|
long (* pcsc_connect) (unsigned long context,
|
||||||
const char *reader,
|
const char *reader,
|
||||||
unsigned long share_mode,
|
unsigned long share_mode,
|
||||||
unsigned long preferred_protocols,
|
unsigned long preferred_protocols,
|
||||||
unsigned long *r_card,
|
unsigned long *r_card,
|
||||||
unsigned long *r_active_protocol);
|
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,
|
long (* pcsc_disconnect) (unsigned long card,
|
||||||
unsigned long disposition);
|
unsigned long disposition);
|
||||||
long (* pcsc_status) (unsigned long card,
|
long (* pcsc_status) (unsigned long card,
|
||||||
@ -284,7 +326,9 @@ load_pcsc_driver (const char *libname)
|
|||||||
pcsc_establish_context = dlsym (handle, "SCardEstablishContext");
|
pcsc_establish_context = dlsym (handle, "SCardEstablishContext");
|
||||||
pcsc_release_context = dlsym (handle, "SCardReleaseContext");
|
pcsc_release_context = dlsym (handle, "SCardReleaseContext");
|
||||||
pcsc_list_readers = dlsym (handle, "SCardListReaders");
|
pcsc_list_readers = dlsym (handle, "SCardListReaders");
|
||||||
|
pcsc_get_status_change = dlsym (handle, "SCardGetStatusChange");
|
||||||
pcsc_connect = dlsym (handle, "SCardConnect");
|
pcsc_connect = dlsym (handle, "SCardConnect");
|
||||||
|
pcsc_reconnect = dlsym (handle, "SCardReconnect");
|
||||||
pcsc_disconnect = dlsym (handle, "SCardDisconnect");
|
pcsc_disconnect = dlsym (handle, "SCardDisconnect");
|
||||||
pcsc_status = dlsym (handle, "SCardStatus");
|
pcsc_status = dlsym (handle, "SCardStatus");
|
||||||
pcsc_begin_transaction = dlsym (handle, "SCardBeginTransaction");
|
pcsc_begin_transaction = dlsym (handle, "SCardBeginTransaction");
|
||||||
@ -295,7 +339,9 @@ load_pcsc_driver (const char *libname)
|
|||||||
if (!pcsc_establish_context
|
if (!pcsc_establish_context
|
||||||
|| !pcsc_release_context
|
|| !pcsc_release_context
|
||||||
|| !pcsc_list_readers
|
|| !pcsc_list_readers
|
||||||
|
|| !pcsc_get_status_change
|
||||||
|| !pcsc_connect
|
|| !pcsc_connect
|
||||||
|
|| !pcsc_reconnect
|
||||||
|| !pcsc_disconnect
|
|| !pcsc_disconnect
|
||||||
|| !pcsc_status
|
|| !pcsc_status
|
||||||
|| !pcsc_begin_transaction
|
|| !pcsc_begin_transaction
|
||||||
@ -307,11 +353,13 @@ load_pcsc_driver (const char *libname)
|
|||||||
available under Windows. */
|
available under Windows. */
|
||||||
fprintf (stderr,
|
fprintf (stderr,
|
||||||
"apdu_open_reader: invalid PC/SC driver "
|
"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_establish_context,
|
||||||
!!pcsc_release_context,
|
!!pcsc_release_context,
|
||||||
!!pcsc_list_readers,
|
!!pcsc_list_readers,
|
||||||
|
!!pcsc_get_status_change,
|
||||||
!!pcsc_connect,
|
!!pcsc_connect,
|
||||||
|
!!pcsc_reconnect,
|
||||||
!!pcsc_disconnect,
|
!!pcsc_disconnect,
|
||||||
!!pcsc_status,
|
!!pcsc_status,
|
||||||
!!pcsc_begin_transaction,
|
!!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
|
/* Handle a open request. The argument is expected to be a string
|
||||||
with the port indentification. ARGBUF is always guaranteed to be
|
with the port identification. ARGBUF is always guaranteed to be
|
||||||
terminted by a 0 which is not counted in ARGLEN. We may modifiy
|
terminted by a 0 which is not counted in ARGLEN. We may modifiy
|
||||||
ARGBUF. */
|
ARGBUF. */
|
||||||
static void
|
static void
|
||||||
handle_open (unsigned char *argbuf, size_t arglen)
|
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");
|
fprintf (stderr, PGM ": PC/SC has already been opened\n");
|
||||||
request_failed (-1);
|
request_failed (-1);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = pcsc_establish_context (PCSC_SCOPE_SYSTEM, NULL, NULL, &pcsc_context);
|
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;
|
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,
|
err = pcsc_connect (pcsc_context,
|
||||||
portstr && *portstr? portstr : list,
|
current_rdrname,
|
||||||
PCSC_SHARE_EXCLUSIVE,
|
PCSC_SHARE_EXCLUSIVE,
|
||||||
PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
|
PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
|
||||||
&pcsc_card,
|
&pcsc_card,
|
||||||
&pcsc_protocol);
|
&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",
|
fprintf (stderr, PGM": pcsc_connect failed: %s (0x%lx)\n",
|
||||||
pcsc_error_string (err), err);
|
pcsc_error_string (err), err);
|
||||||
pcsc_release_context (pcsc_context);
|
pcsc_release_context (pcsc_context);
|
||||||
free (list);
|
free (current_rdrname);
|
||||||
|
current_rdrname = NULL;
|
||||||
request_failed (err);
|
request_failed (err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
atrlen = 32;
|
current_atrlen = 0;
|
||||||
/* (We need to pass a dummy buffer. We use LIST because it ought to
|
if (!err)
|
||||||
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",
|
char reader[250];
|
||||||
pcsc_error_string (err), err);
|
unsigned long readerlen;
|
||||||
pcsc_release_context (pcsc_context);
|
|
||||||
request_failed (err);
|
atrlen = 33;
|
||||||
return;
|
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;
|
driver_is_open = 1;
|
||||||
request_succeeded (current_atr, current_atrlen);
|
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");
|
fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
|
||||||
request_failed (-1);
|
request_failed (-1);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free (current_rdrname);
|
||||||
|
current_rdrname = NULL;
|
||||||
pcsc_release_context (pcsc_context);
|
pcsc_release_context (pcsc_context);
|
||||||
|
|
||||||
request_succeeded (NULL, 0);
|
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. */
|
with the APDU. We may modifiy ARGBUF. */
|
||||||
static void
|
static void
|
||||||
handle_transmit (unsigned char *argbuf, size_t arglen)
|
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");
|
fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
|
||||||
request_failed (-1);
|
request_failed (-1);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((pcsc_protocol & PCSC_PROTOCOL_T1))
|
if ((pcsc_protocol & PCSC_PROTOCOL_T1))
|
||||||
send_pci.protocol = PCSC_PROTOCOL_T1;
|
send_pci.protocol = PCSC_PROTOCOL_T1;
|
||||||
else
|
else
|
||||||
@ -502,15 +699,6 @@ handle_transmit (unsigned char *argbuf, size_t arglen)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
print_version (int with_help)
|
print_version (int with_help)
|
||||||
{
|
{
|
||||||
@ -613,6 +801,14 @@ main (int argc, char **argv)
|
|||||||
handle_transmit (argbuffer, arglen);
|
handle_transmit (argbuffer, arglen);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
handle_status (argbuffer, arglen);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
handle_reset (argbuffer, arglen);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fprintf (stderr, PGM ": invalid request 0x%02X\n", c);
|
fprintf (stderr, PGM ": invalid request 0x%02X\n", c);
|
||||||
exit (1);
|
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