aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2004-10-20 08:54:45 +0000
committerWerner Koch <[email protected]>2004-10-20 08:54:45 +0000
commitd33703e5fe46068ddfe7b04c44ec2e45f1f79ee3 (patch)
tree194a11c2a5fd9138ca8834fd5002a93bd591204c
parentFixed copyright years (diff)
downloadgnupg-d33703e5fe46068ddfe7b04c44ec2e45f1f79ee3.tar.gz
gnupg-d33703e5fe46068ddfe7b04c44ec2e45f1f79ee3.zip
* 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.
-rw-r--r--NEWS4
-rw-r--r--README2
-rw-r--r--doc/ChangeLog5
-rw-r--r--doc/gpg-agent.texi6
-rw-r--r--doc/scdaemon.texi3
-rw-r--r--scd/ChangeLog21
-rw-r--r--scd/Makefile.am18
-rw-r--r--scd/apdu.c488
-rw-r--r--scd/ccid-driver.c2
-rw-r--r--scd/iso7816.c9
-rw-r--r--scd/pcsc-wrapper.c274
-rw-r--r--scd/sc-investigate.c812
12 files changed, 727 insertions, 917 deletions
diff --git a/NEWS b/NEWS
index 595684dc3..9ba9ddac9 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,10 @@
Noteworthy changes in version 1.9.12
-------------------------------------------------
+ * [scdaemon] Partly rewrote the PC/SC code.
+
+ * Removed the sc-investigate tool. It is now in the separate
+ gscutils package.
Noteworthy changes in version 1.9.11 (2004-10-01)
diff --git a/README b/README
index c08c16af1..c14534e93 100644
--- a/README
+++ b/README
@@ -472,7 +472,7 @@ HOW TO EXPORT A PRIVATE KEY
There is also limited support to export a private key in PKCS-12
format. However there is no MAC applied.
- gpgsm --export-secret-key-p12 userIDey >foo.p12
+ gpgsm --export-secret-key-p12 userID >foo.p12
SMARTCARD INTRO
diff --git a/doc/ChangeLog b/doc/ChangeLog
index c8e955ef2..c5ff57691 100644
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -1,3 +1,8 @@
+2004-10-05 Werner Koch <[email protected]>
+
+ * gpg-agent.texi (Invoking GPG-AGENT): Tell that GPG_TTY needs to
+ be set in all cases.
+
2004-09-30 Werner Koch <[email protected]>
* gpg.texi: New.
diff --git a/doc/gpg-agent.texi b/doc/gpg-agent.texi
index 26b5634cc..f361cbf6b 100644
--- a/doc/gpg-agent.texi
+++ b/doc/gpg-agent.texi
@@ -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`
diff --git a/doc/scdaemon.texi b/doc/scdaemon.texi
index 42dedb6b4..872000175 100644
--- a/doc/scdaemon.texi
+++ b/doc/scdaemon.texi
@@ -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
diff --git a/scd/ChangeLog b/scd/ChangeLog
index 7fbc524b3..eba39c731 100644
--- a/scd/ChangeLog
+++ b/scd/ChangeLog
@@ -1,3 +1,24 @@
+2004-10-20 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * ccid-driver.c (ccid_get_atr): Reset T=1 state info.
+
2004-10-14 Werner Koch <[email protected]>
* app-openpgp.c (parse_login_data): New.
diff --git a/scd/Makefile.am b/scd/Makefile.am
index d1d669a1a..0d83271e0 100644
--- a/scd/Makefile.am
+++ b/scd/Makefile.am
@@ -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 \
diff --git a/scd/apdu.c b/scd/apdu.c
index 0d9ef3d0c..f4b32d141 100644
--- a/scd/apdu.c
+++ b/scd/apdu.c
@@ -125,6 +125,8 @@ struct reader_table_s {
rapdu_t handle;
} rapdu;
#endif /*USE_G10CODE_RAPDU*/
+ char *rdrname; /* Name of the connected reader or NULL if unknown. */
+ int last_status;
int status;
unsigned char atr[33];
size_t atrlen; /* A zero length indicates that the ATR has
@@ -169,13 +171,47 @@ static char (* DLSTDCALL CT_close) (unsigned short ctn);
#define PCSC_UNPOWER_CARD 2
#define PCSC_EJECT_CARD 3
-struct pcsc_io_request_s {
+#define PCSC_UNKNOWN 0x0001
+#define PCSC_ABSENT 0x0002 /* Card is absent. */
+#define PCSC_PRESENT 0x0004 /* Card is present. */
+#define PCSC_SWALLOWED 0x0008 /* Card is present and electrical connected. */
+#define PCSC_POWERED 0x0010 /* Card is powered. */
+#define PCSC_NEGOTIABLE 0x0020 /* Card is awaiting PTS. */
+#define PCSC_SPECIFIC 0x0040 /* Card is ready for use. */
+
+#define PCSC_STATE_UNAWARE 0x0000 /* Want status. */
+#define PCSC_STATE_IGNORE 0x0001 /* Ignore this reader. */
+#define PCSC_STATE_CHANGED 0x0002 /* State has changed. */
+#define PCSC_STATE_UNKNOWN 0x0004 /* Reader unknown. */
+#define PCSC_STATE_UNAVAILABLE 0x0008 /* Status unavailable. */
+#define PCSC_STATE_EMPTY 0x0010 /* Card removed. */
+#define PCSC_STATE_PRESENT 0x0020 /* Card inserted. */
+#define PCSC_STATE_ATRMATCH 0x0040 /* ATR matches card. */
+#define PCSC_STATE_EXCLUSIVE 0x0080 /* Exclusive Mode. */
+#define PCSC_STATE_INUSE 0x0100 /* Shared mode. */
+#define PCSC_STATE_MUTE 0x0200 /* Unresponsive card. */
+
+
+struct pcsc_io_request_s
+{
unsigned long protocol;
unsigned long pci_len;
};
typedef struct pcsc_io_request_s *pcsc_io_request_t;
+struct pcsc_readerstate_s
+{
+ const char *reader;
+ void *user_data;
+ unsigned long current_state;
+ unsigned long event_state;
+ unsigned long atrlen;
+ unsigned char atr[33];
+};
+
+typedef struct pcsc_readerstate_s *pcsc_readerstate_t;
+
long (* DLSTDCALL pcsc_establish_context) (unsigned long scope,
const void *reserved1,
const void *reserved2,
@@ -184,12 +220,21 @@ long (* DLSTDCALL pcsc_release_context) (unsigned long context);
long (* DLSTDCALL pcsc_list_readers) (unsigned long context,
const char *groups,
char *readers, unsigned long*readerslen);
+long (* DLSTDCALL pcsc_get_status_change) (unsigned long context,
+ unsigned long timeout,
+ pcsc_readerstate_t readerstates,
+ unsigned long nreaderstates);
long (* DLSTDCALL pcsc_connect) (unsigned long context,
const char *reader,
unsigned long share_mode,
unsigned long preferred_protocols,
unsigned long *r_card,
unsigned long *r_active_protocol);
+long (* DLSTDCALL pcsc_reconnect) (unsigned long card,
+ unsigned long share_mode,
+ unsigned long preferred_protocols,
+ unsigned long initialization,
+ unsigned long *r_active_protocol);
long (* DLSTDCALL pcsc_disconnect) (unsigned long card,
unsigned long disposition);
long (* DLSTDCALL pcsc_status) (unsigned long card,
@@ -211,7 +256,6 @@ long (* DLSTDCALL pcsc_set_timeout) (unsigned long context,
-
/*
Helper
@@ -254,11 +298,13 @@ new_reader_slot (void)
reader_table[reader].dump_status_reader = NULL;
reader_table[reader].used = 1;
+ reader_table[reader].last_status = 0;
#ifdef NEED_PCSC_WRAPPER
reader_table[reader].pcsc.req_fd = -1;
reader_table[reader].pcsc.rsp_fd = -1;
reader_table[reader].pcsc.pid = (pid_t)(-1);
#endif
+
return reader;
}
@@ -662,18 +708,293 @@ dump_pcsc_reader_status (int slot)
}
-
static int
-pcsc_get_status (int slot, unsigned int *status)
+reset_pcsc_reader (int slot)
{
- *status = 1|2|4; /* FIXME!!!! */
+#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
-reset_pcsc_reader (int slot)
+pcsc_get_status (int slot, unsigned int *status)
{
- return SW_HOST_NOT_SUPPORTED;
+#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];
+
+ 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;
}
+ reader_table[slot].rdrname = xtrymalloc (strlen (portstr? portstr : list)+1);
+ if (!reader_table[slot].rdrname)
+ {
+ log_error ("error allocating memory for reader name\n");
+ pcsc_release_context (reader_table[slot].pcsc.context);
+ reader_table[slot].used = 0;
+ return -1;
+ }
+ strcpy (reader_table[slot].rdrname, portstr? portstr : list);
+ xfree (list);
+
err = pcsc_connect (reader_table[slot].pcsc.context,
- portstr? portstr : list,
+ 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)
+ 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;
}
-
- 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)
+
+ reader_table[slot].atrlen = 0;
+ reader_table[slot].last_status = 0;
+ if (!err)
{
- log_error ("pcsc_status failed: %s (0x%lx)\n",
- pcsc_error_string (err), err);
- pcsc_release_context (reader_table[slot].pcsc.context);
- reader_table[slot].used = 0;
- return -1;
+ 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);
+ }
}
- if (atrlen >= DIM (reader_table[0].atr))
- log_bug ("ATR returned by pcsc_status is too large\n");
- reader_table[slot].atrlen = atrlen;
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;
@@ -1971,11 +2339,21 @@ apdu_open_reader (const char *portstr)
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");
#ifdef _WIN32
@@ -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,
@@ -2049,7 +2431,11 @@ apdu_open_remote_reader (const char *portstr,
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;
diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c
index 0a876f0bc..01c8a9980 100644
--- a/scd/ccid-driver.c
+++ b/scd/ccid-driver.c
@@ -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)
diff --git a/scd/iso7816.c b/scd/iso7816.c
index 4861466c1..9eff9d3f7 100644
--- a/scd/iso7816.c
+++ b/scd/iso7816.c
@@ -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);
}
diff --git a/scd/pcsc-wrapper.c b/scd/pcsc-wrapper.c
index 4f47ee95c..93e78fdfe 100644
--- a/scd/pcsc-wrapper.c
+++ b/scd/pcsc-wrapper.c
@@ -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)
- {
- fprintf (stderr, PGM": pcsc_status failed: %s (0x%lx)\n",
- pcsc_error_string (err), err);
- pcsc_release_context (pcsc_context);
- request_failed (err);
- return;
- }
- if (atrlen >= sizeof atr || atrlen >= sizeof current_atr)
+ current_atrlen = 0;
+ if (!err)
{
- fprintf (stderr, PGM": ATR returned by pcsc_status is too large\n");
- exit (4);
+ 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;
+ }
}
- 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);
diff --git a/scd/sc-investigate.c b/scd/sc-investigate.c
deleted file mode 100644
index 1f1920650..000000000
--- a/scd/sc-investigate.c
+++ /dev/null
@@ -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;
-
-}
-