From 9e6c5f9a617088b3930cbd5953a2fc6acaa92833 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 29 Apr 2003 19:08:35 +0000 Subject: [PATCH] * scdaemon.c: New options --print-atr and --reader-port * apdu.c, apdu.h: New * card.c, card-p15.c, card-dinsig.c: Allow build without OpenSC. --- scd/ChangeLog | 5 + scd/Makefile.am | 14 +-- scd/apdu.c | 299 ++++++++++++++++++++++++++++++++++++++++++++++ scd/apdu.h | 29 +++++ scd/card-dinsig.c | 2 + scd/card-p15.c | 2 + scd/card.c | 17 +++ scd/scdaemon.c | 21 +++- 8 files changed, 378 insertions(+), 11 deletions(-) create mode 100644 scd/apdu.c create mode 100644 scd/apdu.h diff --git a/scd/ChangeLog b/scd/ChangeLog index 06ded1bd1..807f96888 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,5 +1,10 @@ 2003-04-29 Werner Koch + * scdaemon.c: New options --print-atr and --reader-port + * apdu.c, apdu.h: New + + * card.c, card-p15.c, card-dinsig.c: Allow build without OpenSC. + * Makefile.am (LDFLAGS): Removed. * command.c (register_commands): Adjusted for new Assuan semantics. diff --git a/scd/Makefile.am b/scd/Makefile.am index 334354237..05f774afb 100644 --- a/scd/Makefile.am +++ b/scd/Makefile.am @@ -24,18 +24,14 @@ INCLUDES = -I../intl -DLOCALEDIR=\"$(localedir)\" bin_PROGRAMS = scdaemon AM_CPPFLAGS = -I$(top_srcdir)/common $(OPENSC_CFLAGS) $(LIBGCRYPT_CFLAGS) \ - $(KSBA_CFLAGS) + $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) scdaemon_SOURCES = \ scdaemon.c scdaemon.h \ command.c card.c \ card-common.h \ - card-p15.c card-dinsig.c - -scdaemon_LDADD = ../jnlib/libjnlib.a ../assuan/libassuan.a \ - ../common/libcommon.a \ - $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) - - - + card-p15.c card-dinsig.c \ + apdu.c apdu.h +scdaemon_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \ + $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) -ldl diff --git a/scd/apdu.c b/scd/apdu.c new file mode 100644 index 000000000..8fa531926 --- /dev/null +++ b/scd/apdu.c @@ -0,0 +1,299 @@ +/* apdu.c - ISO 7816 APDU functions and low level I/O + * Copyright (C) 2003 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include + +#include "scdaemon.h" +#include "apdu.h" + +#define HAVE_CTAPI 1 + +#define MAX_READER 4 /* Number of readers we support concurrently. */ +#define CARD_CONNECT_TIMEOUT 30 /* Number of seconds to wait for + insertion of the card. */ + + + +/* A global table to keep track of active readers. */ +static struct { + int used; /* True if slot is used. */ + unsigned short port; /* port number0 = unused, 1 - dev/tty */ + int status; + unsigned char atr[33]; + size_t atrlen; +} reader_table[MAX_READER]; + + +/* ct API function pointer. */ +static char (*CT_init) (unsigned short ctn, unsigned short Pn); +static char (*CT_data) (unsigned short ctn, unsigned char *dad, + unsigned char *sad, unsigned short lc, + unsigned char *cmd, unsigned short *lr, + unsigned char *rsp); +static char (*CT_close) (unsigned short ctn); + + + + + +/* + Helper + */ + + +/* Find an unused reader slot for PORT and put it into the reader + table. Return -1 on error or the index into the reader table. */ +static int +new_reader_slot (int port) +{ + int i, reader = -1; + + if (port < 0 || port > 0xffff) + { + log_error ("new_reader_slot: invalid port %d requested\n", port); + return -1; + } + + for (i=0; i < MAX_READER; i++) + { + if (reader_table[i].used && reader_table[i].port == port) + { + log_error ("new_reader_slot: requested port %d already in use\n", + reader); + return -1; + } + else if (!reader_table[i].used && reader == -1) + reader = i; + } + if (reader == -1) + { + log_error ("new_reader_slot: out of slots\n"); + return -1; + } + reader_table[reader].used = 1; + reader_table[reader].port = port; + return reader; +} + + +static void +dump_reader_status (int reader) +{ + log_info ("reader %d: %s\n", reader, + reader_table[reader].status == 1? "Processor ICC present" : + reader_table[reader].status == 0? "Memory ICC present" : + "ICC not present" ); + + if (reader_table[reader].status != -1) + { + log_info ("reader %d: ATR=", reader); + log_printhex ("", reader_table[reader].atr, + reader_table[reader].atrlen); + } +} + + + +#ifdef HAVE_CTAPI +/* + ct API Interface + */ + +static const char * +ct_error_string (int err) +{ + switch (err) + { + case 0: return "okay"; + case -1: return "invalid data"; + case -8: return "ct error"; + case -10: return "transmission error"; + case -11: return "memory allocation error"; + case -128: return "HTSI error"; + default: return "unknown CT-API error"; + } +} + +/* Wait for the card in READER and activate it. Return -1 on error or + 0 on success. */ +static int +ct_activate_card (int reader) +{ + int rc, count; + + for (count = 0; count < CARD_CONNECT_TIMEOUT; count++) + { + 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 (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; + } + + if (buf[0] == 0x05) + { /* 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)); + 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; + } + + sleep (1); /* FIXME: we should use a more reliable timer. */ + } + + log_info ("ct_activate_card(%d): timeout waiting for card\n", reader); + return -1; +} + + +/* 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 habe port numbers starting at 32769. */ +static int +open_ct_reader (int port) +{ + int rc, reader; + + reader = new_reader_slot (port); + if (reader == -1) + return reader; + + rc = CT_init (reader, (unsigned short)port); + if (rc) + { + log_error ("apdu_open_ct_reader failed on port %d: %s\n", + port, ct_error_string (rc)); + reader_table[reader].used = 0; + return -1; + } + + rc = ct_activate_card (reader); + if (rc) + { + reader_table[reader].used = 0; + return -1; + } + + dump_reader_status (reader); + return reader; +} + + +#endif /*HAVE_CTAPI*/ + + +#ifdef HAVE_PCSC +/* + PC/SC Interface + */ + + +#endif /*HAVE_PCSC*/ + + +/* + Driver Access + */ + +/* Open the reader and return an internal slot number or -1 on + error. */ +int +apdu_open_reader (int port) +{ + static int ct_api_loaded; + + if (!ct_api_loaded) + { + void *handle; + + handle = dlopen ("libtowitoko.so", RTLD_LAZY); + if (!handle) + { + log_error ("apdu_open_reader: failed to open driver: %s", + dlerror ()); + return -1; + } + CT_init = dlsym (handle, "CT_init"); + CT_data = dlsym (handle, "CT_data"); + CT_close = dlsym (handle, "CT_close"); + if (!CT_init || !CT_data || !CT_close) + { + log_error ("apdu_open_reader: invalid driver\n"); + dlclose (handle); + return -1; + } + ct_api_loaded = 1; + } + return open_ct_reader (port); +} + + + + + + + diff --git a/scd/apdu.h b/scd/apdu.h new file mode 100644 index 000000000..fc10eed69 --- /dev/null +++ b/scd/apdu.h @@ -0,0 +1,29 @@ +/* apdu.c - ISO 7816 APDU functions and low level I/O + * Copyright (C) 2003 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifndef APDU_H +#define APDU_H + + + + + + +#endif /*APDU_H*/ diff --git a/scd/card-dinsig.c b/scd/card-dinsig.c index 161d5dbfc..a9437e47c 100644 --- a/scd/card-dinsig.c +++ b/scd/card-dinsig.c @@ -77,6 +77,7 @@ #include #include +#ifdef HAVE_OPENSC #include #include @@ -254,3 +255,4 @@ card_dinsig_bind (CARD card) card->fnc.read_cert = dinsig_read_cert; } +#endif /*HAVE_OPENSC*/ diff --git a/scd/card-p15.c b/scd/card-p15.c index 25502a610..7fa2d8fe8 100644 --- a/scd/card-p15.c +++ b/scd/card-p15.c @@ -25,6 +25,7 @@ #include #include +#ifdef HAVE_OPENSC #include #include @@ -496,3 +497,4 @@ card_p15_bind (CARD card) card->fnc.sign = p15_sign; card->fnc.decipher = p15_decipher; } +#endif /*HAVE_OPENSC*/ diff --git a/scd/card.c b/scd/card.c index 9e0f060e5..998413f7e 100644 --- a/scd/card.c +++ b/scd/card.c @@ -25,7 +25,9 @@ #include #include +#ifdef HAVE_OPENSC #include +#endif #include #include "scdaemon.h" @@ -38,12 +40,14 @@ map_sc_err (int rc) switch (rc) { case 0: rc = 0; break; +#ifdef HAVE_OPENSC case SC_ERROR_NOT_SUPPORTED: rc = GNUPG_Not_Supported; break; case SC_ERROR_PKCS15_APP_NOT_FOUND: rc = GNUPG_No_PKCS15_App; break; case SC_ERROR_OUT_OF_MEMORY: rc = GNUPG_Out_Of_Core; break; case SC_ERROR_CARD_NOT_PRESENT: rc = GNUPG_Card_Not_Present; break; case SC_ERROR_CARD_REMOVED: rc = GNUPG_Card_Removed; break; case SC_ERROR_INVALID_CARD: rc = GNUPG_Invalid_Card; break; +#endif default: rc = GNUPG_Card_Error; break; } return rc; @@ -89,6 +93,7 @@ card_help_get_keygrip (KsbaCert cert, unsigned char *array) int card_open (CARD *rcard) { +#ifdef HAVE_OPENSC CARD card; int rc; @@ -147,7 +152,11 @@ card_open (CARD *rcard) card_close (card); else *rcard = card; + return rc; +#else + return GNUPG_Not_Supported; +#endif } @@ -157,6 +166,7 @@ card_close (CARD card) { if (card) { +#ifdef HAVE_OPENSC if (card->p15card) { sc_pkcs15_unbind (card->p15card); @@ -175,6 +185,7 @@ card_close (CARD card) sc_release_context (card->ctx); card->ctx = NULL; } +#endif xfree (card); } } @@ -270,11 +281,13 @@ find_iccsn (const unsigned char *buffer, size_t length, char **serial) int card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp) { +#ifdef HAVE_OPENSC int rc; struct sc_path path; struct sc_file *file; unsigned char buf[256]; int buflen; +#endif if (!card || !serial || !stamp) return GNUPG_Invalid_Value; @@ -282,6 +295,7 @@ card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp) *serial = NULL; *stamp = 0; /* not available */ +#ifdef HAVE_OPENSC if (!card->fnc.initialized) { card->fnc.initialized = 1; @@ -389,6 +403,9 @@ card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp) } } return rc; +#else + return GNUPG_Not_Supported; +#endif } diff --git a/scd/scdaemon.c b/scd/scdaemon.c index b63b59c5b..902324ad7 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -38,8 +38,8 @@ #include #define JNLIB_NEED_LOG_LOGV +#include /* malloc hooks */ #include "scdaemon.h" -#include "../assuan/assuan.h" /* malloc hooks */ #include "i18n.h" #include "sysutils.h" @@ -68,6 +68,8 @@ enum cmd_and_opt_values oServer, oDaemon, oBatch, + oReaderPort, + oPrintATR, aTest }; @@ -90,7 +92,8 @@ static ARGPARSE_OPTS opts[] = { { oDebugSC, "debug-sc", 1, N_("|N|set OpenSC debug level to N")}, { oNoDetach, "no-detach" ,0, N_("do not detach from the console")}, { oLogFile, "log-file" ,2, N_("use a log file for the server")}, - + { oReaderPort, "reader-port", 1, N_("|N|connect to reader at port N")}, + { oPrintATR, "print-atr", 0, N_("print ATR and exit")}, {0} }; @@ -229,6 +232,10 @@ main (int argc, char **argv ) int csh_style = 0; char *logfile = NULL; int debug_wait = 0; + int reader_port = 32768; /* First USB reader. */ + int print_atr = 0; + + set_strusage (my_strusage); gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); @@ -363,6 +370,9 @@ main (int argc, char **argv ) case oServer: pipe_server = 1; break; case oDaemon: is_daemon = 1; break; + case oReaderPort: reader_port = pargs.r.ret_int; break; + case oPrintATR: print_atr = 1; break; + default : pargs.err = configfp? 1:2; break; } } @@ -399,6 +409,13 @@ main (int argc, char **argv ) exit (1); } + + if (print_atr) + { + apdu_open_reader (reader_port); + scd_exit (0); + } + if (debug_wait && pipe_server) { log_debug ("waiting for debugger - my pid is %u .....\n",