* 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:
Werner Koch 2004-10-20 08:54:45 +00:00
parent 837c74f7e4
commit d33703e5fe
12 changed files with 739 additions and 929 deletions

4
NEWS
View File

@ -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
View File

@ -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

View File

@ -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.

View File

@ -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`

View File

@ -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

View File

@ -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.

View File

@ -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 \

View File

@ -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;

View File

@ -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)

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}