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 + + * gpg-agent.texi (Invoking GPG-AGENT): Tell that GPG_TTY needs to + be set in all cases. + 2004-09-30 Werner Koch * 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 + + * 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 + + * ccid-driver.c (ccid_get_atr): Reset T=1 state info. + 2004-10-14 Werner Koch * 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 +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; 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) + 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); 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 - -#include -#include -#include -#include -#include -#include -#ifdef USE_GNU_PTH -# include -#endif - -#ifdef HAVE_READLINE_READLINE_H -#include -#include -#endif - -#define JNLIB_NEED_LOG_LOGV -#include "scdaemon.h" -#include - -#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; - -} -