diff --git a/AUTHORS b/AUTHORS index a37d17a90..6fb1fa8fd 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,5 +1,6 @@ Program: GnuPG -Maintainer: Werner Koch +Homepage: http://www.gnupg.org +Maintainer: Werner Koch Bug reports: Security related bug reports: License: GPLv3+ diff --git a/g10/ChangeLog b/g10/ChangeLog index a17c809e9..f7d7a62ee 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,7 @@ +2009-01-26 Werner Koch + + * card-util.c (card_status): Detect a Geldkarte. + 2009-01-13 Werner Koch * call-agent.c (dummy_data_cb): New. diff --git a/g10/card-util.c b/g10/card-util.c index 24a7b094b..1d263669f 100644 --- a/g10/card-util.c +++ b/g10/card-util.c @@ -387,6 +387,12 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen) fputs ("pkcs15-card:\n", fp); log_info ("this is a PKCS#15 compliant card\n"); } + else if (info.apptype && !strcmp (info.apptype, "GELDKARTE")) + { + if (opt.with_colons) + fputs ("geldkarte-card:\n", fp); + log_info ("this is a Geldkarte compliant card\n"); + } else { if (opt.with_colons) diff --git a/jnlib/ChangeLog b/jnlib/ChangeLog index 4a420744e..651ed799f 100644 --- a/jnlib/ChangeLog +++ b/jnlib/ChangeLog @@ -1,3 +1,8 @@ +2009-01-22 Werner Koch + + * t-support.c (gpg_err_code_from_errno) + (gpg_err_code_from_syserror): New. + 2008-11-20 Werner Koch * argparse.c (arg_parse): Fix last change. diff --git a/jnlib/t-support.c b/jnlib/t-support.c index 9b84634f8..126104846 100644 --- a/jnlib/t-support.c +++ b/jnlib/t-support.c @@ -22,9 +22,11 @@ #include #include #include +#include #include "t-support.h" + /* Replacements for the malloc functions as used here. */ static void @@ -107,3 +109,31 @@ gcry_free (void *a) if (a) free (a); } + + + +/* Stubs for gpg-error functions required because some compilers do + not eliminate the supposed-to-be-unused-inline-functions and thus + require functions called from these inline fucntions. Although we + do not use gpg-error, gpg-error.h may get included via gcrypt.h if + it happens to be used used in libjnlib-config.h. */ +int +gpg_err_code_from_errno (int err) +{ + assert (!"stub function"); + return -1; +} + + +/* Retrieve the error code directly from the ERRNO variable. This + returns GPG_ERR_UNKNOWN_ERRNO if the system error is not mapped + (report this) and GPG_ERR_MISSING_ERRNO if ERRNO has the value 0. */ +int +gpg_err_code_from_syserror (void) +{ + assert (!"stub function"); + return -1; +} + + + diff --git a/keyserver/ChangeLog b/keyserver/ChangeLog index 0530d36f8..d9f513620 100644 --- a/keyserver/ChangeLog +++ b/keyserver/ChangeLog @@ -1,3 +1,8 @@ +2009-01-22 Werner Koch + + * Makefile.am (gpg2keys_curl_LDADD, gpg2keys_hkp_LDADD): Add all + standard libs. + 2008-10-20 Werner Koch * curl-shim.c (curl_global_init): Mark usused arg. diff --git a/keyserver/Makefile.am b/keyserver/Makefile.am index b25c294cb..e97fe301a 100644 --- a/keyserver/Makefile.am +++ b/keyserver/Makefile.am @@ -1,4 +1,6 @@ -# Copyright (C) 2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc. +# Makefile.am - Makefile for keyservers +# Copyright (C) 2001, 2002, 2004, 2005, 2006, +# 2009 Free Software Foundation, Inc. # # This file is part of GnuPG. # @@ -66,14 +68,17 @@ gpg2keys_hkp_CPPFLAGS = $(AM_CPPFLAGS) gpg2keys_hkp_LDADD = $(common_libs) $(GPG_ERROR_LIBS) $(NETLIBS) $(DNSLIBS) \ $(other_libs) else +# Note that we need to include all other libs here as well because +# some compilers don't care about inline fucntions and insert +# references to symbols used in unused inline functions. gpg2keys_curl_CPPFLAGS = $(LIBCURL_CPPFLAGS) $(AM_CPPFLAGS) -gpg2keys_curl_LDADD = $(LIBCURL) $(GETOPT) +gpg2keys_curl_LDADD = $(common_libs) $(GPG_ERROR_LIBS) $(NETLIBS) $(DNSLIBS) \ + $(other_libs) $(LIBCURL) $(GETOPT) gpg2keys_hkp_CPPFLAGS = $(LIBCURL_CPPFLAGS) $(AM_CPPFLAGS) -gpg2keys_hkp_LDADD = $(LIBCURL) $(GETOPT) +gpg2keys_hkp_LDADD = $(common_libs) $(GPG_ERROR_LIBS) $(NETLIBS) $(DNSLIBS) \ + $(other_libs) $(LIBCURL) $(GETOPT) endif # Make sure that all libs are build before we use them. This is # important for things like make -j2. $(PROGRAMS): $(common_libs) - - diff --git a/scd/ChangeLog b/scd/ChangeLog index b42c569f1..755019693 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,14 @@ +2009-01-27 Werner Koch + + * app.c (app_munge_serialno): Add case for no serialno. + (app_get_serial_and_stamp): Ditto. + +2009-01-26 Werner Koch + + * app-geldkarte.c: New. + * Makefile.am (card_apps): Add new file. + * app.c (select_application): Test for geldkarte. + 2009-01-12 Werner Koch * command.c (send_client_notifications) [HAVE_W32_SYSTEM]: Fix diff --git a/scd/Makefile.am b/scd/Makefile.am index e9da5b492..86dbff897 100644 --- a/scd/Makefile.am +++ b/scd/Makefile.am @@ -30,7 +30,7 @@ AM_CFLAGS = $(LIBGCRYPT_CFLAGS) \ $(KSBA_CFLAGS) $(LIBASSUAN_PTH_CFLAGS) $(PTH_CFLAGS) -card_apps = app-openpgp.c app-nks.c app-dinsig.c app-p15.c +card_apps = app-openpgp.c app-nks.c app-dinsig.c app-p15.c app-geldkarte.c scdaemon_SOURCES = \ scdaemon.c scdaemon.h \ diff --git a/scd/app-common.h b/scd/app-common.h index fe98bf832..5a45fa05b 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -207,6 +207,9 @@ gpg_error_t app_select_dinsig (app_t app); /*-- app-p15.c --*/ gpg_error_t app_select_p15 (app_t app); +/*-- app-geldkarte.c --*/ +gpg_error_t app_select_geldkarte (app_t app); + #endif diff --git a/scd/app-geldkarte.c b/scd/app-geldkarte.c new file mode 100644 index 000000000..3db42bf95 --- /dev/null +++ b/scd/app-geldkarte.c @@ -0,0 +1,337 @@ +/* app-geldkarte.c - The German Geldkarte application + * Copyright (C) 2004 g10 Code GmbH + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + + +/* This is a read-only application to quickly dump information of a + German Geldkarte (debit card for small amounts). + + Because this application does no use an AID it is best to test for + it after the test for other applications. +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scdaemon.h" + +#include "i18n.h" +#include "iso7816.h" +#include "app-common.h" +#include "tlv.h" + + + +/* Object with application (i.e. Geldkarte) specific data. */ +struct app_local_s +{ + char kblz[2+1+4+1]; + const char *banktype; + char *cardno; + char expires[7+1]; + char validfrom[10+1]; + char *country; + char currency[3+1]; + unsigned int currency_mult100; + unsigned char chipid; + unsigned char osvers; +}; + + + + +/* Deconstructor. */ +static void +do_deinit (app_t app) +{ + if (app && app->app_local) + { + xfree (app->app_local->cardno); + xfree (app->app_local->country); + xfree (app->app_local); + app->app_local = NULL; + } +} + + +static gpg_error_t +send_one_string (ctrl_t ctrl, const char *name, const char *string) +{ + if (!name || !string) + return 0; + send_status_info (ctrl, name, string, strlen (string), NULL, 0); + return 0; +} + +/* Implement the GETATTR command. This is similar to the LEARN + command but returns just one value via the status interface. */ +static gpg_error_t +do_getattr (app_t app, ctrl_t ctrl, const char *name) +{ + gpg_error_t err; + struct app_local_s *ld = app->app_local; + char numbuf[100]; + + if (!strcmp (name, "X-KBLZ")) + err = send_one_string (ctrl, name, ld->kblz); + else if (!strcmp (name, "X-BANKINFO")) + err = send_one_string (ctrl, name, ld->banktype); + else if (!strcmp (name, "X-CARDNO")) + err = send_one_string (ctrl, name, ld->cardno); + else if (!strcmp (name, "X-EXPIRES")) + err = send_one_string (ctrl, name, ld->expires); + else if (!strcmp (name, "X-VALIDFROM")) + err = send_one_string (ctrl, name, ld->validfrom); + else if (!strcmp (name, "X-COUNTRY")) + err = send_one_string (ctrl, name, ld->country); + else if (!strcmp (name, "X-CURRENCY")) + err = send_one_string (ctrl, name, ld->currency); + else if (!strcmp (name, "X-CRNCMULT")) + { + snprintf (numbuf, sizeof numbuf, "%u", ld->currency_mult100); + err = send_one_string (ctrl, name, numbuf); + } + else if (!strcmp (name, "X-ZKACHIPID")) + { + snprintf (numbuf, sizeof numbuf, "0x%02X", ld->chipid); + err = send_one_string (ctrl, name, numbuf); + } + else if (!strcmp (name, "X-OSVERSION")) + { + snprintf (numbuf, sizeof numbuf, "0x%02X", ld->osvers); + err = send_one_string (ctrl, name, numbuf); + } + else + err = gpg_error (GPG_ERR_INV_NAME); + + return err; +} + + +static gpg_error_t +do_learn_status (app_t app, ctrl_t ctrl) +{ + static const char *names[] = { + "X-KBLZ", + "X-BANKINFO", + "X-CARDNO", + "X-EXPIRES", + "X-VALIDFROM", + "X-COUNTRY", + "X-CURRENCY", + "X-CRNCMULT", + "X-ZKACHIPID", + "X-OSVERSION", + NULL + }; + gpg_error_t err = 0; + int idx; + + for (idx=0; names[idx] && !err; idx++) + err = do_getattr (app, ctrl, names[idx]); + return err; +} + + +static char * +copy_bcd (const unsigned char *string, size_t length) +{ + const unsigned char *s; + size_t n; + size_t needed; + char *buffer, *dst; + + if (!length) + return xtrystrdup (""); + + /* Skip leading zeroes. */ + for (; length && !*string; length--, string++) + ; + s = string; + n = length; + needed = 0; + for (; n ; n--, s++) + { + if (!needed && !(*s & 0xf0)) + ; /* Skip the leading zero in the first nibble. */ + else + { + if ( ((*s >> 4) & 0x0f) > 9 ) + { + errno = EINVAL; + return NULL; + } + needed++; + } + if ( n == 1 && (*s & 0x0f) > 9 ) + ; /* Ignore the last digit if it has the sign. */ + else + { + needed++; + if ( (*s & 0x0f) > 9 ) + { + errno = EINVAL; + return NULL; + } + } + + } + if (!needed) /* If it is all zero, print a "0". */ + needed++; + + buffer = dst = xtrymalloc (needed+1); + if (!buffer) + return NULL; + + s = string; + n = length; + needed = 0; + for (; n ; n--, s++) + { + if (!needed && !(*s & 0xf0)) + ; /* Skip the leading zero in the first nibble. */ + else + { + *dst++ = '0' + ((*s >> 4) & 0x0f); + needed++; + } + + if ( n == 1 && (*s & 0x0f) > 9 ) + ; /* Ignore the last digit if it has the sign. */ + else + { + *dst++ = '0' + (*s & 0x0f); + needed++; + } + } + if (!needed) + *dst++ = '0'; + *dst = 0; + + return buffer; +} + + + + +/* Select the Geldkarte application. */ +gpg_error_t +app_select_geldkarte (app_t app) +{ + gpg_error_t err; + int slot = app->slot; + unsigned char *result = NULL; + size_t resultlen; + struct app_local_s *ld; + const char *banktype; + + err = iso7816_select_file (slot, 0x3f00, 1, NULL, NULL); + if (err) + goto leave; /* Oops. */ + + /* Read short EF 0xbc. We require this record to be at least 24 + bytes with the the first byte 0x67 and a correct the filler + byte. */ + err = iso7816_read_record (slot, 1, 1, 0xbc, &result, &resultlen); + if (err) + goto leave; /* No such record or other error - not a Geldkarte. */ + if (resultlen < 24 || *result != 0x67 || result[22]) + { + err = gpg_error (GPG_ERR_NOT_FOUND); + goto leave; + } + + /* The short Bankleitzahl consists of 3 bytes at offset 1. */ + switch (result[1]) + { + case 0x21: banktype = "Oeffentlich-rechtliche oder private Bank"; break; + case 0x22: banktype = "Privat- oder Geschaeftsbank"; break; + case 0x25: banktype = "Sparkasse"; break; + case 0x26: + case 0x29: banktype = "Genossenschaftsbank"; break; + default: + err = gpg_error (GPG_ERR_NOT_FOUND); + goto leave; /* Probably not a Geldkarte. */ + } + + app->apptype = "GELDKARTE"; + app->fnc.deinit = do_deinit; + + app->app_local = ld = xtrycalloc (1, sizeof *app->app_local); + if (!app->app_local) + { + err = gpg_err_code_from_syserror (); + goto leave; + } + + snprintf (ld->kblz, sizeof ld->kblz, "%02X-%02X%02X", + result[1], result[2], result[3]); + ld->banktype = banktype; + ld->cardno = copy_bcd (result+4, 5); + if (!ld->cardno) + { + err = gpg_err_code_from_syserror (); + goto leave; + } + + snprintf (ld->expires, sizeof ld->expires, "20%02X-%02X", + result[10], result[11]); + snprintf (ld->validfrom, sizeof ld->validfrom, "20%02X-%02X-%02X", + result[12], result[13], result[14]); + + ld->country = copy_bcd (result+15, 2); + if (!ld->country) + { + err = gpg_err_code_from_syserror (); + goto leave; + } + + snprintf (ld->currency, sizeof ld->currency, "%c%c%c", + isascii (result[17])? result[17]:' ', + isascii (result[18])? result[18]:' ', + isascii (result[19])? result[19]:' '); + + ld->currency_mult100 = (result[20] == 0x01? 1: + result[20] == 0x02? 10: + result[20] == 0x04? 100: + result[20] == 0x08? 1000: + result[20] == 0x10? 10000: + result[20] == 0x20? 100000:0); + + ld->chipid = result[21]; + ld->osvers = result[23]; + + /* Setup the rest of the methods. */ + app->fnc.learn_status = do_learn_status; + app->fnc.getattr = do_getattr; + + + leave: + xfree (result); + if (err) + do_deinit (app); + return err; +} diff --git a/scd/app.c b/scd/app.c index 731f98326..4034fa64c 100644 --- a/scd/app.c +++ b/scd/app.c @@ -352,6 +352,9 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app) err = app_select_p15 (app); if (err && is_app_allowed ("dinsig") && (!name || !strcmp (name, "dinsig"))) err = app_select_dinsig (app); + if (err && is_app_allowed ("geldkarte") + && (!name || !strcmp (name, "geldkarte"))) + err = app_select_geldkarte (app); if (err && name) err = gpg_error (GPG_ERR_NOT_SUPPORTED); @@ -440,6 +443,7 @@ release_application (app_t app) FF 00 00 = For serial numbers starting with an FF FF 01 00 = Some german p15 cards return an empty serial number so the serial number from the EF(TokenInfo) is used instead. + FF 7F 00 = No serialno. All other serial number not starting with FF are used as they are. */ @@ -452,13 +456,23 @@ app_munge_serialno (app_t app) requires that we put our default prefix "FF0000" in front. */ unsigned char *p = xtrymalloc (app->serialnolen + 3); if (!p) - return gpg_error (gpg_err_code_from_errno (errno)); + return gpg_error_from_syserror (); memcpy (p, "\xff\0", 3); memcpy (p+3, app->serialno, app->serialnolen); app->serialnolen += 3; xfree (app->serialno); app->serialno = p; } + else if (!app->serialnolen) + { + unsigned char *p = xtrymalloc (3); + if (!p) + return gpg_error_from_syserror (); + memcpy (p, "\xff\x7f", 3); + app->serialnolen = 3; + xfree (app->serialno); + app->serialno = p; + } return 0; } @@ -482,7 +496,10 @@ app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp) if (stamp) *stamp = 0; /* not available */ - buf = bin2hex (app->serialno, app->serialnolen, NULL); + if (!app->serialnolen) + buf = xtrystrdup ("FF7F00"); + else + buf = bin2hex (app->serialno, app->serialnolen, NULL); if (!buf) return gpg_error_from_syserror ();