diff --git a/ChangeLog b/ChangeLog index c6d8e0950..ff62810bb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,10 @@ 2004-09-09 Werner Koch - * configure.ac: Check for readline. + * README: Doc --disable-card-support and --without-readline. + + * configure.ac: Check for readline. Make enable-card-support the + default. New option --without-readline. Allow the use of either + the development or the stable libusb. 2004-07-27 Werner Koch diff --git a/NEWS b/NEWS index 5cf3d2729..0a2df5b7f 100644 --- a/NEWS +++ b/NEWS @@ -1,8 +1,13 @@ Noteworthy changes in version 1.3.6 (2004-05-22) ------------------------------------------------ - * Readline support at all prompt if the systems provides a - readline library. + * Readline support at all prompts is now available if the systems + provides a readline library. The build time option + --without-readline may be used to disable this feature. + + * Support for the OpenPGP smartcard is now enabled by default. + Use the option --disable-card-support to build without support + for smartcards. * New --keyid-format option that selects short (99242560), long (DB698D7199242560), 0xshort (0x99242560), or 0xlong diff --git a/README b/README index 22d1ad013..6a7912c22 100644 --- a/README +++ b/README @@ -476,7 +476,7 @@ auto - Compile linux, egd and unix in and automagically select at runtime. - --with-egd-socket= + --with-egd-socket= This is only used when EGD is used as random gatherer. GnuPG uses by default "~/.gnupg/entropy" as the socket to connect EGD. Using this option the @@ -485,7 +485,13 @@ "~/" uses the socket in the home directory of the user and one starting with a "=" uses a socket in the GnuPG home directory which is "~/.gnupg" by default. - + + --without-readline + Do not include support for the readline libary + even if it is available. The default is to check + whether the readline libarry is a availbale and + use it to allow fancy command line editing. + --with-included-zlib Forces usage of the local zlib sources. Default is to use the (shared) library of the system. @@ -590,6 +596,11 @@ platforms where memory is an issue, it can be set as low as 5. + --disable-card-support + Do not include smartcard support. The default is + to include support if all required libraries are + available. + Installation Problems --------------------- diff --git a/TODO b/TODO index e63f05ab6..d51ce1b84 100644 --- a/TODO +++ b/TODO @@ -72,9 +72,6 @@ * Get new assembler stuff from gmp 3.1 - * use DEL and ^H for erasing the previous character (util/ttyio.c). - or better readline. - * add test cases for invalid data (scrambled armor or other random data) * add checking of armor trailers. Try to detect garbled header diff --git a/configure.ac b/configure.ac index 9bde51804..00c18f989 100644 --- a/configure.ac +++ b/configure.ac @@ -104,10 +104,20 @@ fi AC_MSG_CHECKING([whether OpenPGP card support is requested]) AC_ARG_ENABLE(card-support, - [ --enable-card-support enable OpenPGP card support], - card_support=$enableval, card_support=no) + AC_HELP_STRING([--disable-card-support], + [enable OpenPGP card support]), + card_support=$enableval, card_support=yes) AC_MSG_RESULT($card_support) +AC_MSG_CHECKING([whether readline support is requested]) +AC_ARG_WITH(readline, + AC_HELP_STRING([--without-readline], + [do not support fancy command line editing]), + [readline_support="$withval"], [readline_support=yes]) +case "$readline_support" in yes);; no);; *)readline_support=yes;; esac +AC_MSG_RESULT($readline_support) + + dnl See if we are disabling any algorithms or features for a smaller dnl binary @@ -1072,23 +1082,24 @@ AC_SUBST(ZLIBS) # # libusb allows us to use the integrated CCID smartcard reader driver. # -# Note, that we need the CVS (unstable) version. -# LIBUSB_LIBS="" if test "$card_support" = yes; then - AC_CHECK_LIB(usb, usb_find_device, + AC_CHECK_LIB(usb, usb_bulk_write, [ LIBUSB_LIBS="$LIBUSB_LIBS -lusb" AC_DEFINE(HAVE_LIBUSB,1, [defined if libusb is available]) ]) fi AC_SUBST(LIBUSB_LIBS) +AC_CHECK_FUNCS(usb_create_match) # # Check for readline support # -AC_CHECK_LIB(readline, add_history) -AC_CHECK_HEADERS([readline/readline.h]) +if test "$readline_support" = yes ; then + AC_CHECK_LIB(readline, add_history) + AC_CHECK_HEADERS([readline/readline.h]) +fi # Allow users to append something to the version string without diff --git a/g10/ChangeLog b/g10/ChangeLog index bd01c8725..982401868 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,77 @@ +2004-09-09 Werner Koch + + * cardglue.h: Add members for CA fingerprints. + * cardglue.c (agent_release_card_info): Invalid them. + (learn_status_cb): Store them. + + * app-common.h, app-openpgp.c, iso7816.c, iso7816.h + * apdu.c, apdu.h, ccid-driver.c, ccid-driver.h + * card-util.c: Updated from current gnupg-1.9. + + Changes are: + + * ccid-driver.h (CCID_DRIVER_ERR_ABORTED): New. + * ccid-driver.c (ccid_open_reader): Support the stable 0.1 version + of libusb. + (ccid_get_atr): Handle short messages. + * apdu.c (my_rapdu_get_status): Implemented. + * apdu.c: Include . + * apdu.c (reader_table_s): Add function pointers for the backends. + (apdu_close_reader, apdu_get_status, apdu_activate) + (send_apdu): Make use of them. + (new_reader_slot): Intialize them to NULL. + (dump_ccid_reader_status, ct_dump_reader_status): New. + (dump_pcsc_reader_status): New. + (open_ct_reader, open_pcsc_reader, open_ccid_reader) + (open_osc_reader, open_rapdu_reader): Intialize function pointers. + (ct_activate_card, ct_send_apdu, pcsc_send_apdu, osc_send_apdu) + (error_string): Removed. Replaced by apdu_strerror. + (get_ccid_error_string): Removed. + (ct_activate_card): Remove the unused loop. + (reset_ct_reader): Implemented. + (ct_send_apdu): Activate the card if not yet done. + (pcsc_send_apdu): Ditto. + * ccid-driver.h: Add error codes. + * ccid-driver.c: Implement more or less proper error codes all + over the place. + * apdu.c (apdu_send_direct): New. + (get_ccid_error_string): Add some error code mappings. + (send_apdu): Pass error codes along for drivers already supporting + them. + (host_sw_string): New. + (get_ccid_error_string): Use above. + (send_apdu_ccid): Reset the reader if it has not yet been done. + (open_ccid_reader): Don't care if the ATR can't be read. + (apdu_activate_card): New. + (apdu_strerror): New. + (dump_reader_status): Only enable it with opt.VERBOSE. + * iso7816.c (map_sw): Add mappings for the new error codes. + * apdu.c (open_ct_reader, open_pcsc_reader, open_ccid_reader) + (reset_ccid_reader, open_osc_reader): Call dump_reader_status only + in verbose mode. + * app-openpgp.c (do_getattr): Fix for sending CA-FPR. + * app-openpgp.c (app_openpgp_readkey): Fixed check for valid + exponent. + * app-openpgp.c (do_setattr): Sync FORCE_CHV1. + * card-util.c (change_login): Kludge to allow reading data from a + file. + (card_edit): Pass ARG_STRING to change_login. + (card_status): Print CA fingerprints. + (change_cafpr): New. + (card_edit): New command CAFPR. + +2004-04-30 Werner Koch + + * g10.c (main) : Use gpg.conf and not /dev/null as + default filename. + +2004-04-28 Werner Koch + + * card-util.c (card_edit): Remove PIN verification. + (generate_card_keys): New arg SERIALNO. Do PIN verification here + after resetting forced_chv1. + + 2004-09-09 Werner Koch * signal.c (got_fatal_signal): Do readline cleanup. Print signal diff --git a/g10/apdu.c b/g10/apdu.c index 42b337238..5f800c983 100644 --- a/g10/apdu.c +++ b/g10/apdu.c @@ -24,6 +24,7 @@ #include #include #include +#include #ifdef USE_GNU_PTH # include # include @@ -31,8 +32,17 @@ #endif #ifdef HAVE_OPENSC # include +# ifdef USE_GNU_PTH +# undef USE_GNU_PTH +# endif #endif +/* If requested include the definitions for the remote APDU protocol + code. */ +#ifdef USE_G10CODE_RAPDU +#include "rapdu.h" +#endif /*USE_G10CODE_RAPDU*/ + #if defined(GNUPG_SCD_MAIN_HEADER) #include GNUPG_SCD_MAIN_HEADER #elif GNUPG_MAJOR_VERSION == 1 @@ -59,8 +69,6 @@ #define MAX_READER 4 /* Number of readers we support concurrently. */ -#define CARD_CONNECT_TIMEOUT 1 /* Number of seconds to wait for - insertion of the card (1 = don't wait). */ #ifdef _WIN32 @@ -81,11 +89,18 @@ struct reader_table_s { int used; /* True if slot is used. */ unsigned short port; /* Port number: 0 = unused, 1 - dev/tty */ - int is_ccid; /* Uses the internal CCID driver. */ + + /* Function pointers intialized to the various backends. */ + int (*close_reader)(int); + int (*reset_reader)(int); + int (*get_status_reader)(int, unsigned int *); + int (*send_apdu_reader)(int,unsigned char *,size_t, + unsigned char *, size_t *); + void (*dump_status_reader)(int); + struct { ccid_driver_t handle; } ccid; - int is_ctapi; /* This is a ctAPI driver. */ struct { unsigned long context; unsigned long card; @@ -97,15 +112,21 @@ struct reader_table_s { #endif /*NEED_PCSC_WRAPPER*/ } pcsc; #ifdef HAVE_OPENSC - int is_osc; /* We are using the OpenSC driver layer. */ struct { struct sc_context *ctx; struct sc_card *scard; } osc; #endif /*HAVE_OPENSC*/ +#ifdef USE_G10CODE_RAPDU + struct { + rapdu_t handle; + } rapdu; +#endif /*USE_G10CODE_RAPDU*/ int status; unsigned char atr[33]; - size_t atrlen; + size_t atrlen; /* A zero length indicates that the ATR has + not yet been read; i.e. the card is not + ready for use. */ unsigned int change_counter; #ifdef USE_GNU_PTH int lock_initialized; @@ -222,12 +243,13 @@ new_reader_slot (void) reader_table[reader].lock_initialized = 1; } #endif /*USE_GNU_PTH*/ + reader_table[reader].close_reader = NULL; + reader_table[reader].reset_reader = NULL; + reader_table[reader].get_status_reader = NULL; + reader_table[reader].send_apdu_reader = NULL; + reader_table[reader].dump_status_reader = NULL; + reader_table[reader].used = 1; - reader_table[reader].is_ccid = 0; - reader_table[reader].is_ctapi = 0; -#ifdef HAVE_OPENSC - reader_table[reader].is_osc = 0; -#endif #ifdef NEED_PCSC_WRAPPER reader_table[reader].pcsc.req_fd = -1; reader_table[reader].pcsc.rsp_fd = -1; @@ -238,34 +260,73 @@ new_reader_slot (void) static void -dump_reader_status (int reader) +dump_reader_status (int slot) { - if (reader_table[reader].is_ccid) - log_info ("reader slot %d: using ccid driver\n", reader); - else if (reader_table[reader].is_ctapi) - { - log_info ("reader slot %d: %s\n", reader, - reader_table[reader].status == 1? "Processor ICC present" : - reader_table[reader].status == 0? "Memory ICC present" : - "ICC not present" ); - } - else - { - log_info ("reader slot %d: active protocol:", reader); - if ((reader_table[reader].pcsc.protocol & PCSC_PROTOCOL_T0)) - log_printf (" T0"); - else if ((reader_table[reader].pcsc.protocol & PCSC_PROTOCOL_T1)) - log_printf (" T1"); - else if ((reader_table[reader].pcsc.protocol & PCSC_PROTOCOL_RAW)) - log_printf (" raw"); - log_printf ("\n"); - } + if (!opt.verbose) + return; - if (reader_table[reader].status != -1) + if (reader_table[slot].dump_status_reader) + reader_table[slot].dump_status_reader (slot); + + if (reader_table[slot].status != -1 + && reader_table[slot].atrlen) { - log_info ("reader %d: ATR=", reader); - log_printhex ("", reader_table[reader].atr, - reader_table[reader].atrlen); + log_info ("slot %d: ATR=", slot); + log_printhex ("", reader_table[slot].atr, reader_table[slot].atrlen); + } +} + + + +static const char * +host_sw_string (long err) +{ + switch (err) + { + case 0: return "okay"; + case SW_HOST_OUT_OF_CORE: return "out of core"; + case SW_HOST_INV_VALUE: return "invalid value"; + case SW_HOST_NO_DRIVER: return "no driver"; + case SW_HOST_NOT_SUPPORTED: return "not supported"; + case SW_HOST_LOCKING_FAILED: return "locking failed"; + case SW_HOST_BUSY: return "busy"; + case SW_HOST_NO_CARD: return "no card"; + case SW_HOST_CARD_INACTIVE: return "card inactive"; + case SW_HOST_CARD_IO_ERROR: return "card I/O error"; + case SW_HOST_GENERAL_ERROR: return "general error"; + case SW_HOST_NO_READER: return "no reader"; + case SW_HOST_ABORTED: return "aborted"; + default: return "unknown host status error"; + } +} + + +const char * +apdu_strerror (int rc) +{ + switch (rc) + { + case SW_EOF_REACHED : return "eof reached"; + case SW_EEPROM_FAILURE : return "eeprom failure"; + case SW_WRONG_LENGTH : return "wrong length"; + case SW_CHV_WRONG : return "CHV wrong"; + case SW_CHV_BLOCKED : return "CHV blocked"; + case SW_USE_CONDITIONS : return "use conditions not satisfied"; + case SW_BAD_PARAMETER : return "bad parameter"; + case SW_NOT_SUPPORTED : return "not supported"; + case SW_FILE_NOT_FOUND : return "file not found"; + case SW_RECORD_NOT_FOUND:return "record not found"; + case SW_REF_NOT_FOUND : return "reference not found"; + case SW_BAD_P0_P1 : return "bad P0 or P1"; + case SW_INS_NOT_SUP : return "instruction not supported"; + case SW_CLA_NOT_SUP : return "class not supported"; + case SW_SUCCESS : return "success"; + default: + if ((rc & ~0x00ff) == SW_MORE_DATA) + return "more data available"; + if ( (rc & 0x10000) ) + return host_sw_string (rc); + return "unknown status error"; } } @@ -290,81 +351,136 @@ ct_error_string (long err) } } -/* Wait for the card in READER and activate it. Return -1 on error or - 0 on success. */ -static int -ct_activate_card (int reader) + +static void +ct_dump_reader_status (int slot) { - int rc, count; - - for (count = 0; count < CARD_CONNECT_TIMEOUT; count++) - { - unsigned char dad[1], sad[1], cmd[11], buf[256]; - unsigned short buflen; - - if (count) - ; /* FIXME: we should use a more reliable timer than sleep. */ - - /* Check whether card has been inserted. */ - dad[0] = 1; /* Destination address: CT. */ - sad[0] = 2; /* Source address: Host. */ - - cmd[0] = 0x20; /* Class byte. */ - cmd[1] = 0x13; /* Request status. */ - cmd[2] = 0x00; /* From kernel. */ - cmd[3] = 0x80; /* Return card's DO. */ - cmd[4] = 0x00; - - buflen = DIM(buf); - - rc = CT_data (reader, dad, sad, 5, cmd, &buflen, buf); - if (rc || buflen < 2 || buf[buflen-2] != 0x90) - { - log_error ("ct_activate_card: can't get status of reader %d: %s\n", - reader, ct_error_string (rc)); - return -1; - } - - /* Connected, now activate the card. */ - dad[0] = 1; /* Destination address: CT. */ - sad[0] = 2; /* Source address: Host. */ - - cmd[0] = 0x20; /* Class byte. */ - cmd[1] = 0x12; /* Request ICC. */ - cmd[2] = 0x01; /* From first interface. */ - cmd[3] = 0x01; /* Return card's ATR. */ - cmd[4] = 0x00; - - buflen = DIM(buf); - - rc = CT_data (reader, dad, sad, 5, cmd, &buflen, buf); - if (rc || buflen < 2 || buf[buflen-2] != 0x90) - { - log_error ("ct_activate_card(%d): activation failed: %s\n", - reader, ct_error_string (rc)); - if (!rc) - log_printhex (" received data:", buf, buflen); - return -1; - } - - /* Store the type and the ATR. */ - if (buflen - 2 > DIM (reader_table[0].atr)) - { - log_error ("ct_activate_card(%d): ATR too long\n", reader); - return -1; - } - - reader_table[reader].status = buf[buflen - 1]; - memcpy (reader_table[reader].atr, buf, buflen - 2); - reader_table[reader].atrlen = buflen - 2; - return 0; - } - - log_info ("ct_activate_card(%d): timeout waiting for card\n", reader); - return -1; + log_info ("reader slot %d: %s\n", slot, + reader_table[slot].status == 1? "Processor ICC present" : + reader_table[slot].status == 0? "Memory ICC present" : + "ICC not present" ); } +/* Wait for the card in SLOT and activate it. Return a status word + error or 0 on success. */ +static int +ct_activate_card (int slot) +{ + int rc; + unsigned char dad[1], sad[1], cmd[11], buf[256]; + unsigned short buflen; + + /* Check whether card has been inserted. */ + dad[0] = 1; /* Destination address: CT. */ + sad[0] = 2; /* Source address: Host. */ + + cmd[0] = 0x20; /* Class byte. */ + cmd[1] = 0x13; /* Request status. */ + cmd[2] = 0x00; /* From kernel. */ + cmd[3] = 0x80; /* Return card's DO. */ + cmd[4] = 0x00; + + buflen = DIM(buf); + + rc = CT_data (slot, dad, sad, 5, cmd, &buflen, buf); + if (rc || buflen < 2 || buf[buflen-2] != 0x90) + { + log_error ("ct_activate_card: can't get status of reader %d: %s\n", + slot, ct_error_string (rc)); + return SW_HOST_CARD_IO_ERROR; + } + + /* Connected, now activate the card. */ + dad[0] = 1; /* Destination address: CT. */ + sad[0] = 2; /* Source address: Host. */ + + cmd[0] = 0x20; /* Class byte. */ + cmd[1] = 0x12; /* Request ICC. */ + cmd[2] = 0x01; /* From first interface. */ + cmd[3] = 0x01; /* Return card's ATR. */ + cmd[4] = 0x00; + + buflen = DIM(buf); + + rc = CT_data (slot, dad, sad, 5, cmd, &buflen, buf); + if (rc || buflen < 2 || buf[buflen-2] != 0x90) + { + log_error ("ct_activate_card(%d): activation failed: %s\n", + slot, ct_error_string (rc)); + if (!rc) + log_printhex (" received data:", buf, buflen); + return SW_HOST_CARD_IO_ERROR; + } + + /* Store the type and the ATR. */ + if (buflen - 2 > DIM (reader_table[0].atr)) + { + log_error ("ct_activate_card(%d): ATR too long\n", slot); + return SW_HOST_CARD_IO_ERROR; + } + + reader_table[slot].status = buf[buflen - 1]; + memcpy (reader_table[slot].atr, buf, buflen - 2); + reader_table[slot].atrlen = buflen - 2; + return 0; +} + + +static int +close_ct_reader (int slot) +{ + CT_close (slot); + reader_table[slot].used = 0; + return 0; +} + +static int +reset_ct_reader (int slot) +{ + /* FIXME: Check is this is sufficient do do a reset. */ + return ct_activate_card (slot); +} + + +static int +ct_get_status (int slot, unsigned int *status) +{ + *status = 1|2|4; /* FIXME */ + return 0; + + return SW_HOST_NOT_SUPPORTED; +} + +/* Actually send the APDU of length APDULEN to SLOT and return a + maximum of *BUFLEN data in BUFFER, the actual retruned size will be + set to BUFLEN. Returns: CT API error code. */ +static int +ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen, + unsigned char *buffer, size_t *buflen) +{ + int rc; + unsigned char dad[1], sad[1]; + unsigned short ctbuflen; + + /* If we don't have an ATR, we need to reset the reader first. */ + if (!reader_table[slot].atrlen + && (rc = reset_ct_reader (slot))) + return rc; + + dad[0] = 0; /* Destination address: Card. */ + sad[0] = 2; /* Source address: Host. */ + ctbuflen = *buflen; + if (DBG_CARD_IO) + log_printhex (" CT_data:", apdu, apdulen); + rc = CT_data (slot, dad, sad, apdulen, apdu, &ctbuflen, buffer); + *buflen = ctbuflen; + + return rc? SW_HOST_CARD_IO_ERROR: 0; +} + + + /* Open a reader and return an internal handle for it. PORT is a non-negative value with the port number of the reader. USB readers do have port numbers starting at 32769. */ @@ -392,64 +508,24 @@ open_ct_reader (int port) return -1; } + /* Only try to activate the card. */ rc = ct_activate_card (reader); if (rc) { - reader_table[reader].used = 0; - return -1; + reader_table[reader].atrlen = 0; + rc = 0; } - reader_table[reader].is_ctapi = 1; + reader_table[reader].close_reader = close_ct_reader; + reader_table[reader].reset_reader = reset_ct_reader; + reader_table[reader].get_status_reader = ct_get_status; + reader_table[reader].send_apdu_reader = ct_send_apdu; + reader_table[reader].dump_status_reader = ct_dump_reader_status; + dump_reader_status (reader); return reader; } -static int -close_ct_reader (int slot) -{ - CT_close (slot); - reader_table[slot].used = 0; - return 0; -} - -static int -reset_ct_reader (int slot) -{ - return SW_HOST_NOT_SUPPORTED; -} - - -static int -ct_get_status (int slot, unsigned int *status) -{ - return SW_HOST_NOT_SUPPORTED; -} - -/* Actually send the APDU of length APDULEN to SLOT and return a - maximum of *BUFLEN data in BUFFER, the actual retruned size will be - set to BUFLEN. Returns: CT API error code. */ -static int -ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen, - unsigned char *buffer, size_t *buflen) -{ - int rc; - unsigned char dad[1], sad[1]; - unsigned short ctbuflen; - - dad[0] = 0; /* Destination address: Card. */ - sad[0] = 2; /* Source address: Host. */ - ctbuflen = *buflen; - if (DBG_CARD_IO) - log_printhex (" CT_data:", apdu, apdulen); - rc = CT_data (slot, dad, sad, apdulen, apdu, &ctbuflen, buffer); - *buflen = ctbuflen; - - /* FIXME: map the errorcodes to GNUPG ones, so that they can be - shared between CTAPI and PCSC. */ - return rc; -} - - #ifdef NEED_PCSC_WRAPPER static int @@ -568,6 +644,252 @@ pcsc_error_string (long err) PC/SC Interface */ +static void +dump_pcsc_reader_status (int slot) +{ + log_info ("reader slot %d: active protocol:", slot); + if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T0)) + log_printf (" T0"); + else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1)) + log_printf (" T1"); + else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_RAW)) + log_printf (" raw"); + log_printf ("\n"); +} + + + +static int +pcsc_get_status (int slot, unsigned int *status) +{ + *status = 1|2|4; /* FIXME!!!! */ + return 0; +} + +static int +reset_pcsc_reader (int slot) +{ + return SW_HOST_NOT_SUPPORTED; +} + + +/* Actually send the APDU of length APDULEN to SLOT and return a + maximum of *BUFLEN data in BUFFER, the actual returned size will be + set to BUFLEN. Returns: CT API error code. */ +static int +pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, + unsigned char *buffer, size_t *buflen) +{ +#ifdef NEED_PCSC_WRAPPER + long err; + reader_table_t slotp; + size_t len, full_len; + int i, n; + unsigned char msgbuf[9]; + + if (!reader_table[slot].atrlen + && (err = reset_pcsc_reader (slot))) + return err; + + if (DBG_CARD_IO) + log_printhex (" PCSC_data:", apdu, apdulen); + + slotp = reader_table + slot; + + if (slotp->pcsc.req_fd == -1 + || slotp->pcsc.rsp_fd == -1 + || slotp->pcsc.pid == (pid_t)(-1) ) + { + log_error ("pcsc_send_apdu: pcsc-wrapper not running\n"); + return SW_HOST_CARD_IO_ERROR; + } + + msgbuf[0] = 0x03; /* TRANSMIT command. */ + len = apdulen; + msgbuf[1] = (len >> 24); + msgbuf[2] = (len >> 16); + msgbuf[3] = (len >> 8); + msgbuf[4] = (len ); + if ( writen (slotp->pcsc.req_fd, msgbuf, 5) + || writen (slotp->pcsc.req_fd, apdu, len)) + { + log_error ("error sending PC/SC TRANSMIT 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 TRANSMIT 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_transmit failed: %s (0x%lx)\n", + pcsc_error_string (err), err); + return SW_HOST_CARD_IO_ERROR; + } + + full_len = len; + + n = *buflen < len ? *buflen : len; + if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != n) + { + log_error ("error receiving PC/SC TRANSMIT response: %s\n", + i? strerror (errno) : "premature EOF"); + goto command_failed; + } + *buflen = n; + + full_len -= len; + if (full_len) + { + log_error ("pcsc_send_apdu: provided buffer too short - truncated\n"); + err = SW_HOST_INV_VALUE; + } + /* We need to read any rest of the response, to keep the + protocol runnng. */ + 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; + } + + return err; + + 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_io_request_s send_pci; + unsigned long recv_len; + + if (!reader_table[slot].atrlen + && (err = reset_pcsc_reader (slot))) + return err; + + if (DBG_CARD_IO) + log_printhex (" PCSC_data:", apdu, apdulen); + + if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1)) + send_pci.protocol = PCSC_PROTOCOL_T1; + else + send_pci.protocol = PCSC_PROTOCOL_T0; + send_pci.pci_len = sizeof send_pci; + recv_len = *buflen; + err = pcsc_transmit (reader_table[slot].pcsc.card, + &send_pci, apdu, apdulen, + NULL, buffer, &recv_len); + *buflen = recv_len; + if (err) + log_error ("pcsc_transmit failed: %s (0x%lx)\n", + pcsc_error_string (err), err); + + return err? SW_HOST_CARD_IO_ERROR:0; +#endif /*!NEED_PCSC_WRAPPER*/ +} + + +static int +close_pcsc_reader (int slot) +{ +#ifdef NEED_PCSC_WRAPPER + long err; + reader_table_t slotp; + size_t len; + int i; + 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 ("close_pcsc_reader: pcsc-wrapper not running\n"); + return 0; + } + + msgbuf[0] = 0x02; /* CLOSE 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 CLOSE 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 CLOSE 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_close failed: %s (0x%lx)\n", + pcsc_error_string (err), err); + + /* We will the wrapper in any case - errors are merely + informational. */ + + 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 0; + +#else /*!NEED_PCSC_WRAPPER*/ + + pcsc_release_context (reader_table[slot].pcsc.context); + reader_table[slot].used = 0; + return 0; +#endif /*!NEED_PCSC_WRAPPER*/ +} + static int open_pcsc_reader (const char *portstr) { @@ -724,6 +1046,12 @@ open_pcsc_reader (const char *portstr) } slotp->atrlen = len; + reader_table[slot].close_reader = close_pcsc_reader; + reader_table[slot].reset_reader = reset_pcsc_reader; + reader_table[slot].get_status_reader = pcsc_get_status; + reader_table[slot].send_apdu_reader = pcsc_send_apdu; + reader_table[slot].dump_status_reader = dump_pcsc_reader_status; + dump_reader_status (slot); return slot; @@ -834,6 +1162,13 @@ open_pcsc_reader (const char *portstr) 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; + reader_table[slot].get_status_reader = pcsc_get_status; + reader_table[slot].send_apdu_reader = pcsc_send_apdu; + reader_table[slot].dump_status_reader = dump_pcsc_reader_status; + /* log_debug ("state from pcsc_status: 0x%lx\n", card_state); */ /* log_debug ("protocol from pcsc_status: 0x%lx\n", card_protocol); */ @@ -843,229 +1178,6 @@ open_pcsc_reader (const char *portstr) } -static int -pcsc_get_status (int slot, unsigned int *status) -{ - return SW_HOST_NOT_SUPPORTED; -} - -/* Actually send the APDU of length APDULEN to SLOT and return a - maximum of *BUFLEN data in BUFFER, the actual returned size will be - set to BUFLEN. Returns: CT API error code. */ -static int -pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, - unsigned char *buffer, size_t *buflen) -{ -#ifdef NEED_PCSC_WRAPPER - long err; - reader_table_t slotp; - size_t len, full_len; - int i, n; - unsigned char msgbuf[9]; - - if (DBG_CARD_IO) - log_printhex (" PCSC_data:", apdu, apdulen); - - slotp = reader_table + slot; - - if (slotp->pcsc.req_fd == -1 - || slotp->pcsc.rsp_fd == -1 - || slotp->pcsc.pid == (pid_t)(-1) ) - { - log_error ("pcsc_send_apdu: pcsc-wrapper not running\n"); - return -1; - } - - msgbuf[0] = 0x03; /* TRANSMIT command. */ - len = apdulen; - msgbuf[1] = (len >> 24); - msgbuf[2] = (len >> 16); - msgbuf[3] = (len >> 8); - msgbuf[4] = (len ); - if ( writen (slotp->pcsc.req_fd, msgbuf, 5) - || writen (slotp->pcsc.req_fd, apdu, len)) - { - log_error ("error sending PC/SC TRANSMIT 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 TRANSMIT 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_transmit failed: %s (0x%lx)\n", - pcsc_error_string (err), err); - return -1; - } - - full_len = len; - - n = *buflen < len ? *buflen : len; - if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != n) - { - log_error ("error receiving PC/SC TRANSMIT response: %s\n", - i? strerror (errno) : "premature EOF"); - goto command_failed; - } - *buflen = n; - - full_len -= len; - if (full_len) - { - log_error ("pcsc_send_apdu: provided buffer too short - truncated\n"); - err = -1; - } - /* We need to read any rest of the response, to keep the - protocol runnng. */ - 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; - } - - return err; - - 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_io_request_s send_pci; - unsigned long recv_len; - - if (DBG_CARD_IO) - log_printhex (" PCSC_data:", apdu, apdulen); - - if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1)) - send_pci.protocol = PCSC_PROTOCOL_T1; - else - send_pci.protocol = PCSC_PROTOCOL_T0; - send_pci.pci_len = sizeof send_pci; - recv_len = *buflen; - err = pcsc_transmit (reader_table[slot].pcsc.card, - &send_pci, apdu, apdulen, - NULL, buffer, &recv_len); - *buflen = recv_len; - if (err) - log_error ("pcsc_transmit failed: %s (0x%lx)\n", - pcsc_error_string (err), err); - - return err? -1:0; /* FIXME: Return appropriate error code. */ -#endif /*!NEED_PCSC_WRAPPER*/ -} - - -static int -close_pcsc_reader (int slot) -{ -#ifdef NEED_PCSC_WRAPPER - long err; - reader_table_t slotp; - size_t len; - int i; - 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 ("close_pcsc_reader: pcsc-wrapper not running\n"); - return 0; - } - - msgbuf[0] = 0x02; /* CLOSE 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 CLOSE 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 CLOSE 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_close failed: %s (0x%lx)\n", - pcsc_error_string (err), err); - - /* We will the wrapper in any case - errors are merely - informational. */ - - 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 0; - -#else /*!NEED_PCSC_WRAPPER*/ - - pcsc_release_context (reader_table[slot].pcsc.context); - reader_table[slot].used = 0; - return 0; -#endif /*!NEED_PCSC_WRAPPER*/ -} - -static int -reset_pcsc_reader (int slot) -{ - return SW_HOST_NOT_SUPPORTED; -} - - - #ifdef HAVE_LIBUSB @@ -1073,46 +1185,11 @@ reset_pcsc_reader (int slot) Internal CCID driver interface. */ -static const char * -get_ccid_error_string (long err) + +static void +dump_ccid_reader_status (int slot) { - if (!err) - return "okay"; - else - return "unknown CCID error"; -} - -static int -open_ccid_reader (void) -{ - int err; - int slot; - reader_table_t slotp; - - slot = new_reader_slot (); - if (slot == -1) - return -1; - slotp = reader_table + slot; - - err = ccid_open_reader (&slotp->ccid.handle, 0); - if (err) - { - slotp->used = 0; - return -1; - } - - err = ccid_get_atr (slotp->ccid.handle, - slotp->atr, sizeof slotp->atr, &slotp->atrlen); - if (err) - { - slotp->used = 0; - return -1; - } - - slotp->is_ccid = 1; - - dump_reader_status (slot); - return slot; + log_info ("reader slot %d: using ccid driver\n", slot); } static int @@ -1134,7 +1211,7 @@ reset_ccid_reader (int slot) err = ccid_get_atr (slotp->ccid.handle, atr, sizeof atr, &atrlen); if (err) - return -1; + return err; /* If the reset was successful, update the ATR. */ assert (sizeof slotp->atr >= sizeof atr); slotp->atrlen = atrlen; @@ -1175,6 +1252,11 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen, long err; size_t maxbuflen; + /* If we don't have an ATR, we need to reset the reader first. */ + if (!reader_table[slot].atrlen + && (err = reset_ccid_reader (slot))) + return err; + if (DBG_CARD_IO) log_printhex (" APDU_data:", apdu, apdulen); @@ -1186,9 +1268,49 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen, log_error ("ccid_transceive failed: (0x%lx)\n", err); - return err? -1:0; /* FIXME: Return appropriate error code. */ + return err; } +/* Open the reader and try to read an ATR. */ +static int +open_ccid_reader (void) +{ + int err; + int slot; + reader_table_t slotp; + + slot = new_reader_slot (); + if (slot == -1) + return -1; + slotp = reader_table + slot; + + err = ccid_open_reader (&slotp->ccid.handle, 0); + if (err) + { + slotp->used = 0; + return -1; + } + + err = ccid_get_atr (slotp->ccid.handle, + slotp->atr, sizeof slotp->atr, &slotp->atrlen); + if (err) + { + slotp->atrlen = 0; + err = 0; + } + + reader_table[slot].close_reader = close_ccid_reader; + reader_table[slot].reset_reader = reset_ccid_reader; + reader_table[slot].get_status_reader = get_status_ccid; + reader_table[slot].send_apdu_reader = send_apdu_ccid; + reader_table[slot].dump_status_reader = dump_ccid_reader_status; + + dump_reader_status (slot); + return slot; +} + + + #endif /* HAVE_LIBUSB */ @@ -1202,6 +1324,117 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen, access to a card for resource conflict reasons. */ + +static int +close_osc_reader (int slot) +{ + /* FIXME: Implement. */ + reader_table[slot].used = 0; + return 0; +} + +static int +reset_osc_reader (int slot) +{ + return SW_HOST_NOT_SUPPORTED; +} + + +static int +osc_get_status (int slot, unsigned int *status) +{ + return SW_HOST_NOT_SUPPORTED; +} + + +/* Actually send the APDU of length APDULEN to SLOT and return a + maximum of *BUFLEN data in BUFFER, the actual returned size will be + set to BUFLEN. Returns: OpenSC error code. */ +static int +osc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, + unsigned char *buffer, size_t *buflen) +{ + long err; + struct sc_apdu a; + unsigned char data[SC_MAX_APDU_BUFFER_SIZE]; + unsigned char result[SC_MAX_APDU_BUFFER_SIZE]; + + if (DBG_CARD_IO) + log_printhex (" APDU_data:", apdu, apdulen); + + if (apdulen < 4) + { + log_error ("osc_send_apdu: APDU is too short\n"); + return SW_HOST_INV_VALUE; + } + + memset(&a, 0, sizeof a); + a.cla = *apdu++; + a.ins = *apdu++; + a.p1 = *apdu++; + a.p2 = *apdu++; + apdulen -= 4; + + if (!apdulen) + a.cse = SC_APDU_CASE_1; + else if (apdulen == 1) + { + a.le = *apdu? *apdu : 256; + apdu++; apdulen--; + a.cse = SC_APDU_CASE_2_SHORT; + } + else + { + a.lc = *apdu++; apdulen--; + if (apdulen < a.lc) + { + log_error ("osc_send_apdu: APDU shorter than specified in Lc\n"); + return SW_HOST_INV_VALUE; + + } + memcpy(data, apdu, a.lc); + apdu += a.lc; apdulen -= a.lc; + + a.data = data; + a.datalen = a.lc; + + if (!apdulen) + a.cse = SC_APDU_CASE_3_SHORT; + else + { + a.le = *apdu? *apdu : 256; + apdu++; apdulen--; + if (apdulen) + { + log_error ("osc_send_apdu: APDU larger than specified\n"); + return SW_HOST_INV_VALUE; + } + a.cse = SC_APDU_CASE_4_SHORT; + } + } + + a.resp = result; + a.resplen = DIM(result); + + err = sc_transmit_apdu (reader_table[slot].osc.scard, &a); + if (err) + { + log_error ("sc_apdu_transmit failed: %s\n", sc_strerror (err)); + return SW_HOST_CARD_IO_ERROR; + } + + if (*buflen < 2 || a.resplen > *buflen - 2) + { + log_error ("osc_send_apdu: provided buffer too short to store result\n"); + return SW_HOST_INV_VALUE; + } + memcpy (buffer, a.resp, a.resplen); + buffer[a.resplen] = a.sw1; + buffer[a.resplen+1] = a.sw2; + *buflen = a.resplen + 2; + return 0; +} + static int open_osc_reader (int portno) { @@ -1286,32 +1519,156 @@ open_osc_reader (int portno) slotp->atrlen = slotp->osc.scard->atr_len; memcpy (slotp->atr, slotp->osc.scard->atr, slotp->atrlen); - slotp->is_osc = 1; + reader_table[slot].close_reader = close_osc_reader; + reader_table[slot].reset_reader = reset_osc_reader; + reader_table[slot].get_status_reader = osc_get_status; + reader_table[slot].send_apdu_reader = osc_send_apdu; + reader_table[slot].dump_status_reader = NULL; dump_reader_status (slot); return slot; } +#endif /* HAVE_OPENSC */ + + + +#ifdef USE_G10CODE_RAPDU +/* + The Remote APDU Interface. + + This uses the Remote APDU protocol to contact a reader. + + The port number is actually an index into the list of ports as + returned via the protocol. + */ + static int -close_osc_reader (int slot) +rapdu_status_to_sw (int status) { - /* FIXME: Implement. */ + int rc; + + switch (status) + { + case RAPDU_STATUS_SUCCESS: rc = 0; break; + + case RAPDU_STATUS_INVCMD: + case RAPDU_STATUS_INVPROT: + case RAPDU_STATUS_INVSEQ: + case RAPDU_STATUS_INVCOOKIE: + case RAPDU_STATUS_INVREADER: rc = SW_HOST_INV_VALUE; break; + + case RAPDU_STATUS_TIMEOUT: rc = SW_HOST_CARD_IO_ERROR; break; + case RAPDU_STATUS_CARDIO: rc = SW_HOST_CARD_IO_ERROR; break; + case RAPDU_STATUS_NOCARD: rc = SW_HOST_NO_CARD; break; + case RAPDU_STATUS_CARDCHG: rc = SW_HOST_NO_CARD; break; + case RAPDU_STATUS_BUSY: rc = SW_HOST_BUSY; break; + case RAPDU_STATUS_NEEDRESET: rc = SW_HOST_CARD_INACTIVE; break; + + default: rc = SW_HOST_GENERAL_ERROR; break; + } + + return rc; +} + + + +static int +close_rapdu_reader (int slot) +{ + rapdu_release (reader_table[slot].rapdu.handle); reader_table[slot].used = 0; return 0; } + static int -reset_osc_reader (int slot) +reset_rapdu_reader (int slot) { - return SW_HOST_NOT_SUPPORTED; + int err; + reader_table_t slotp; + rapdu_msg_t msg = NULL; + + slotp = reader_table + slot; + + err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_RESET); + if (err) + { + log_error ("sending rapdu command RESET failed: %s\n", + err < 0 ? strerror (errno): rapdu_strerror (err)); + rapdu_msg_release (msg); + return rapdu_status_to_sw (err); + } + err = rapdu_read_msg (slotp->rapdu.handle, &msg); + if (err) + { + log_error ("receiving rapdu message failed: %s\n", + err < 0 ? strerror (errno): rapdu_strerror (err)); + rapdu_msg_release (msg); + return rapdu_status_to_sw (err); + } + if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen) + { + int sw = rapdu_status_to_sw (msg->cmd); + log_error ("rapdu command RESET failed: %s\n", + rapdu_strerror (msg->cmd)); + rapdu_msg_release (msg); + return sw; + } + if (msg->datalen >= DIM (slotp->atr)) + { + log_error ("ATR returned by the RAPDU layer is too large\n"); + rapdu_msg_release (msg); + return SW_HOST_INV_VALUE; + } + slotp->atrlen = msg->datalen; + memcpy (slotp->atr, msg->data, msg->datalen); + + rapdu_msg_release (msg); + return 0; } static int -ocsc_get_status (int slot, unsigned int *status) +my_rapdu_get_status (int slot, unsigned int *status) { - return SW_HOST_NOT_SUPPORTED; + int err; + reader_table_t slotp; + rapdu_msg_t msg = NULL; + int oldslot; + + slotp = reader_table + slot; + + oldslot = rapdu_set_reader (slotp->rapdu.handle, slot); + err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_GET_STATUS); + rapdu_set_reader (slotp->rapdu.handle, oldslot); + if (err) + { + log_error ("sending rapdu command GET_STATUS failed: %s\n", + err < 0 ? strerror (errno): rapdu_strerror (err)); + return rapdu_status_to_sw (err); + } + err = rapdu_read_msg (slotp->rapdu.handle, &msg); + if (err) + { + log_error ("receiving rapdu message failed: %s\n", + err < 0 ? strerror (errno): rapdu_strerror (err)); + rapdu_msg_release (msg); + return rapdu_status_to_sw (err); + } + if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen) + { + int sw = rapdu_status_to_sw (msg->cmd); + log_error ("rapdu command GET_STATUS failed: %s\n", + rapdu_strerror (msg->cmd)); + rapdu_msg_release (msg); + return sw; + } + *status = msg->data[0]; + + rapdu_msg_release (msg); + return 0; } @@ -1319,91 +1676,151 @@ ocsc_get_status (int slot, unsigned int *status) maximum of *BUFLEN data in BUFFER, the actual returned size will be set to BUFLEN. Returns: OpenSC error code. */ static int -osc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, - unsigned char *buffer, size_t *buflen) +my_rapdu_send_apdu (int slot, unsigned char *apdu, size_t apdulen, + unsigned char *buffer, size_t *buflen) { - long err; - struct sc_apdu a; - unsigned char data[SC_MAX_APDU_BUFFER_SIZE]; - unsigned char result[SC_MAX_APDU_BUFFER_SIZE]; + int err; + reader_table_t slotp; + rapdu_msg_t msg = NULL; + size_t maxlen = *buflen; + slotp = reader_table + slot; + + *buflen = 0; if (DBG_CARD_IO) log_printhex (" APDU_data:", apdu, apdulen); if (apdulen < 4) { - log_error ("osc_send_apdu: APDU is too short\n"); - return SC_ERROR_CMD_TOO_SHORT; + log_error ("rapdu_send_apdu: APDU is too short\n"); + return SW_HOST_INV_VALUE; } - memset(&a, 0, sizeof a); - a.cla = *apdu++; - a.ins = *apdu++; - a.p1 = *apdu++; - a.p2 = *apdu++; - apdulen -= 4; - - if (!apdulen) - a.cse = SC_APDU_CASE_1; - else if (apdulen == 1) - { - a.le = *apdu? *apdu : 256; - apdu++; apdulen--; - a.cse = SC_APDU_CASE_2_SHORT; - } - else - { - a.lc = *apdu++; apdulen--; - if (apdulen < a.lc) - { - log_error ("osc_send_apdu: APDU shorter than specified in Lc\n"); - return SC_ERROR_CMD_TOO_SHORT; - - } - memcpy(data, apdu, a.lc); - apdu += a.lc; apdulen -= a.lc; - - a.data = data; - a.datalen = a.lc; - - if (!apdulen) - a.cse = SC_APDU_CASE_3_SHORT; - else - { - a.le = *apdu? *apdu : 256; - apdu++; apdulen--; - if (apdulen) - { - log_error ("osc_send_apdu: APDU larger than specified\n"); - return SC_ERROR_CMD_TOO_LONG; - } - a.cse = SC_APDU_CASE_4_SHORT; - } - } - - a.resp = result; - a.resplen = DIM(result); - - err = sc_transmit_apdu (reader_table[slot].osc.scard, &a); + err = rapdu_send_apdu (slotp->rapdu.handle, apdu, apdulen); if (err) { - log_error ("sc_apdu_transmit failed: %s\n", sc_strerror (err)); - return err; + log_error ("sending rapdu command APDU failed: %s\n", + err < 0 ? strerror (errno): rapdu_strerror (err)); + rapdu_msg_release (msg); + return rapdu_status_to_sw (err); + } + err = rapdu_read_msg (slotp->rapdu.handle, &msg); + if (err) + { + log_error ("receiving rapdu message failed: %s\n", + err < 0 ? strerror (errno): rapdu_strerror (err)); + rapdu_msg_release (msg); + return rapdu_status_to_sw (err); + } + if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen) + { + int sw = rapdu_status_to_sw (msg->cmd); + log_error ("rapdu command APDU failed: %s\n", + rapdu_strerror (msg->cmd)); + rapdu_msg_release (msg); + return sw; + } + + if (msg->datalen > maxlen) + { + log_error ("rapdu response apdu too large\n"); + rapdu_msg_release (msg); + return SW_HOST_INV_VALUE; } - if (*buflen < 2 || a.resplen > *buflen - 2) - { - log_error ("osc_send_apdu: provided buffer too short to store result\n"); - return SC_ERROR_BUFFER_TOO_SMALL; - } - memcpy (buffer, a.resp, a.resplen); - buffer[a.resplen] = a.sw1; - buffer[a.resplen+1] = a.sw2; - *buflen = a.resplen + 2; + *buflen = msg->datalen; + memcpy (buffer, msg->data, msg->datalen); + + rapdu_msg_release (msg); return 0; } -#endif /* HAVE_OPENSC */ +static int +open_rapdu_reader (int portno, + const unsigned char *cookie, size_t length, + int (*readfnc) (void *opaque, + void *buffer, size_t size), + void *readfnc_value, + int (*writefnc) (void *opaque, + const void *buffer, size_t size), + void *writefnc_value, + void (*closefnc) (void *opaque), + void *closefnc_value) +{ + int err; + int slot; + reader_table_t slotp; + rapdu_msg_t msg = NULL; + + slot = new_reader_slot (); + if (slot == -1) + return -1; + slotp = reader_table + slot; + + slotp->rapdu.handle = rapdu_new (); + if (!slotp->rapdu.handle) + { + slotp->used = 0; + return -1; + } + + + rapdu_set_iofunc (slotp->rapdu.handle, + readfnc, readfnc_value, + writefnc, writefnc_value, + closefnc, closefnc_value); + rapdu_set_cookie (slotp->rapdu.handle, cookie, length); + + /* First try to get the current ATR, but if the card is inactive + issue a reset instead. */ + err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_GET_ATR); + if (err == RAPDU_STATUS_NEEDRESET) + err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_RESET); + if (err) + { + log_info ("sending rapdu command GET_ATR/RESET failed: %s\n", + err < 0 ? strerror (errno): rapdu_strerror (err)); + goto failure; + } + err = rapdu_read_msg (slotp->rapdu.handle, &msg); + if (err) + { + log_info ("receiving rapdu message failed: %s\n", + err < 0 ? strerror (errno): rapdu_strerror (err)); + goto failure; + } + if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen) + { + log_info ("rapdu command GET ATR failed: %s\n", + rapdu_strerror (msg->cmd)); + goto failure; + } + if (msg->datalen >= DIM (slotp->atr)) + { + log_error ("ATR returned by the RAPDU layer is too large\n"); + goto failure; + } + slotp->atrlen = msg->datalen; + memcpy (slotp->atr, msg->data, msg->datalen); + + reader_table[slot].close_reader = close_rapdu_reader; + reader_table[slot].reset_reader = reset_rapdu_reader; + reader_table[slot].get_status_reader = my_rapdu_get_status; + reader_table[slot].send_apdu_reader = my_rapdu_send_apdu; + reader_table[slot].dump_status_reader = NULL; + + dump_reader_status (slot); + rapdu_msg_release (msg); + return slot; + + failure: + rapdu_msg_release (msg); + rapdu_release (slotp->rapdu.handle); + slotp->used = 0; + return -1; +} + +#endif /*USE_G10CODE_RAPDU*/ @@ -1583,23 +2000,46 @@ apdu_open_reader (const char *portstr) } +/* Open an remote reader and return an internal slot number or -1 on + error. This function is an alternative to apdu_open_reader and used + with remote readers only. Note that the supplied CLOSEFNC will + only be called once and the slot will not be valid afther this. + + If PORTSTR is NULL we default to the first availabe port. +*/ +int +apdu_open_remote_reader (const char *portstr, + const unsigned char *cookie, size_t length, + int (*readfnc) (void *opaque, + void *buffer, size_t size), + void *readfnc_value, + int (*writefnc) (void *opaque, + const void *buffer, size_t size), + void *writefnc_value, + void (*closefnc) (void *opaque), + void *closefnc_value) +{ +#ifdef USE_G10CODE_RAPDU + return open_rapdu_reader (portstr? atoi (portstr) : 0, + cookie, length, + readfnc, readfnc_value, + writefnc, writefnc_value, + closefnc, closefnc_value); +#else + errno = ENOSYS; + return -1; +#endif +} + + int apdu_close_reader (int slot) { if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) return SW_HOST_NO_DRIVER; - if (reader_table[slot].is_ctapi) - return close_ct_reader (slot); -#ifdef HAVE_LIBUSB - else if (reader_table[slot].is_ccid) - return close_ccid_reader (slot); -#endif -#ifdef HAVE_OPENSC - else if (reader_table[slot].is_osc) - return close_osc_reader (slot); -#endif - else - return close_pcsc_reader (slot); + if (reader_table[slot].close_reader) + return reader_table[slot].close_reader (slot); + return SW_HOST_NOT_SUPPORTED; } /* Enumerate all readers and return information on whether this reader @@ -1626,24 +2066,53 @@ apdu_reset (int slot) if ((sw = lock_slot (slot))) return sw; - if (reader_table[slot].is_ctapi) - sw = reset_ct_reader (slot); -#ifdef HAVE_LIBUSB - else if (reader_table[slot].is_ccid) - sw = reset_ccid_reader (slot); -#endif -#ifdef HAVE_OPENSC - else if (reader_table[slot].is_osc) - sw = reset_osc_reader (slot); -#endif - else - sw = reset_pcsc_reader (slot); + if (reader_table[slot].reset_reader) + sw = reader_table[slot].reset_reader (slot); unlock_slot (slot); return sw; } +/* Activate a card if it has not yet been done. This is a kind of + reset-if-required. It is useful to test for presence of a card + before issuing a bunch of apdu commands. It does not wait on a + locked card. */ +int +apdu_activate (int slot) +{ + int sw; + unsigned int s; + + if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) + return SW_HOST_NO_DRIVER; + + if ((sw = trylock_slot (slot))) + return sw; + + if (reader_table[slot].get_status_reader) + sw = reader_table[slot].get_status_reader (slot, &s); + + if (!sw) + { + if (!(s & 2)) /* Card not present. */ + sw = SW_HOST_NO_CARD; + else if ( ((s & 2) && !(s & 4)) + || !reader_table[slot].atrlen ) + { + /* 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); + } + } + + unlock_slot (slot); + return sw; +} + + + unsigned char * apdu_get_atr (int slot, size_t *atrlen) { @@ -1660,29 +2129,9 @@ apdu_get_atr (int slot, size_t *atrlen) return buf; } - + -static const char * -error_string (int slot, long rc) -{ - if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) - return "[invalid slot]"; - if (reader_table[slot].is_ctapi) - return ct_error_string (rc); -#ifdef HAVE_LIBUSB - else if (reader_table[slot].is_ccid) - return get_ccid_error_string (rc); -#endif -#ifdef HAVE_OPENSC - else if (reader_table[slot].is_osc) - return sc_strerror (rc); -#endif - else - return pcsc_error_string (rc); -} - - -/* Retrieve the status for SLOT. The function does obnly wait fot the +/* Retrieve the status for SLOT. The function does only wait for the card to become available if HANG is set to true. On success the bits in STATUS will be set to @@ -1691,7 +2140,7 @@ error_string (int slot, long rc) bit 2 = card active bit 3 = card access locked [not yet implemented] - For must application, tetsing bit 0 is sufficient. + For must application, testing bit 0 is sufficient. CHANGED will receive the value of the counter tracking the number of card insertions. This value may be used to detect a card @@ -1710,18 +2159,8 @@ apdu_get_status (int slot, int hang, if ((sw = hang? lock_slot (slot) : trylock_slot (slot))) return sw; - if (reader_table[slot].is_ctapi) - sw = ct_get_status (slot, &s); -#ifdef HAVE_LIBUSB - else if (reader_table[slot].is_ccid) - sw = get_status_ccid (slot, &s); -#endif -#ifdef HAVE_OPENSC - else if (reader_table[slot].is_osc) - sw = osc_get_status (slot, &s); -#endif - else - sw = pcsc_get_status (slot, &s); + if (reader_table[slot].get_status_reader) + sw = reader_table[slot].get_status_reader (slot, &s); unlock_slot (slot); @@ -1744,18 +2183,13 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen, { if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) return SW_HOST_NO_DRIVER; - if (reader_table[slot].is_ctapi) - return ct_send_apdu (slot, apdu, apdulen, buffer, buflen); -#ifdef HAVE_LIBUSB - else if (reader_table[slot].is_ccid) - return send_apdu_ccid (slot, apdu, apdulen, buffer, buflen); -#endif -#ifdef HAVE_OPENSC - else if (reader_table[slot].is_osc) - return osc_send_apdu (slot, apdu, apdulen, buffer, buflen); -#endif + + if (reader_table[slot].send_apdu_reader) + return reader_table[slot].send_apdu_reader (slot, + apdu, apdulen, + buffer, buflen); else - return pcsc_send_apdu (slot, apdu, apdulen, buffer, buflen); + return SW_HOST_NOT_SUPPORTED; } /* Send an APDU to the card in SLOT. The APDU is created from all @@ -1819,9 +2253,9 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, if (rc || resultlen < 2) { log_error ("apdu_send_simple(%d) failed: %s\n", - slot, error_string (slot, rc)); + slot, apdu_strerror (rc)); unlock_slot (slot); - return SW_HOST_INCOMPLETE_CARD_RESPONSE; + return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE; } sw = (result[resultlen-2] << 8) | result[resultlen-1]; /* store away the returned data but strip the statusword. */ @@ -1886,9 +2320,9 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, if (rc || resultlen < 2) { log_error ("apdu_send_simple(%d) for get response failed: %s\n", - slot, error_string (slot, rc)); + slot, apdu_strerror (rc)); unlock_slot (slot); - return SW_HOST_INCOMPLETE_CARD_RESPONSE; + return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE; } sw = (result[resultlen-2] << 8) | result[resultlen-1]; resultlen -= 2; @@ -1977,5 +2411,180 @@ apdu_send_simple (int slot, int class, int ins, int p0, int p1, } +/* This is a more generic version of the apdu sending routine. It + takes an already formatted APDU in APDUDATA or length APDUDATALEN + and returns the with the APDU including the status word. With + HANDLE_MORE set to true this function will handle the MORE DATA + status and return all APDUs concatenated with one status word at + the end. The function does not return a regular status word but 0 + on success. If the slot is locked, the fucntion returns + immediately.*/ +int +apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen, + int handle_more, + unsigned char **retbuf, size_t *retbuflen) +{ +#define RESULTLEN 256 + unsigned char apdu[5+256+1]; + size_t apdulen; + unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in + the driver. */ + size_t resultlen; + int sw; + long rc; /* we need a long here due to PC/SC. */ + int class; + + if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) + return SW_HOST_NO_DRIVER; + + if ((sw = trylock_slot (slot))) + return sw; + + /* We simply trucntate a too long APDU. */ + if (apdudatalen > sizeof apdu) + apdudatalen = sizeof apdu; + apdulen = apdudatalen; + memcpy (apdu, apdudata, apdudatalen); + class = apdulen? *apdu : 0; + + + rc = send_apdu (slot, apdu, apdulen, result, &resultlen); + if (rc || resultlen < 2) + { + log_error ("apdu_send_direct(%d) failed: %s\n", + slot, apdu_strerror (rc)); + unlock_slot (slot); + return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE; + } + sw = (result[resultlen-2] << 8) | result[resultlen-1]; + /* Store away the returned data but strip the statusword. */ + resultlen -= 2; + if (DBG_CARD_IO) + { + log_debug (" response: sw=%04X datalen=%d\n", sw, resultlen); + if ( !retbuf && (sw == SW_SUCCESS || (sw & 0xff00) == SW_MORE_DATA)) + log_printhex (" dump: ", result, resultlen); + } + + if (handle_more && (sw & 0xff00) == SW_MORE_DATA) + { + unsigned char *p = NULL, *tmp; + size_t bufsize = 4096; + + /* It is likely that we need to return much more data, so we + start off with a large buffer. */ + if (retbuf) + { + *retbuf = p = xtrymalloc (bufsize + 2); + if (!*retbuf) + { + unlock_slot (slot); + return SW_HOST_OUT_OF_CORE; + } + assert (resultlen < bufsize); + memcpy (p, result, resultlen); + p += resultlen; + } + + do + { + int len = (sw & 0x00ff); + + if (DBG_CARD_IO) + log_debug ("apdu_send_direct(%d): %d more bytes available\n", + slot, len); + apdulen = 0; + apdu[apdulen++] = class; + apdu[apdulen++] = 0xC0; + apdu[apdulen++] = 0; + apdu[apdulen++] = 0; + apdu[apdulen++] = len; + memset (apdu+apdulen, 0, sizeof (apdu) - apdulen); + resultlen = RESULTLEN; + rc = send_apdu (slot, apdu, apdulen, result, &resultlen); + if (rc || resultlen < 2) + { + log_error ("apdu_send_direct(%d) for get response failed: %s\n", + slot, apdu_strerror (rc)); + unlock_slot (slot); + return rc ? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE; + } + sw = (result[resultlen-2] << 8) | result[resultlen-1]; + resultlen -= 2; + if (DBG_CARD_IO) + { + log_debug (" more: sw=%04X datalen=%d\n", sw, resultlen); + if (!retbuf && (sw==SW_SUCCESS || (sw&0xff00)==SW_MORE_DATA)) + log_printhex (" dump: ", result, resultlen); + } + + if ((sw & 0xff00) == SW_MORE_DATA + || sw == SW_SUCCESS + || sw == SW_EOF_REACHED ) + { + if (retbuf && resultlen) + { + if (p - *retbuf + resultlen > bufsize) + { + bufsize += resultlen > 4096? resultlen: 4096; + tmp = xtryrealloc (*retbuf, bufsize + 2); + if (!tmp) + { + unlock_slot (slot); + return SW_HOST_OUT_OF_CORE; + } + p = tmp + (p - *retbuf); + *retbuf = tmp; + } + memcpy (p, result, resultlen); + p += resultlen; + } + } + else + log_info ("apdu_send_sdirect(%d) " + "got unexpected status %04X from get response\n", + slot, sw); + } + while ((sw & 0xff00) == SW_MORE_DATA); + + if (retbuf) + { + *retbuflen = p - *retbuf; + tmp = xtryrealloc (*retbuf, *retbuflen + 2); + if (tmp) + *retbuf = tmp; + } + } + else + { + if (retbuf) + { + *retbuf = xtrymalloc ((resultlen? resultlen : 1)+2); + if (!*retbuf) + { + unlock_slot (slot); + return SW_HOST_OUT_OF_CORE; + } + *retbuflen = resultlen; + memcpy (*retbuf, result, resultlen); + } + } + + unlock_slot (slot); + + /* Append the status word - we reseved the two extra bytes while + allocating the buffer. */ + if (retbuf) + { + (*retbuf)[(*retbuflen)++] = (sw >> 8); + (*retbuf)[(*retbuflen)++] = sw; + } + + if (DBG_CARD_IO && retbuf) + log_printhex (" dump: ", *retbuf, *retbuflen); + + return 0; +#undef RESULTLEN +} diff --git a/g10/apdu.h b/g10/apdu.h index f74bab7fe..a0654a242 100644 --- a/g10/apdu.h +++ b/g10/apdu.h @@ -53,19 +53,39 @@ enum { SW_HOST_NO_DRIVER = 0x10004, SW_HOST_NOT_SUPPORTED = 0x10005, SW_HOST_LOCKING_FAILED= 0x10006, - SW_HOST_BUSY = 0x10007 + SW_HOST_BUSY = 0x10007, + SW_HOST_NO_CARD = 0x10008, + SW_HOST_CARD_INACTIVE = 0x10009, + SW_HOST_CARD_IO_ERROR = 0x1000a, + SW_HOST_GENERAL_ERROR = 0x1000b, + SW_HOST_NO_READER = 0x1000c, + SW_HOST_ABORTED = 0x1000d }; /* Note , that apdu_open_reader returns no status word but -1 on error. */ int apdu_open_reader (const char *portstr); +int apdu_open_remote_reader (const char *portstr, + const unsigned char *cookie, size_t length, + int (*readfnc) (void *opaque, + void *buffer, size_t size), + void *readfnc_value, + int (*writefnc) (void *opaque, + const void *buffer, size_t size), + void *writefnc_value, + void (*closefnc) (void *opaque), + void *closefnc_value); int apdu_close_reader (int slot); int apdu_enum_reader (int slot, int *used); unsigned char *apdu_get_atr (int slot, size_t *atrlen); +const char *apdu_strerror (int rc); -/* The apdu send functions do return status words. */ + +/* These apdu functions do return status words. */ + +int apdu_activate (int slot); int apdu_reset (int slot); int apdu_get_status (int slot, int hang, unsigned int *status, unsigned int *changed); @@ -77,6 +97,10 @@ int apdu_send (int slot, int class, int ins, int p0, int p1, int apdu_send_le (int slot, int class, int ins, int p0, int p1, int lc, const char *data, int le, unsigned char **retbuf, size_t *retbuflen); +int apdu_send_direct (int slot, + const unsigned char *apdudata, size_t apdudatalen, + int handle_more, + unsigned char **retbuf, size_t *retbuflen); #endif /*APDU_H*/ diff --git a/g10/app-openpgp.c b/g10/app-openpgp.c index f40951941..3dc015baa 100644 --- a/g10/app-openpgp.c +++ b/g10/app-openpgp.c @@ -561,7 +561,7 @@ do_getattr (APP app, CTRL ctrl, const char *name) { if (valuelen >= 60) for (i=0; i < 3; i++) - send_fpr_if_not_null (ctrl, "KEY-FPR", i+1, value+i*20); + send_fpr_if_not_null (ctrl, table[idx].name, i+1, value+i*20); } else send_status_info (ctrl, table[idx].name, value, valuelen, NULL, 0); @@ -1619,7 +1619,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen, memcpy (*m, a, alen); a = find_tlv (keydata, keydatalen, 0x0082, &alen); - if (!e) + if (!a) { log_error ("response does not contain the RSA public exponent\n"); rc = gpg_error (GPG_ERR_CARD); diff --git a/g10/card-util.c b/g10/card-util.c index c93d028bd..2d7f00800 100644 --- a/g10/card-util.c +++ b/g10/card-util.c @@ -336,6 +336,11 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen) info.chvretry[0], info.chvretry[1], info.chvretry[2]); fprintf (fp, "sigcount:%lu:::\n", info.sig_counter); + fputs ("cafpr:", fp); + print_sha1_fpr_colon (fp, info.cafpr1valid? info.cafpr1:NULL); + print_sha1_fpr_colon (fp, info.cafpr2valid? info.cafpr2:NULL); + print_sha1_fpr_colon (fp, info.cafpr3valid? info.cafpr3:NULL); + putc ('\n', fp); fputs ("fpr:", fp); print_sha1_fpr_colon (fp, info.fpr1valid? info.fpr1:NULL); print_sha1_fpr_colon (fp, info.fpr2valid? info.fpr2:NULL); @@ -362,6 +367,21 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen) info.disp_sex == 2? _("female") : _("unspecified")); print_name (fp, "URL of public key : ", info.pubkey_url); print_name (fp, "Login data .......: ", info.login_data); + if (info.cafpr1valid) + { + tty_fprintf (fp, "CA fingerprint %d .:", 1); + print_sha1_fpr (fp, info.cafpr1); + } + if (info.cafpr2valid) + { + tty_fprintf (fp, "CA fingerprint %d .:", 2); + print_sha1_fpr (fp, info.cafpr2); + } + if (info.cafpr3valid) + { + tty_fprintf (fp, "CA fingerprint %d .:", 3); + print_sha1_fpr (fp, info.cafpr3); + } tty_fprintf (fp, "Signature PIN ....: %s\n", info.chv1_cached? _("not forced"): _("forced")); tty_fprintf (fp, "Max. PIN lengths .: %d %d %d\n", @@ -491,19 +511,46 @@ change_url (void) } static int -change_login (void) +change_login (const char *args) { char *data; + int n; int rc; - data = cpr_get ("cardedit.change_login", - _("Login data (account name): ")); - if (!data) - return -1; - trim_spaces (data); - cpr_kill_prompt (); + if (args && *args == '<') /* Read it from a file */ + { + FILE *fp; - if (strlen (data) > 254 ) + for (args++; spacep (args); args++) + ; + fp = fopen (args, "rb"); + if (!fp) + { + tty_printf ("can't open `%s': %s\n", args, strerror (errno)); + return -1; + } + data = xmalloc (254); + n = fread (data, 1, 254, fp); + fclose (fp); + if (n < 0) + { + tty_printf ("error reading `%s': %s\n", args, strerror (errno)); + xfree (data); + return -1; + } + } + else + { + data = cpr_get ("cardedit.change_login", + _("Login data (account name): ")); + if (!data) + return -1; + trim_spaces (data); + cpr_kill_prompt (); + n = strlen (data); + } + + if (n > 254 ) { tty_printf (_("Error: Login data too long " "(limit is %d characters).\n"), 254); @@ -511,7 +558,7 @@ change_login (void) return -1; } - rc = agent_scd_setattr ("LOGIN-DATA", data, strlen (data) ); + rc = agent_scd_setattr ("LOGIN-DATA", data, n ); if (rc) log_error ("error setting login data: %s\n", gpg_strerror (rc)); xfree (data); @@ -590,6 +637,51 @@ change_sex (void) } +static int +change_cafpr (int fprno) +{ + char *data; + const char *s; + int i, c, rc; + unsigned char fpr[20]; + + data = cpr_get ("cardedit.change_cafpr", _("CA fingerprint: ")); + if (!data) + return -1; + trim_spaces (data); + cpr_kill_prompt (); + + for (i=0, s=data; i < 20 && *s; ) + { + while (spacep(s)) + s++; + if (*s == ':') + s++; + while (spacep(s)) + s++; + c = hextobyte (s); + if (c == -1) + break; + fpr[i++] = c; + s += 2; + } + xfree (data); + if (i != 20 || *s) + { + tty_printf (_("Error: invalid formatted fingerprint.\n")); + return -1; + } + + rc = agent_scd_setattr (fprno==1?"CA-FPR-1": + fprno==2?"CA-FPR-2": + fprno==3?"CA-FPR-3":"x", fpr, 20 ); + if (rc) + log_error ("error setting cafpr: %s\n", gpg_strerror (rc)); + return rc; +} + + + static void toggle_forcesig (void) { @@ -700,7 +792,7 @@ card_edit (STRLIST commands) enum cmdids { cmdNOP = 0, cmdQUIT, cmdHELP, cmdLIST, cmdDEBUG, - cmdNAME, cmdURL, cmdLOGIN, cmdLANG, cmdSEX, + cmdNAME, cmdURL, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR, cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdINVCMD }; @@ -722,6 +814,7 @@ card_edit (STRLIST commands) { N_("login") , cmdLOGIN , N_("change the login name") }, { N_("lang") , cmdLANG , N_("change the language preferences") }, { N_("sex") , cmdSEX , N_("change card holder's sex") }, + { N_("cafpr"), cmdCAFPR, N_("change a CA fingerprint") }, { N_("forcesig"), cmdFORCESIG, N_("toggle the signature force PIN flag") }, { N_("generate"), @@ -840,7 +933,7 @@ card_edit (STRLIST commands) break; case cmdLOGIN: - change_login (); + change_login (arg_string); break; case cmdLANG: @@ -851,6 +944,14 @@ card_edit (STRLIST commands) change_sex (); break; + case cmdCAFPR: + if ( arg_number < 1 || arg_number > 3 ) + tty_printf ("usage: cafpr N\n" + " 1 <= N <= 3\n"); + else + change_cafpr (arg_number); + break; + case cmdFORCESIG: toggle_forcesig (); break; diff --git a/g10/cardglue.c b/g10/cardglue.c index a19246211..7bb3c8488 100644 --- a/g10/cardglue.c +++ b/g10/cardglue.c @@ -1,5 +1,5 @@ /* cardglue.c - mainly dispatcher for card related functions. - * Copyright (C) 2003 Free Software Foundation, Inc. + * Copyright (C) 2003, 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -237,6 +237,7 @@ agent_release_card_info (struct agent_card_info_s *info) xfree (info->pubkey_url); info->pubkey_url = NULL; xfree (info->login_data); info->login_data = NULL; info->fpr1valid = info->fpr2valid = info->fpr3valid = 0; + info->cafpr1valid = info->cafpr2valid = info->cafpr3valid = 0; } @@ -529,7 +530,21 @@ learn_status_cb (void *opaque, const char *line) else if (no == 3) parm->fpr3valid = unhexify_fpr (line, parm->fpr3); } - + else if (keywordlen == 6 && !memcmp (keyword, "CA-FPR", keywordlen)) + { + int no = atoi (line); + while (*line && !spacep (line)) + line++; + while (spacep (line)) + line++; + if (no == 1) + parm->cafpr1valid = unhexify_fpr (line, parm->cafpr1); + else if (no == 2) + parm->cafpr2valid = unhexify_fpr (line, parm->cafpr2); + else if (no == 3) + parm->cafpr3valid = unhexify_fpr (line, parm->cafpr3); + } + return 0; } diff --git a/g10/cardglue.h b/g10/cardglue.h index f6f64bc48..764064ece 100644 --- a/g10/cardglue.h +++ b/g10/cardglue.h @@ -1,5 +1,5 @@ -/* call-agent.h - Divert operations to the agent - * Copyright (C) 2003 Free Software Foundation, Inc. +/* cardglue.h - Divert operations to the agent + * Copyright (C) 2003, 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -37,6 +37,12 @@ struct agent_card_info_s { int disp_sex; /* 0 = unspecified, 1 = male, 2 = female */ char *pubkey_url; /* malloced. */ char *login_data; /* malloced. */ + char cafpr1valid; + char cafpr2valid; + char cafpr3valid; + char cafpr1[20]; + char cafpr2[20]; + char cafpr3[20]; char fpr1valid; char fpr2valid; char fpr3valid; @@ -93,10 +99,15 @@ typedef struct ctrl_ctx_s *ctrl_t; #define GPG_ERR_NOT_IMPLEMENTED G10ERR_GENERAL #define GPG_ERR_BAD_BER G10ERR_GENERAL #define GPG_ERR_EOF (-1) +#define GPG_ERR_CARD_NOT_PRESENT G10ERR_NO_CARD +#define GPG_ERR_CARD_RESET G10ERR_GENERAL #define GPG_ERR_EBUSY G10ERR_GENERAL #define GPG_ERR_ENOENT G10ERR_OPEN_FILE #define GPG_ERR_EACCES G10ERR_UNSUPPORTED +#define GPG_ERR_EIO G10ERR_GENERAL +#define GPG_ERR_ENODEV G10ERR_GENERAL +#define GPG_ERR_CANCELED G10ERR_CANCELED typedef int gpg_error_t; typedef int gpg_err_code_t; diff --git a/g10/ccid-driver.c b/g10/ccid-driver.c index 131693820..0fc168590 100644 --- a/g10/ccid-driver.c +++ b/g10/ccid-driver.c @@ -412,7 +412,13 @@ read_device_info (ccid_driver_t handle, struct usb_device *dev) { int cfg_no; - for (cfg_no=0; cfg_no < dev->descriptor->bNumConfigurations; cfg_no++) + for (cfg_no=0; cfg_no < +#ifdef HAVE_USB_CREATE_MATCH + dev->descriptor->bNumConfigurations +#else + dev->descriptor.bNumConfigurations +#endif + ; cfg_no++) { struct usb_config_descriptor *config = dev->config + cfg_no; int ifc_no; @@ -442,17 +448,18 @@ read_device_info (ccid_driver_t handle, struct usb_device *dev) } } } - return -1; /* No suitable device found. */ + return CCID_DRIVER_ERR_NO_READER; /* No suitable device found. */ } -/* Open the reader with the internal number READERNO and return a a +/* Open the reader with the internal number READERNO and return a pointer to be used as handle in HANDLE. Returns 0 on success. */ int ccid_open_reader (ccid_driver_t *handle, int readerno) { +#ifdef HAVE_USB_CREATE_MATCH + /* This is the development version of libusb. */ static int initialized; - int rc; usb_match_handle *match = NULL; struct usb_device *dev = NULL; @@ -469,9 +476,9 @@ ccid_open_reader (ccid_driver_t *handle, int readerno) if (rc) { DEBUGOUT_1 ("usb_create_match failed: %d\n", rc); - return -1; + return CCID_DRIVER_ERR_NO_READER; } - + while (usb_find_device(match, dev, &dev) >= 0) { DEBUGOUT_3 ("%-40s %04X/%04X\n", dev->filename, @@ -482,7 +489,7 @@ ccid_open_reader (ccid_driver_t *handle, int readerno) if (!*handle) { DEBUGOUT ("out of memory\n"); - rc = -1; + rc = CCID_DRIVER_ERR_OUT_OF_CORE; free (*handle); *handle = NULL; goto leave; @@ -503,6 +510,7 @@ ccid_open_reader (ccid_driver_t *handle, int readerno) DEBUGOUT_1 ("usb_open failed: %d\n", rc); free (*handle); *handle = NULL; + rc = CCID_DRIVER_ERR_CARD_IO_ERROR; goto leave; } @@ -513,16 +521,9 @@ ccid_open_reader (ccid_driver_t *handle, int readerno) if (rc) { DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc); -#if defined(GNUPG_MAJOR_VERSION) \ - || (defined(GNUPG_SCD_MAIN_HEADER) && defined(HAVE_JNLIB_LOGGING)) - log_info ("CCID can't be used - " - "please check that the device file " - "(e.g. /proc/bus/usb) " - "has appropriate permissions\n" ); -#endif - free (*handle); *handle = NULL; + rc = CCID_DRIVER_ERR_CARD_IO_ERROR; goto leave; } @@ -536,7 +537,6 @@ ccid_open_reader (ccid_driver_t *handle, int readerno) readerno--; } - leave: if (idev) usb_close (idev); @@ -548,6 +548,96 @@ ccid_open_reader (ccid_driver_t *handle, int readerno) rc = -1; /* In case we didn't enter the while loop at all. */ return rc; +#else /* Stable 0.1 version of libusb. */ + static int initialized; + int rc = 0; + struct usb_bus *busses, *bus; + struct usb_device *dev = NULL; + usb_dev_handle *idev = NULL; + + *handle = NULL; + if (!initialized) + { + usb_init (); + initialized = 1; + } + + usb_find_busses(); + usb_find_devices(); + busses = usb_get_busses(); + + for (bus = busses; bus; bus = bus->next) + { + for (dev = bus->devices; dev; dev = dev->next) + { + DEBUGOUT_3 ("%-40s %04X/%04X\n", dev->filename, + dev->descriptor.idVendor, dev->descriptor.idProduct); + + if (!readerno) + { + *handle = calloc (1, sizeof **handle); + if (!*handle) + { + DEBUGOUT ("out of memory\n"); + rc = CCID_DRIVER_ERR_OUT_OF_CORE; + free (*handle); + *handle = NULL; + goto leave; + } + + rc = read_device_info (*handle, dev); + if (rc) + { + DEBUGOUT ("device not supported\n"); + free (*handle); + *handle = NULL; + continue; + } + + idev = usb_open (dev); + if (!idev) + { + DEBUGOUT_1 ("usb_open failed: %s\n", strerror (errno)); + free (*handle); + *handle = NULL; + rc = CCID_DRIVER_ERR_CARD_IO_ERROR; + goto leave; + } + + /* fixme: Do we need to claim and set the interface as + determined by read_device_info ()? */ + rc = usb_claim_interface (idev, 0); + if (rc) + { + DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc); + free (*handle); + *handle = NULL; + rc = CCID_DRIVER_ERR_CARD_IO_ERROR; + goto leave; + } + + (*handle)->idev = idev; + idev = NULL; + /* FIXME: Do we need to get the endpoint addresses from the + structure and store them with the handle? */ + + goto leave; /* ready. */ + } + readerno--; + } + } + + leave: + if (idev) + usb_close (idev); + /* fixme: Do we need to release dev or is it supposed to be a + shallow copy of the list created internally by usb_init ? */ + + if (!rc && !*handle) + rc = -1; /* In case we didn't enter the while loop at all. */ + + return rc; +#endif /* Stable version 0.1 of libusb. */ } @@ -613,7 +703,7 @@ bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen) DEBUGOUT_1 ("usb_bulk_write error: %s\n", strerror (errno)); else DEBUGOUT_1 ("usb_bulk_write failed: %d\n", rc); - return -1; + return CCID_DRIVER_ERR_CARD_IO_ERROR; } @@ -639,7 +729,7 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, if (rc < 0) { DEBUGOUT_1 ("usb_bulk_read error: %s\n", strerror (errno)); - return -1; + return CCID_DRIVER_ERR_CARD_IO_ERROR; } *nread = msglen = rc; @@ -647,23 +737,23 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, if (msglen < 10) { DEBUGOUT_1 ("bulk-in msg too short (%u)\n", (unsigned int)msglen); - return -1; + return CCID_DRIVER_ERR_INV_VALUE; } if (buffer[0] != expected_type) { DEBUGOUT_1 ("unexpected bulk-in msg type (%02x)\n", buffer[0]); - return -1; + return CCID_DRIVER_ERR_INV_VALUE; } if (buffer[5] != 0) { DEBUGOUT_1 ("unexpected bulk-in slot (%d)\n", buffer[5]); - return -1; + return CCID_DRIVER_ERR_INV_VALUE; } if (buffer[6] != seqno) { DEBUGOUT_2 ("bulk-in seqno does not match (%d/%d)\n", seqno, buffer[6]); - return -1; + return CCID_DRIVER_ERR_INV_VALUE; } if ( !(buffer[7] & 0x03) && (buffer[7] & 0xC0) == 0x80) @@ -680,6 +770,13 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, DEBUGOUT_CONT_1 (" %02X", buffer[i]); DEBUGOUT_LF (); + switch ((buffer[7] & 0x03)) + { + case 0: /* no error */ break; + case 1: return CCID_DRIVER_ERR_CARD_INACTIVE; + case 2: return CCID_DRIVER_ERR_NO_CARD; + case 3: /* RFU */ break; + } return 0; } @@ -703,7 +800,7 @@ ccid_poll (ccid_driver_t handle) if (rc < 0) { DEBUGOUT_1 ("usb_intr_read error: %s\n", strerror (errno)); - return -1; + return CCID_DRIVER_ERR_CARD_IO_ERROR; } msglen = rc; @@ -712,7 +809,7 @@ ccid_poll (ccid_driver_t handle) if (msglen < 1) { DEBUGOUT ("intr-in msg too short\n"); - return -1; + return CCID_DRIVER_ERR_INV_VALUE; } if (msg[0] == RDR_to_PC_NotifySlotChange) @@ -739,7 +836,8 @@ ccid_poll (ccid_driver_t handle) } - +/* Note that this fucntion won't return the error codes NO_CARD or + CARD_INACTIVE */ int ccid_slot_status (ccid_driver_t handle, int *statusbits) { @@ -760,7 +858,8 @@ ccid_slot_status (ccid_driver_t handle, int *statusbits) if (rc) return rc; rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, seqno); - if (rc) + if (rc && rc != CCID_DRIVER_ERR_NO_CARD + && rc != CCID_DRIVER_ERR_CARD_INACTIVE) return rc; *statusbits = (msg[7] & 3); @@ -891,10 +990,7 @@ ccid_get_atr (ccid_driver_t handle, tpdulen = msglen - 10; if (tpdulen < 4) - { - DEBUGOUT ("cannot yet handle short blocks!\n"); - return -1; - } + return CCID_DRIVER_ERR_ABORTED; #ifdef DEBUG_T1 fprintf (stderr, "T1: got %c-block seq=%d err=%d\n", @@ -1026,7 +1122,7 @@ ccid_transceive (ccid_driver_t handle, /* Construct an I-Block. */ if (apdulen > 254) - return -1; /* Invalid length. */ + return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */ tpdu = msg+10; /* NAD: DAD=1, SAD=0 */ @@ -1089,10 +1185,9 @@ ccid_transceive (ccid_driver_t handle, if (tpdulen < 4) { - DEBUGOUT ("cannot yet handle short blocks!\n"); - return -1; + usb_clear_halt (handle->idev, 0x82); + return CCID_DRIVER_ERR_ABORTED; } - #ifdef DEBUG_T1 fprintf (stderr, "T1: got %c-block seq=%d err=%d\n", ((msg[11] & 0xc0) == 0x80)? 'R' : @@ -1140,7 +1235,7 @@ ccid_transceive (ccid_driver_t handle, DEBUGOUT_2 ("provided buffer too short for received data " "(%u/%u)\n", (unsigned int)n, (unsigned int)maxresplen); - return -1; + return CCID_DRIVER_ERR_INV_VALUE; } memcpy (resp, p, n); @@ -1171,7 +1266,7 @@ ccid_transceive (ccid_driver_t handle, if (++retries > 3) { DEBUGOUT ("3 failed retries\n"); - return -1; + return CCID_DRIVER_ERR_CARD_IO_ERROR; } msg = send_buffer; tpdulen = last_tpdulen; @@ -1179,7 +1274,7 @@ ccid_transceive (ccid_driver_t handle, else if (sending && !!(tpdu[1] & 0x40) == handle->t1_ns) { /* Reponse does not match our sequence number. */ DEBUGOUT ("R-block with wrong seqno received on more bit\n"); - return -1; + return CCID_DRIVER_ERR_CARD_IO_ERROR; } else if (sending) { /* Send next chunk. */ @@ -1191,7 +1286,7 @@ ccid_transceive (ccid_driver_t handle, else { DEBUGOUT ("unexpected ACK R-block received\n"); - return -1; + return CCID_DRIVER_ERR_CARD_IO_ERROR; } } else @@ -1218,7 +1313,7 @@ ccid_transceive (ccid_driver_t handle, DEBUGOUT_1 ("T1 waittime extension of bwi=%d\n", bwi); } else - return -1; + return CCID_DRIVER_ERR_CARD_IO_ERROR; } } /* end T=1 protocol loop. */ diff --git a/g10/ccid-driver.h b/g10/ccid-driver.h index 8b86eb1a5..0cb52e1c3 100644 --- a/g10/ccid-driver.h +++ b/g10/ccid-driver.h @@ -55,6 +55,22 @@ #ifndef CCID_DRIVER_H #define CCID_DRIVER_H +/* The CID driver returns the same error codes as the statsu words + used by GnuPG's apdu.h. For ease of maintenance they should always + match. */ +#define CCID_DRIVER_ERR_OUT_OF_CORE 0x10001 +#define CCID_DRIVER_ERR_INV_VALUE 0x10002 +#define CCID_DRIVER_ERR_INCOMPLETE_CARD_RESPONSE = 0x10003 +#define CCID_DRIVER_ERR_NO_DRIVER 0x10004 +#define CCID_DRIVER_ERR_NOT_SUPPORTED 0x10005 +#define CCID_DRIVER_ERR_LOCKING_FAILED 0x10006 +#define CCID_DRIVER_ERR_BUSY 0x10007 +#define CCID_DRIVER_ERR_NO_CARD 0x10008 +#define CCID_DRIVER_ERR_CARD_INACTIVE 0x10009 +#define CCID_DRIVER_ERR_CARD_IO_ERROR 0x1000a +#define CCID_DRIVER_ERR_GENERAL_ERROR 0x1000b +#define CCID_DRIVER_ERR_NO_READER 0x1000c +#define CCID_DRIVER_ERR_ABORTED 0x1000d struct ccid_driver_s; typedef struct ccid_driver_s *ccid_driver_t; diff --git a/g10/iso7816.c b/g10/iso7816.c index fd3f0485c..cbb314eb2 100644 --- a/g10/iso7816.c +++ b/g10/iso7816.c @@ -85,6 +85,13 @@ map_sw (int sw) case SW_HOST_NOT_SUPPORTED: ec = GPG_ERR_NOT_SUPPORTED; break; case SW_HOST_LOCKING_FAILED: ec = GPG_ERR_BUG; break; case SW_HOST_BUSY: ec = GPG_ERR_EBUSY; break; + case SW_HOST_NO_CARD: ec = GPG_ERR_CARD_NOT_PRESENT; break; + case SW_HOST_CARD_INACTIVE: ec = GPG_ERR_CARD_RESET; break; + case SW_HOST_CARD_IO_ERROR: ec = GPG_ERR_EIO; break; + case SW_HOST_GENERAL_ERROR: ec = GPG_ERR_GENERAL; break; + case SW_HOST_NO_READER: ec = GPG_ERR_ENODEV; break; + case SW_HOST_ABORTED: ec = GPG_ERR_CANCELED; break; + default: if ((sw & 0x010000)) ec = GPG_ERR_GENERAL; /* Should not happen. */ diff --git a/include/ChangeLog b/include/ChangeLog index 19122ed72..29e3cba7a 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,7 @@ +2004-09-09 Werner Koch + + * errors.h (G10ERR_NO_CARD, G10ERR_CANCELED): New error codes. + 2004-04-27 Werner Koch * mpi.h: Renamed prototype parameter name to avoid gcc warnings. diff --git a/include/errors.h b/include/errors.h index 0dde0f9b5..38664e10c 100644 --- a/include/errors.h +++ b/include/errors.h @@ -75,7 +75,8 @@ #define G10ERR_UNU_PUBKEY 53 #define G10ERR_UNU_SECKEY 54 #define G10ERR_KEYSERVER 55 - +#define G10ERR_CANCELED 56 +#define G10ERR_NO_CARD 57 #ifndef HAVE_STRERROR char *strerror( int n ); diff --git a/po/en@boldquot.gmo b/po/en@boldquot.gmo index 9b84cd42c..0daf7f995 100644 Binary files a/po/en@boldquot.gmo and b/po/en@boldquot.gmo differ diff --git a/po/en@quot.gmo b/po/en@quot.gmo index 8dec4e75a..93099a22f 100644 Binary files a/po/en@quot.gmo and b/po/en@quot.gmo differ diff --git a/util/ChangeLog b/util/ChangeLog index fd87232ad..bc0433dbf 100644 --- a/util/ChangeLog +++ b/util/ChangeLog @@ -1,5 +1,8 @@ 2004-09-09 Werner Koch + * errors.c (g10_errstr): New error codes G10ERR_NO_CARD, + G10ERR_CANCELED. + * ttyio.c (tty_get): Add readline support. * iobuf.c (iobuf_skip_rest): New. Orginal patch by Florian diff --git a/util/errors.c b/util/errors.c index 10956a4e2..feb84fca1 100644 --- a/util/errors.c +++ b/util/errors.c @@ -105,6 +105,8 @@ g10_errstr( int err ) X(UNU_PUBKEY ,N_("unusable public key")) X(UNU_SECKEY ,N_("unusable secret key")) X(KEYSERVER ,N_("keyserver error")) + X(CANCELED ,N_("canceled")) + X(NO_CARD ,N_("no card")) default: p = buf; sprintf(buf, "g10err=%d", err); break; } #undef X