diff --git a/ChangeLog b/ChangeLog index 00acca1c4..7e172242e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2003-08-06 Werner Koch + + * configure.ac: Check for libgpg-error. Print infos about missing + libraries more nicely. + * acinclude.m4 (AM_PATH_GPG_ERROR): Added. + 2003-08-05 Werner Koch Released 1.9.0. diff --git a/NEWS b/NEWS index d275fddfc..14722fbd9 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,10 @@ Noteworthy changes in version 1.9.1 (unreleased) ------------------------------------------------ + * Support for OpenSC is back. scdaemon support a --disable-opensc to + disable OpenSC use at runtime, so that PC/SC or ct-API can still be + used directly. + Noteworthy changes in version 1.9.0 (2003-08-05) ------------------------------------------------ diff --git a/acinclude.m4 b/acinclude.m4 index 42377380b..023563422 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -636,3 +636,59 @@ AC_DEFUN(AM_PATH_OPENSC, ]) +dnl AM_PATH_GPG_ERROR([MINIMUM-VERSION, +dnl [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]]) +dnl Test for libgpg-error and define GPG_ERROR_CFLAGS and GPG_ERROR_LIBS +dnl +AC_DEFUN(AM_PATH_GPG_ERROR, +[ AC_ARG_WITH(gpg-error-prefix, + AC_HELP_STRING([--with-gpg-error-prefix=PFX], + [prefix where GPG Error is installed (optional)]), + gpg_error_config_prefix="$withval", gpg_error_config_prefix="") + if test x$gpg_error_config_prefix != x ; then + gpg_error_config_args="$gpg_error_config_args --prefix=$gpg_error_config_prefix" + if test x${GPG_ERROR_CONFIG+set} != xset ; then + GPG_ERROR_CONFIG=$gpg_error_config_prefix/bin/gpg-error-config + fi + fi + + AC_PATH_PROG(GPG_ERROR_CONFIG, gpg-error-config, no) + min_gpg_error_version=ifelse([$1], ,0.0,$1) + AC_MSG_CHECKING(for GPG Error - version >= $min_gpg_error_version) + ok=no + if test "$GPG_ERROR_CONFIG" != "no" ; then + req_major=`echo $min_gpg_error_version | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)/\1/'` + req_minor=`echo $min_gpg_error_version | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)/\2/'` + gpg_error_config_version=`$GPG_ERROR_CONFIG $gpg_error_config_args --version` + major=`echo $gpg_error_config_version | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'` + minor=`echo $gpg_error_config_version | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'` + if test "$major" -gt "$req_major"; then + ok=yes + else + if test "$major" -eq "$req_major"; then + if test "$minor" -ge "$req_minor"; then + ok=yes + fi + fi + fi + fi + if test $ok = yes; then + GPG_ERROR_CFLAGS=`$GPG_ERROR_CONFIG $gpg_error_config_args --cflags` + GPG_ERROR_LIBS=`$GPG_ERROR_CONFIG $gpg_error_config_args --libs` + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + GPG_ERROR_CFLAGS="" + GPG_ERROR_LIBS="" + AC_MSG_RESULT(no) + ifelse([$3], , :, [$3]) + fi + AC_SUBST(GPG_ERROR_CFLAGS) + AC_SUBST(GPG_ERROR_LIBS) +]) + + diff --git a/configure.ac b/configure.ac index 0d12770a9..0bf39ba75 100644 --- a/configure.ac +++ b/configure.ac @@ -27,8 +27,9 @@ AC_INIT(gnupg, 1.9.1-cvs, gnupg-devel@gnupg.org) # feel that the default check for a development version is not # sufficient. development_version=yes +NEED_GPG_ERROR_VERSION=0.2 NEED_LIBGCRYPT_VERSION=1.1.42 -NEED_LIBASSUAN_VERSION=0.0.1 +NEED_LIBASSUAN_VERSION=0.6.0 NEED_KSBA_VERSION=0.4.6 NEED_OPENSC_VERSION=0.7.0 @@ -47,7 +48,9 @@ AM_INIT_AUTOMAKE($PACKAGE, $VERSION) AC_GNU_SOURCE # Some status variables to give feedback at the end of a configure run -habe_libassuan=no +have_gpg_error=no +have_libgcrypt=no +have_libassuan=no have_ksba=no have_opensc=no have_pth=no @@ -346,59 +349,32 @@ AM_CONDITIONAL(HAVE_DOSISH_SYSTEM, test "$have_dosish_system" = yes) # Checks for libraries. # +# +# libgpg-error is a library with error codes shared between GnuPG +# related projects. +# +AM_PATH_GPG_ERROR("$NEED_GPG_ERROR_VERSION" + have_gpg_error=yes,have_gpg_error=no) + + # # Libgcrypt is our generic crypto library # -#AC_PATH_PROG(LIBGCRYPT_CONFIG, libgcrypt-config) -#if test -n "$LIBGCRYPT_CONFIG"; then -# LIBGCRYPT_CFLAGS=`$LIBGCRYPT_CONFIG --cflags` -# LIBGCRYPT_LIBS=`$LIBGCRYPT_CONFIG --libs` -#else -# AC_MSG_ERROR([[ -#*** -#*** You need libgcrypt to build this program. -#*** It should be available at the same place you -#*** got this software. -#***]]) -#fi -#AC_SUBST(LIBGCRYPT_CFLAGS) -#AC_SUBST(LIBGCRYPT_LIBS) -AM_PATH_LIBGCRYPT("$NEED_LIBGCRYPT_VERSION",, - AC_MSG_ERROR([[ -*** -*** libgcrypt was not found. You may want to get it from -*** ftp://ftp.gnupg.org/pub/gcrypt/alpha/libgcrypt/ -*** -]])) +AM_PATH_LIBGCRYPT("$NEED_LIBGCRYPT_VERSION", + have_libgcrypt=yes,have_libgcrypt=no) # # libassuan is used for IPC # AM_PATH_LIBASSUAN("$NEED_LIBASSUAN_VERSION", - have_libasssuan=yes,have_libasssun=no) -if test "$have_libassuan" = "no"; then - AC_MSG_ERROR([[ -*** -*** You need libassuan to build this program.. -*** It should be available at the same place you -*** got this software. -***]]) -fi + have_libassuan=yes,have_libassuan=no) # # libksba is our X.509 support library # AM_PATH_KSBA("$NEED_KSBA_VERSION",have_ksba=yes,have_ksba=no) -if test "$have_ksba" = "no"; then - AC_MSG_ERROR([[ -*** -*** You need libksba to build this program.. -*** It should be available at the same place you -*** got this software. -***]]) -fi # @@ -910,6 +886,64 @@ AC_SUBST(NETLIBS) AC_DEFINE(HAVE_JNLIB_LOGGING, 1, [Defined if jnlib style logging fucntions are available]) + + + +# +# Print errors here so that they are visible all +# together and the user can acquire them all together. +# +die=no +if test "$have_gpg_error" = "no"; then + die=yes + AC_MSG_NOTICE([[ +*** +*** You need libgpg-error to build this program. +** This library is for example available at +*** ftp://ftp.gnupg.org/pub/gcrypt/alpha/libgpg-error +*** (at least version $NEED_GPG_ERROR_VERSION is required.) +***]]) +fi +if test "$have_libgcrypt" = "no"; then + die=yes + AC_MSG_NOTICE([[ +*** +*** You need libgcrypt to build this program. +** This library is for example available at +*** ftp://ftp.gnupg.org/pub/gcrypt/alpha/libgcrypt/ +*** (at least version $NEED_LIBGCRYPT_VERSION is required.) +***]]) +fi +if test "$have_libassuan" = "no"; then + die=yes + AC_MSG_NOTICE([[ +*** +*** You need libassuan to build this program. +*** This library is for example available at +*** ftp://ftp.gnupg.org/pub/gcrypt/alpha/libassuan/ +*** (at least version $NEED_LIBASSUAN_VERSION is required). +***]]) +fi +if test "$have_ksba" = "no"; then + die=yes + AC_MSG_NOTICE([[ +*** +*** You need libksba to build this program. +*** This library is for example available at +*** ftp://ftp.gnupg.org/pub/gcrypt/alpha/aegypten/ +*** (at least version $NEED_KSBA_VERSION is required). +***]]) +fi + +if test "$die" = "yes"; then + AC_MSG_ERROR([[ +*** +*** Required libraries not found. Please consult the above messages +*** and install them before running configure again. +***]]) +fi + + # # Decide what to build # diff --git a/scd/ChangeLog b/scd/ChangeLog index d78c4aa16..a1cb11e0b 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,9 +1,24 @@ +2003-08-18 Werner Koch + + * Makefile.am: Add OPENSC_LIBS to all programs. + + * scdaemon.c, scdaemon.h: New option --disable-opensc. + * card.c (card_open): Implement it. + * apdu.c (open_osc_reader, osc_send_apdu): New. + (apdu_open_reader) [HAVE_OPENSC]: Use the opensc driver if not + disabled. + (error_string) [HAVE_OPENSC]: Use sc_strerror. + (send_apdu) [HAVE_OPENSC]: Call osc_apdu_send. + + * card-p15.c (p15_enum_keypairs, p15_prepare_key): Adjusted for + libgpg-error. + 2003-08-14 Timo Schulz * apdu.c (ct_activate_card): Change the code a little to avoid problems with other readers. * Always use 'dynload.h' instead of 'dlfcn.h'. - + 2003-08-05 Werner Koch * app-openpgp.c (dump_all_do): Don't analyze constructed DOs after diff --git a/scd/Makefile.am b/scd/Makefile.am index 0771beb60..5ecadd2e8 100644 --- a/scd/Makefile.am +++ b/scd/Makefile.am @@ -50,7 +50,7 @@ sc_investigate_SOURCES = \ sc_investigate_LDADD = \ ../jnlib/libjnlib.a ../common/libcommon.a \ - $(LIBGCRYPT_LIBS) @INTLLIBS@ -lgpg-error -ldl + $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) @INTLLIBS@ -lgpg-error -ldl sc_copykeys_SOURCES = \ @@ -64,7 +64,7 @@ sc_copykeys_SOURCES = \ sc_copykeys_LDADD = \ ../jnlib/libjnlib.a ../common/libcommon.a \ ../common/libsimple-pwquery.a \ - $(LIBGCRYPT_LIBS) -lgpg-error @INTLLIBS@ -ldl + $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) -lgpg-error @INTLLIBS@ -ldl diff --git a/scd/apdu.c b/scd/apdu.c index 6afcd6711..978d9ae3d 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -24,6 +24,9 @@ #include #include #include +#ifdef HAVE_OPENSC +# include +#endif #include "scdaemon.h" #include "apdu.h" @@ -34,9 +37,9 @@ insertion of the card (1 = don't wait). */ - -/* A global table to keep track of active readers. */ -static struct { +/* A structure to collect information pertaining to one reader + slot. */ +struct reader_table_s { int used; /* True if slot is used. */ unsigned short port; /* Port number: 0 = unused, 1 - dev/tty */ int is_ctapi; /* This is a ctAPI driver. */ @@ -45,10 +48,21 @@ static struct { unsigned long card; unsigned long protocol; } 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*/ int status; unsigned char atr[33]; size_t atrlen; -} reader_table[MAX_READER]; +}; +typedef struct reader_table_s *reader_table_t; + +/* A global table to keep track of active readers. */ +static struct reader_table_s reader_table[MAX_READER]; /* ct API function pointer. */ @@ -142,6 +156,7 @@ new_reader_slot (void) } reader_table[reader].used = 1; reader_table[reader].is_ctapi = 0; + reader_table[reader].is_osc = 0; return reader; } @@ -513,7 +528,7 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, unsigned long recv_len; if (DBG_CARD_IO) - log_printhex (" CT_data:", apdu, apdulen); + log_printhex (" PCSC_data:", apdu, apdulen); if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1)) send_pci.protocol = PCSC_PROTOCOL_T1; @@ -529,10 +544,199 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, log_error ("pcsc_transmit failed: %s (0x%lx)\n", pcsc_error_string (err), err); - return err? -1:0; + return err? -1:0; /* FIXME: Return appropriate error code. */ } +#ifdef HAVE_OPENSC +/* + OpenSC Interface. + + This uses the OpenSC primitives to send APDUs. We need this + because we can't mix OpenSC and native (i.e. ctAPI or PC/SC) + access to a card for resource conflict reasons. + */ + +static int +open_osc_reader (int portno) +{ + int err; + int slot; + reader_table_t slotp; + + slot = new_reader_slot (); + if (slot == -1) + return -1; + slotp = reader_table + slot; + + err = sc_establish_context (&slotp->osc.ctx, "scdaemon"); + if (err) + { + log_error ("failed to establish SC context: %s\n", sc_strerror (err)); + slotp->used = 0; + return -1; + } + if (portno < 0 || portno >= slotp->osc.ctx->reader_count) + { + log_error ("no card reader available\n"); + sc_release_context (slotp->osc.ctx); + slotp->used = 0; + return -1; + } + + /* Redirect to our logging facility. */ + slotp->osc.ctx->error_file = log_get_stream (); + slotp->osc.ctx->debug = opt.debug_sc; + slotp->osc.ctx->debug_file = log_get_stream (); + + if (sc_detect_card_presence (slotp->osc.ctx->reader[portno], 0) != 1) + { + log_error ("no card present\n"); + sc_release_context (slotp->osc.ctx); + slotp->used = 0; + return -1; + } + + /* We want the standard ISO driver. */ + /*FIXME: OpenSC does not like "iso7816", so we use EMV for now. */ + err = sc_set_card_driver(slotp->osc.ctx, "emv"); + if (err) + { + log_error ("failed to select the iso7816 driver: %s\n", + sc_strerror (err)); + sc_release_context (slotp->osc.ctx); + slotp->used = 0; + return -1; + } + + /* Now connect the card and hope that OpenSC won't try to be too + smart. */ + err = sc_connect_card (slotp->osc.ctx->reader[portno], 0, + &slotp->osc.scard); + if (err) + { + log_error ("failed to connect card in reader %d: %s\n", + portno, sc_strerror (err)); + sc_release_context (slotp->osc.ctx); + slotp->used = 0; + return -1; + } + if (opt.verbose) + log_info ("connected to card in opensc reader %d using driver `%s'\n", + portno, slotp->osc.scard->driver->name); + + err = sc_lock (slotp->osc.scard); + if (err) + { + log_error ("can't lock card in reader %d: %s\n", + portno, sc_strerror (err)); + sc_disconnect_card (slotp->osc.scard, 0); + sc_release_context (slotp->osc.ctx); + slotp->used = 0; + return -1; + } + + if (slotp->osc.scard->atr_len >= DIM (slotp->atr)) + log_bug ("ATR returned by opensc is too large\n"); + slotp->atrlen = slotp->osc.scard->atr_len; + memcpy (slotp->atr, slotp->osc.scard->atr, slotp->atrlen); + + slotp->is_osc = 1; + + dump_reader_status (slot); + return slot; +} + + +/* 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 SC_ERROR_CMD_TOO_SHORT; + } + + 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); + if (err) + { + log_error ("sc_apdu_transmit failed: %s\n", sc_strerror (err)); + return err; + } + + 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; + return 0; +} + +#endif /* HAVE_OPENSC */ @@ -542,12 +746,23 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, /* Open the reader and return an internal slot number or -1 on error. If PORTSTR is NULL we default to a suitable port (for ctAPI: - the first USB reader. For PCSC/ the first listed reader. */ + the first USB reader. For PC/SC the first listed reader). IF + OpenSC support is cmpiled in, we first try to use OpenSC. */ int apdu_open_reader (const char *portstr) { static int pcsc_api_loaded, ct_api_loaded; +#ifdef HAVE_OPENSC + if (!opt.disable_opensc) + { + int port = portstr? atoi (portstr) : 0; + + return open_osc_reader (port); + } +#endif /* HAVE_OPENSC */ + + if (opt.ctapi_driver && *opt.ctapi_driver) { int port = portstr? atoi (portstr) : 32768; @@ -648,6 +863,10 @@ error_string (int slot, long rc) return "[invalid slot]"; if (reader_table[slot].is_ctapi) return ct_error_string (rc); +#ifdef HAVE_OPENSC + else if (reader_table[slot].is_osc) + return sc_strerror (rc); +#endif else return pcsc_error_string (rc); } @@ -662,6 +881,10 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen, return SW_HOST_NO_DRIVER; if (reader_table[slot].is_ctapi) return ct_send_apdu (slot, apdu, apdulen, buffer, buflen); +#ifdef HAVE_OPENSC + else if (reader_table[slot].is_osc) + return osc_send_apdu (slot, apdu, apdulen, buffer, buflen); +#endif else return pcsc_send_apdu (slot, apdu, apdulen, buffer, buflen); } diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 11952615d..af813b57d 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -70,6 +70,7 @@ enum cmd_and_opt_values oBatch, oReaderPort, octapiDriver, + oDisableOpenSC, aTest }; @@ -94,6 +95,16 @@ static ARGPARSE_OPTS opts[] = { { oLogFile, "log-file" ,2, N_("use a log file for the server")}, { oReaderPort, "reader-port", 2, N_("|N|connect to reader at port N")}, { octapiDriver, "ctapi-driver", 2, N_("NAME|use NAME as ctAPI driver")}, + { oDisableOpenSC, "disable-opensc", 0, +#ifdef HAVE_OPENSC + N_("Do not use the OpenSC layer") +#else + "@" +#endif + /* end --disable-opensc */}, + + + {0} }; @@ -368,6 +379,7 @@ main (int argc, char **argv ) case oReaderPort: app_set_default_reader_port (pargs.r.ret_str); break; case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break; + case oDisableOpenSC: opt.disable_opensc = 1; break; default : pargs.err = configfp? 1:2; break; } diff --git a/scd/scdaemon.h b/scd/scdaemon.h index bdc4c21b3..20e2fa768 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -54,6 +54,7 @@ struct { int batch; /* batch mode */ const char *homedir; /* configuration directory name */ const char *ctapi_driver; /* Library to access the ctAPI. */ + int disable_opensc; /* Disable the sue of the OpenSC framework. */ } opt;