From 82a17c9fb3d64ccdd474c3bedf564368f77e84a4 Mon Sep 17 00:00:00 2001 From: Repo Admin Date: Sat, 19 Oct 2002 07:55:27 +0000 Subject: This commit was manufactured by cvs2svn to create branch 'GNUPG-1-9-BRANCH'. --- scd/scdaemon.c | 633 --------------------------------------------------------- 1 file changed, 633 deletions(-) delete mode 100644 scd/scdaemon.c (limited to 'scd/scdaemon.c') diff --git a/scd/scdaemon.c b/scd/scdaemon.c deleted file mode 100644 index b63b59c5b..000000000 --- a/scd/scdaemon.c +++ /dev/null @@ -1,633 +0,0 @@ -/* scdaemon.c - The GnuPG Smartcard Daemon - * Copyright (C) 2001, 2002 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 -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define JNLIB_NEED_LOG_LOGV -#include "scdaemon.h" -#include "../assuan/assuan.h" /* malloc hooks */ - -#include "i18n.h" -#include "sysutils.h" - - - -enum cmd_and_opt_values -{ aNull = 0, - oCsh = 'c', - oQuiet = 'q', - oSh = 's', - oVerbose = 'v', - - oNoVerbose = 500, - oOptions, - oDebug, - oDebugAll, - oDebugWait, - oDebugSC, - oNoGreeting, - oNoOptions, - oHomedir, - oNoDetach, - oNoGrab, - oLogFile, - oServer, - oDaemon, - oBatch, - -aTest }; - - - -static ARGPARSE_OPTS opts[] = { - - { 301, NULL, 0, N_("@Options:\n ") }, - - { oServer, "server", 0, N_("run in server mode (foreground)") }, - { oDaemon, "daemon", 0, N_("run in daemon mode (background)") }, - { oVerbose, "verbose", 0, N_("verbose") }, - { oQuiet, "quiet", 0, N_("be somewhat more quiet") }, - { oSh, "sh", 0, N_("sh-style command output") }, - { oCsh, "csh", 0, N_("csh-style command output") }, - { oOptions, "options" , 2, N_("read options from file")}, - { oDebug, "debug" ,4|16, N_("set debugging flags")}, - { oDebugAll, "debug-all" ,0, N_("enable full debugging")}, - { oDebugWait,"debug-wait",1, "@"}, - { 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")}, - - - {0} -}; - - -static volatile int caught_fatal_sig = 0; - -/* It is possible that we are currently running under setuid permissions */ -static int maybe_setuid = 1; - -/* Name of the communication socket */ -static char socket_name[128]; - -static const char * -my_strusage (int level) -{ - const char *p; - switch (level) - { - case 11: p = "scdaemon (GnuPG)"; - break; - case 13: p = VERSION; break; - case 17: p = PRINTABLE_OS_NAME; break; - case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n"); - break; - case 1: - case 40: p = _("Usage: scdaemon [options] (-h for help)"); - break; - case 41: p = _("Syntax: scdaemon [options] [command [args]]\n" - "Smartcard daemon for GnuPG\n"); - break; - - default: p = NULL; - } - return p; -} - - - -static void -i18n_init (void) -{ -#ifdef USE_SIMPLE_GETTEXT - set_gettext_file( PACKAGE ); -#else -#ifdef ENABLE_NLS - setlocale (LC_ALL, ""); - bindtextdomain (PACKAGE, LOCALEDIR); - textdomain (PACKAGE); -#endif -#endif -} - - - -/* Used by gcry for logging */ -static void -my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr) -{ - /* translate the log levels */ - switch (level) - { - case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break; - case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break; - case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break; - case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break; - case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break; - case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break; - case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break; - default: level = JNLIB_LOG_ERROR; break; - } - log_logv (level, fmt, arg_ptr); -} - - -static void -cleanup (void) -{ - if (*socket_name) - { - char *p; - - remove (socket_name); - p = strrchr (socket_name, '/'); - if (p) - { - *p = 0; - rmdir (socket_name); - *p = '/'; - } - *socket_name = 0; - } -} - - -static RETSIGTYPE -cleanup_sh (int sig) -{ - if (caught_fatal_sig) - raise (sig); - caught_fatal_sig = 1; - - /* gcry_control( GCRYCTL_TERM_SECMEM );*/ - cleanup (); - -#ifndef HAVE_DOSISH_SYSTEM - { /* reset action to default action and raise signal again */ - struct sigaction nact; - nact.sa_handler = SIG_DFL; - sigemptyset( &nact.sa_mask ); - nact.sa_flags = 0; - sigaction( sig, &nact, NULL); - } -#endif - raise( sig ); -} - -int -main (int argc, char **argv ) -{ - ARGPARSE_ARGS pargs; - int orig_argc; - int may_coredump; - char **orig_argv; - FILE *configfp = NULL; - char *configname = NULL; - const char *shell; - unsigned configlineno; - int parse_debug = 0; - int default_config =1; - int greeting = 0; - int nogreeting = 0; - int pipe_server = 0; - int is_daemon = 0; - int nodetach = 0; - int csh_style = 0; - char *logfile = NULL; - int debug_wait = 0; - - set_strusage (my_strusage); - gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); - /* Please note that we may running SUID(ROOT), so be very CAREFUL - when adding any stuff between here and the call to INIT_SECMEM() - somewhere after the option parsing */ - log_set_prefix ("scdaemon", 1|4); - i18n_init (); - - /* check that the libraries are suitable. Do it here because - the option parsing may need services of the library */ - if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) ) - { - log_fatal( _("libgcrypt is too old (need %s, have %s)\n"), - NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) ); - } - - ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free); - assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free); - gcry_set_log_handler (my_gcry_logger, NULL); - gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); - - may_coredump = disable_core_dumps (); - - shell = getenv ("SHELL"); - if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") ) - csh_style = 1; - - /* FIXME: Using this homedir option does only make sense when not - running as a system service. We might want to check for this by - looking at the uid or ebtter use an explict option for this */ - opt.homedir = getenv("GNUPGHOME"); - if (!opt.homedir || !*opt.homedir) - opt.homedir = GNUPG_DEFAULT_HOMEDIR; - - /* check whether we have a config file on the commandline */ - orig_argc = argc; - orig_argv = argv; - pargs.argc = &argc; - pargs.argv = &argv; - pargs.flags= 1|(1<<6); /* do not remove the args, ignore version */ - while (arg_parse( &pargs, opts)) - { - if (pargs.r_opt == oDebug || pargs.r_opt == oDebugAll) - parse_debug++; - else if (pargs.r_opt == oOptions) - { /* yes there is one, so we do not try the default one, but - read the option file when it is encountered at the - commandline */ - default_config = 0; - } - else if (pargs.r_opt == oNoOptions) - default_config = 0; /* --no-options */ - else if (pargs.r_opt == oHomedir) - opt.homedir = pargs.r.ret_str; - } - - /* initialize the secure memory. */ - gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); - maybe_setuid = 0; - - /* - Now we are working under our real uid - */ - - - if (default_config) - configname = make_filename (opt.homedir, "scdaemon.conf", NULL ); - - argc = orig_argc; - argv = orig_argv; - pargs.argc = &argc; - pargs.argv = &argv; - pargs.flags= 1; /* do not remove the args */ - next_pass: - if (configname) - { - configlineno = 0; - configfp = fopen (configname, "r"); - if (!configfp) - { - if (default_config) - { - if( parse_debug ) - log_info (_("NOTE: no default option file `%s'\n"), - configname ); - } - else - { - log_error (_("option file `%s': %s\n"), - configname, strerror(errno) ); - exit(2); - } - xfree (configname); - configname = NULL; - } - if (parse_debug && configname ) - log_info (_("reading options from `%s'\n"), configname ); - default_config = 0; - } - - while (optfile_parse( configfp, configname, &configlineno, &pargs, opts) ) - { - switch (pargs.r_opt) - { - case oQuiet: opt.quiet = 1; break; - case oVerbose: opt.verbose++; break; - case oBatch: opt.batch=1; break; - - case oDebug: opt.debug |= pargs.r.ret_ulong; break; - case oDebugAll: opt.debug = ~0; break; - case oDebugWait: debug_wait = pargs.r.ret_int; break; - case oDebugSC: opt.debug_sc = pargs.r.ret_int; break; - - case oOptions: - /* config files may not be nested (silently ignore them) */ - if (!configfp) - { - xfree(configname); - configname = xstrdup(pargs.r.ret_str); - goto next_pass; - } - break; - case oNoGreeting: nogreeting = 1; break; - case oNoVerbose: opt.verbose = 0; break; - case oNoOptions: break; /* no-options */ - case oHomedir: opt.homedir = pargs.r.ret_str; break; - case oNoDetach: nodetach = 1; break; - case oLogFile: logfile = pargs.r.ret_str; break; - case oCsh: csh_style = 1; break; - case oSh: csh_style = 0; break; - case oServer: pipe_server = 1; break; - case oDaemon: is_daemon = 1; break; - - default : pargs.err = configfp? 1:2; break; - } - } - if (configfp) - { - fclose( configfp ); - configfp = NULL; - xfree(configname); - configname = NULL; - goto next_pass; - } - xfree (configname); - configname = NULL; - if (log_get_errorcount(0)) - exit(2); - if (nogreeting ) - greeting = 0; - - if (greeting) - { - fprintf (stderr, "%s %s; %s\n", - strusage(11), strusage(13), strusage(14) ); - fprintf (stderr, "%s\n", strusage(15) ); - } -#ifdef IS_DEVELOPMENT_VERSION - log_info ("NOTE: this is a development version!\n"); -#endif - - - if (atexit (cleanup)) - { - log_error ("atexit failed\n"); - cleanup (); - exit (1); - } - - if (debug_wait && pipe_server) - { - log_debug ("waiting for debugger - my pid is %u .....\n", - (unsigned int)getpid()); - sleep (debug_wait); - log_debug ("... okay\n"); - } - - /* now start with logging to a file if this is desired */ - if (logfile) - { - log_set_file (logfile); - log_set_prefix (NULL, 1|2|4); - } - - - if (pipe_server) - { /* this is the simple pipe based server */ - scd_command_handler (-1); - } - else if (!is_daemon) - { - log_info (_("please use the option `--daemon'" - " to run the program in the background\n")); - } - else - { /* regular server mode */ - int fd; - pid_t pid; - int i; - int len; - struct sockaddr_un serv_addr; - char *p; - - /* fixme: if there is already a running gpg-agent we should - share the same directory - and vice versa */ - *socket_name = 0; - snprintf (socket_name, DIM(socket_name)-1, - "/tmp/gpg-XXXXXX/S.scdaemon"); - socket_name[DIM(socket_name)-1] = 0; - p = strrchr (socket_name, '/'); - if (!p) - BUG (); - *p = 0;; - if (!mkdtemp(socket_name)) - { - log_error ("can't create directory `%s': %s\n", - socket_name, strerror(errno) ); - exit (1); - } - *p = '/'; - - if (strchr (socket_name, ':') ) - { - log_error ("colons are not allowed in the socket name\n"); - exit (1); - } - if (strlen (socket_name)+1 >= sizeof serv_addr.sun_path ) - { - log_error ("name of socket to long\n"); - exit (1); - } - - - fd = socket (AF_UNIX, SOCK_STREAM, 0); - if (fd == -1) - { - log_error ("can't create socket: %s\n", strerror(errno) ); - exit (1); - } - - memset (&serv_addr, 0, sizeof serv_addr); - serv_addr.sun_family = AF_UNIX; - strcpy (serv_addr.sun_path, socket_name); - len = (offsetof (struct sockaddr_un, sun_path) - + strlen(serv_addr.sun_path) + 1); - - if (bind (fd, (struct sockaddr*)&serv_addr, len) == -1) - { - log_error ("error binding socket to `%s': %s\n", - serv_addr.sun_path, strerror (errno) ); - close (fd); - exit (1); - } - - if (listen (fd, 5 ) == -1) - { - log_error ("listen() failed: %s\n", strerror (errno)); - close (fd); - exit (1); - } - - if (opt.verbose) - log_info ("listening on socket `%s'\n", socket_name ); - - - fflush (NULL); - pid = fork (); - if (pid == (pid_t)-1) - { - log_fatal ("fork failed: %s\n", strerror (errno) ); - exit (1); - } - else if (pid) - { /* we are the parent */ - char *infostr; - - close (fd); - - /* create the info string: :: */ - if (asprintf (&infostr, "SCDAEMON_INFO=%s:%lu:1", - socket_name, (ulong)pid ) < 0) - { - log_error ("out of core\n"); - kill (pid, SIGTERM); - exit (1); - } - *socket_name = 0; /* don't let cleanup() remove the socket - - the child should do this from now on */ - if (argc) - { /* run the program given on the commandline */ - if (putenv (infostr)) - { - log_error ("failed to set environment: %s\n", - strerror (errno) ); - kill (pid, SIGTERM ); - exit (1); - } - execvp (argv[0], argv); - log_error ("failed to run the command: %s\n", strerror (errno)); - kill (pid, SIGTERM); - exit (1); - } - else - { - /* print the environment string, so that the caller can use - shell's eval to set it */ - if (csh_style) - { - *strchr (infostr, '=') = ' '; - printf ( "setenv %s\n", infostr); - } - else - { - printf ( "%s; export SCDAEMON_INFO;\n", infostr); - } - free (infostr); - exit (0); - } - /* NOTREACHED */ - } /* end parent */ - - /* this is the child */ - - /* detach from tty and put process into a new session */ - if (!nodetach ) - { /* close stdin, stdout and stderr unless it is the log stream */ - for (i=0; i <= 2; i++) - { - if ( log_get_fd () != i) - close (i); - } - if (setsid() == -1) - { - log_error ("setsid() failed: %s\n", strerror(errno) ); - cleanup (); - exit (1); - } - } - - /* setup signals */ - { - struct sigaction oact, nact; - - nact.sa_handler = cleanup_sh; - sigemptyset (&nact.sa_mask); - nact.sa_flags = 0; - - sigaction (SIGHUP, NULL, &oact); - if (oact.sa_handler != SIG_IGN) - sigaction (SIGHUP, &nact, NULL); - sigaction( SIGTERM, NULL, &oact ); - if (oact.sa_handler != SIG_IGN) - sigaction (SIGTERM, &nact, NULL); - nact.sa_handler = SIG_IGN; - sigaction (SIGPIPE, &nact, NULL); - sigaction (SIGINT, &nact, NULL); - } - - if (chdir("/")) - { - log_error ("chdir to / failed: %s\n", strerror (errno)); - exit (1); - } - - scd_command_handler (fd); - - close (fd); - } - - return 0; -} - -void -scd_exit (int rc) -{ - #if 0 -#warning no update_random_seed_file - update_random_seed_file(); - #endif -#if 0 - /* at this time a bit annoying */ - if (opt.debug & DBG_MEMSTAT_VALUE) - { - gcry_control( GCRYCTL_DUMP_MEMORY_STATS ); - gcry_control( GCRYCTL_DUMP_RANDOM_STATS ); - } - if (opt.debug) - gcry_control (GCRYCTL_DUMP_SECMEM_STATS ); -#endif - gcry_control (GCRYCTL_TERM_SECMEM ); - rc = rc? rc : log_get_errorcount(0)? 2 : 0; - exit (rc); -} - - -void -scd_init_default_ctrl (CTRL ctrl) -{ - -} - -- cgit From 9ca4830a5b8392bc7bb01211407c876fd40b49d4 Mon Sep 17 00:00:00 2001 From: Repo Admin Date: Tue, 5 Aug 2003 17:11:04 +0000 Subject: This commit was manufactured by cvs2svn to create branch 'GNUPG-1-9-BRANCH'. --- agent/ChangeLog | 565 +++++++++++ agent/Makefile.am | 62 ++ agent/agent.h | 226 +++++ agent/call-scd.c | 661 +++++++++++++ agent/command.c | 782 +++++++++++++++ agent/divert-scd.c | 319 +++++++ agent/findkey.c | 359 +++++++ agent/genkey.c | 240 +++++ agent/gpg-agent.c | 1063 +++++++++++++++++++++ agent/learncard.c | 448 +++++++++ agent/minip12.c | 1140 ++++++++++++++++++++++ agent/minip12.h | 33 + agent/pkdecrypt.c | 138 +++ agent/pksign.c | 185 ++++ agent/protect-tool.c | 977 +++++++++++++++++++ agent/protect.c | 971 +++++++++++++++++++ agent/simple-pwquery.c | 486 ++++++++++ common/ChangeLog | 219 +++++ common/Makefile.am | 58 ++ common/README | 11 + common/errors.h | 110 +++ common/gettime.c | 250 +++++ common/iobuf.c | 2415 +++++++++++++++++++++++++++++++++++++++++++++++ common/iobuf.h | 170 ++++ common/maperror.c | 157 +++ common/membuf.c | 89 ++ common/membuf.h | 41 + common/miscellaneous.c | 126 +++ common/simple-pwquery.c | 486 ++++++++++ common/simple-pwquery.h | 69 ++ common/ttyio.c | 508 ++++++++++ common/ttyio.h | 40 + common/util.h | 120 +++ common/yesno.c | 96 ++ kbx/Makefile.am | 52 + kbx/kbxutil.c | 339 +++++++ kbx/keybox-blob.c | 1008 ++++++++++++++++++++ scd/ChangeLog | 242 +++++ scd/Makefile.am | 72 ++ scd/apdu.c | 558 +++++++++++ scd/apdu.h | 73 ++ scd/app-common.h | 128 +++ scd/app-openpgp.c | 1482 +++++++++++++++++++++++++++++ scd/app.c | 278 ++++++ scd/card-common.h | 73 ++ scd/card-p15.c | 502 ++++++++++ scd/card.c | 564 +++++++++++ scd/command.c | 1034 ++++++++++++++++++++ scd/iso7816.c | 371 ++++++++ scd/iso7816.h | 56 ++ scd/sc-copykeys.c | 731 ++++++++++++++ scd/sc-investigate.c | 209 ++++ scd/scdaemon.c | 638 +++++++++++++ scd/scdaemon.h | 127 +++ sm/ChangeLog | 816 ++++++++++++++++ sm/Makefile.am | 55 ++ sm/call-agent.c | 713 ++++++++++++++ sm/call-dirmngr.c | 632 +++++++++++++ sm/certchain.c | 793 ++++++++++++++++ sm/certcheck.c | 300 ++++++ sm/certdump.c | 457 +++++++++ sm/certlist.c | 315 +++++++ sm/certreqgen.c | 699 ++++++++++++++ sm/decrypt.c | 506 ++++++++++ sm/delete.c | 165 ++++ sm/encrypt.c | 550 +++++++++++ sm/export.c | 249 +++++ sm/fingerprint.c | 271 ++++++ sm/gpgsm.c | 1458 ++++++++++++++++++++++++++++ sm/gpgsm.h | 274 ++++++ sm/import.c | 349 +++++++ sm/keydb.c | 1282 +++++++++++++++++++++++++ sm/keylist.c | 617 ++++++++++++ sm/server.c | 1070 +++++++++++++++++++++ sm/sign.c | 621 ++++++++++++ sm/verify.c | 550 +++++++++++ 76 files changed, 34899 insertions(+) create mode 100644 agent/ChangeLog create mode 100644 agent/Makefile.am create mode 100644 agent/agent.h create mode 100644 agent/call-scd.c create mode 100644 agent/command.c create mode 100644 agent/divert-scd.c create mode 100644 agent/findkey.c create mode 100644 agent/genkey.c create mode 100644 agent/gpg-agent.c create mode 100644 agent/learncard.c create mode 100644 agent/minip12.c create mode 100644 agent/minip12.h create mode 100644 agent/pkdecrypt.c create mode 100644 agent/pksign.c create mode 100644 agent/protect-tool.c create mode 100644 agent/protect.c create mode 100644 agent/simple-pwquery.c create mode 100644 common/ChangeLog create mode 100644 common/Makefile.am create mode 100644 common/README create mode 100644 common/errors.h create mode 100644 common/gettime.c create mode 100644 common/iobuf.c create mode 100644 common/iobuf.h create mode 100644 common/maperror.c create mode 100644 common/membuf.c create mode 100644 common/membuf.h create mode 100644 common/miscellaneous.c create mode 100644 common/simple-pwquery.c create mode 100644 common/simple-pwquery.h create mode 100644 common/ttyio.c create mode 100644 common/ttyio.h create mode 100644 common/util.h create mode 100644 common/yesno.c create mode 100644 kbx/Makefile.am create mode 100644 kbx/kbxutil.c create mode 100644 kbx/keybox-blob.c create mode 100644 scd/ChangeLog create mode 100644 scd/Makefile.am create mode 100644 scd/apdu.c create mode 100644 scd/apdu.h create mode 100644 scd/app-common.h create mode 100644 scd/app-openpgp.c create mode 100644 scd/app.c create mode 100644 scd/card-common.h create mode 100644 scd/card-p15.c create mode 100644 scd/card.c create mode 100644 scd/command.c create mode 100644 scd/iso7816.c create mode 100644 scd/iso7816.h create mode 100644 scd/sc-copykeys.c create mode 100644 scd/sc-investigate.c create mode 100644 scd/scdaemon.c create mode 100644 scd/scdaemon.h create mode 100644 sm/ChangeLog create mode 100644 sm/Makefile.am create mode 100644 sm/call-agent.c create mode 100644 sm/call-dirmngr.c create mode 100644 sm/certchain.c create mode 100644 sm/certcheck.c create mode 100644 sm/certdump.c create mode 100644 sm/certlist.c create mode 100644 sm/certreqgen.c create mode 100644 sm/decrypt.c create mode 100644 sm/delete.c create mode 100644 sm/encrypt.c create mode 100644 sm/export.c create mode 100644 sm/fingerprint.c create mode 100644 sm/gpgsm.c create mode 100644 sm/gpgsm.h create mode 100644 sm/import.c create mode 100644 sm/keydb.c create mode 100644 sm/keylist.c create mode 100644 sm/server.c create mode 100644 sm/sign.c create mode 100644 sm/verify.c (limited to 'scd/scdaemon.c') diff --git a/agent/ChangeLog b/agent/ChangeLog new file mode 100644 index 000000000..10f4d45fa --- /dev/null +++ b/agent/ChangeLog @@ -0,0 +1,565 @@ +2003-07-31 Werner Koch + + * Makefile.am (gpg_agent_LDADD): Added INTLLIBS. + (gpg_protect_tool_SOURCES): Added simple-pwquery.[ch] + +2003-07-27 Werner Koch + + Adjusted for gcry_mpi_print and gcry_mpi_scan API change. + +2003-07-15 Werner Koch + + * simple-pwquery.c, simple-pwquery.h: Moved to ../common. + * Makefile.am (gpg_protect_tool_LDADD): Add simple-pwquery.o. + Removed it from xx_SOURCES. + +2003-07-04 Werner Koch + + * gpg-agent.c (handle_connections): Kludge to allow use of Pth 1 + and 2. + +2003-06-30 Werner Koch + + * call-scd.c (learn_status_cb): Store the serialno in PARM. + +2003-06-26 Werner Koch + + * call-scd.c (agent_card_serialno): Don't do a RESET anymore. + +2003-06-25 Werner Koch + + * command.c (cmd_scd): New. + * call-scd.c (agent_card_scd): New. + * divert-scd.c (divert_generic_cmd): New + + * call-scd.c (agent_card_learn): New callback args SINFO. + (learn_status_cb): Pass all other status lines to the sinfo + callback. + * learncard.c (release_sinfo, sinfo_cb): New. + (agent_handle_learn): Pass the new cb to the learn function and + pass the collected information back to the client's assuan + connection. + + * gpg-agent.c (main): Moved pth_init before gcry_check_version. + +2003-06-24 Werner Koch + + * gpg-agent.c (handle_connections): Adjusted for Pth 2.0 + + Adjusted for changes in the libgcrypt API. Some more fixes for the + libgpg-error stuff. + +2003-06-04 Werner Koch + + Renamed error codes from INVALID to INV and removed _ERROR suffixes. + +2003-06-03 Werner Koch + + Changed all error codes in all files to the new libgpg-error scheme. + + * agent.h: Include gpg-error.h and errno.h + * Makefile.am: Link with libgpg-error + + * query.c: assuan.h is now a system header. + * genkey.c (agent_genkey): Fixed silly use of xmalloc by + xtrymalloc. + +2003-04-29 Werner Koch + + * command.c (register_commands): Adjusted for new Assuan semantics. + + * Makefile.am: Don't override LDFLAGS. + +2002-12-04 Werner Koch + + * gpg-agent.c: New variable config_filename. + (parse_rereadable_options): New. + (main): Use it here. Add setting of default values, set + config_filename. + (reread_configuration): Filled with actual code. + +2002-12-03 Werner Koch + + * protect-tool.c (read_key): Don't run make_canonical on a NULL + buffer. + + * command.c (parse_hexstring): New. + (cmd_sethash): Use it. + (parse_keygrip): New. + (cmd_havekey, cmd_sigkey): Use it. + (cmd_passwd): New. + * genkey.c (agent_protect_and_store): New. + (store_key): Add arg FORCE. + (agent_genkey): Pass false to this force of store_key. + +2002-11-13 Werner Koch + + * gpg-agent.c (main): Switch all messages to utf-8. + + * simple-pwquery.c (agent_send_all_options): Use $GPG_TTY and + stdin with ttyname. + + * cache.c (new_data): Uiih - /sizeof d/sizeof *d/. + +2002-11-10 Werner Koch + + * command.c (option_handler): Fix keep_tty check. + +2002-11-06 Werner Koch + + * gpg-agent.c (main): Make sure we have a default ttyname. + * command.c (option_handler): Check opt.keep_tty here + * query.c (start_pinentry): but not anymore here. + +2002-11-05 Werner Koch + + * agent.h (opt,server_control_s): Move display and lc_ variables + to the control struct so that they are per connection. + * gpg-agent.c (agent_init_default_ctrl): New. + (main): Assign those command line options to new default_* variables. + Reset DISPLAY in server mode so that tehre is no implicit default. + * command.c (start_command_handler): Initialize and deinitialize + the control values. + (option_handler): Work on the ctrl values and not on the opt. + * query.c (start_pinentry): New argument CTRL to set the display + connection specific. Changed all callers to pass this value. + (agent_askpin,agent_get_passphrase,agent_get_confirmation): Add + CTRL arg and pass it ot start_pinentry. + * command.c (cmd_get_passphrase): Pass CTRL argument. + * trustlist.c (agent_marktrusted): Add CTRL argument + * command.c (cmd_marktrusted): Pass CTRL argument + * divert-scd.c (ask_for_card): Add CTRL arg. + (divert_pksign,divert_pkdecrypt): Ditto. Changed caller. + (getpin_cb): Use OPAQUE to pass the CTRL variable. Changed both + users. + * findkey.c (unprotect): Add CTRL arg. + (agent_key_from_file): Ditto. + + * query.c (unlock_pinentry): Disconnect the pinentry so that we + start a new one for each request. This is required to support + clients with different environments (e.g. X magic cookies). + +2002-09-05 Neal H. Walfield + + * gpg-agent.c (main) [USE_GNU_PTH]: No need to call + assuan_set_io_func as assuan is smart. + +2002-09-25 Werner Koch + + * gpg-agent.c (handle_signal): Flush cache on SIGHUP. + * cache.c (agent_flush_cache): New. + + * gpg-agent.c, agent.h: Add --keep-display and --keep-tty. + * query.c (start_pinentry): Implement them. The option passing + needs more thoughts. + +2002-09-09 Werner Koch + + * gpg-agent.c (create_private_keys_directory) + (create_directories): New. + (main): Try to create a home directory. + +2002-09-04 Neal H. Walfield + + * gpg-agent.c (main): Use sigaction, not signal. + +2002-09-03 Neal H. Walfield + + * findkey.c: Include . + (agent_write_private_key): Prefer POSIX compatibity, open and + fdopen, over the simplicity of GNU extensions, fopen(file, "x"). + +2002-08-22 Werner Koch + + * query.c (agent_askpin): Provide the default desc text depending + on the pininfo. Do the basic PIN verification only when + min_digits is set. + +2002-08-21 Werner Koch + + * query.c (agent_askpin): Hack to show the right default prompt. + (agent_get_passphrase): Ditto. + + * trans.c: Removed and replaced all usages with standard _() + + * divert-scd.c (getpin_cb): Pass a more descritive text to the + pinentry. + + * Makefile.am: Renamed the binary protect-tool to gpg-protect-tool. + * protect-tool.c: Removed the note about internal use only. + + * gpg-agent.c (main): New option --daemon so that the program is + not accidently started in the background. + +2002-08-16 Werner Koch + + * call-scd.c (learn_status_cb): Handle CERTINFO status. + (agent_card_learn): Add args for certinfo cb. + * learncard.c (release_certinfo,certinfo_cb): New. + (send_cert_back): New. With factored out code from .. + (agent_handle_learn): here. Return certinfo stuff. + +2002-07-26 Werner Koch + + * gpg-agent.c (main): New option --ignore-cache-for-signing. + * command.c (option_handler): New server option + use-cache-for-signing defaulting to true. + (cmd_pksign): handle global and per session option. + * findkey.c (agent_key_from_file, unprotect): New arg + ignore_cache. Changed all callers. + * pksign.c (agent_pksign): Likewise. + +2002-06-29 Werner Koch + + * query.c (start_pinentry): Use GNUPG_DERAULT_PINENTRY. + * call-scd.c (start_scd): Use GNUPG_DEFAULT_SCDAEMON. + +2002-06-28 Werner Koch + + * protect-tool.c (export_p12_file): New. + (main): New command --p12-export. + * minip12.c (create_final,p12_build,compute_tag_length): New. + (store_tag_length): New. + +2002-06-27 Werner Koch + + * minip12.c (crypt_block): Renamed from decrypt_block, add arg to + allow encryption. + + * Makefile.am (pkglib_PROGRAMS): Put protect-tool there. + + * findkey.c (agent_write_private_key,agent_key_from_file) + (agent_key_available): Use GNUPG_PRIVATE_KEYS_DIR constant. + * gpg-agent.c (main): Use GNUPG_DEFAULT_HOMEDIR constant. + + * protect-tool.c (store_private_key): New. + (import_p12_file): Store the new file if requested. + (main): New options --force and --store. + + * gpg-agent.c (main): Set a global flag when running detached. + * query.c (start_pinentry): Pass the list of FD to keep in the + child when not running detached. + * call-scd.c (start_scd): Ditto. + +2002-06-26 Werner Koch + + * command.c (cmd_istrusted, cmd_listtrusted, cmd_marktrusted) + (cmd_pksign, cmd_pkdecrypt, cmd_genkey, cmd_get_passphrase) + (cmd_learn): Print an error message for a failed operation. + + * simple-pwquery.c, simple-pwquery.h: New. + * protect-tool. (get_passphrase): New, used to get a passphrase + from the agent if none was given on the command line. + +2002-06-25 Werner Koch + + * protect-tool.c (rsa_key_check): New. + (import_p12_file): New. + (main): New command --p12-import. + * minip12.c, minip12.h: New. + +2002-06-24 Werner Koch + + * protect-tool.c (read_file): New. + (read_key): Factored most code out to read_file. + +2002-06-17 Werner Koch + + * agent.h: Add a callback function to the pin_entry_info structure. + * query.c (agent_askpin): Use the callback to check for a correct + PIN. Removed the start_err_text argument because it is not + anymore needed; changed callers. + * findkey.c (unprotect): Replace our own check loop by a callback. + (try_unprotect_cb): New. + * genkey.c (reenter_compare_cb): New. + (agent_genkey): Use this callback here. Fixed setting of the pi2 + variable and a segv in case of an empty PIN. + + * divert-scd.c (getpin_cb): Removed some unused stuff and + explained what we still have to change. + +2002-06-12 Werner Koch + + * gpg-agent.c (main): New option --disable-pth. + +2002-06-11 Werner Koch + + * protect-tool.c: Add command --show-keygrip + (show_keygrip): New. + +2002-05-23 Werner Koch + + * call-scd.c: Seirialized all scdaeom access when using Pth. + + * cache.c: Made the cache Pth-thread-safe. + (agent_unlock_cache_entry): New. + * findkey.c (unprotect): Unlock the returned cache value. + * command.c (cmd_get_passphrase): Ditto. + + * gpg-agent.c (main): Register pth_read/write with Assuan. + +2002-05-22 Werner Koch + + * query.c: Serialized all pinentry access when using Pth. + + * gpg-agent.c (handle_signal,start_connection_thread) + (handle_connections): New + (main): Use the new Pth stuff to allow concurrent connections. + * command.c (start_command_handler): Add new arg FD so that the + fucntion can also be used for an already connected socket. + * Makefile.am: Link with Pth. + +2002-05-14 Werner Koch + + * cache.c (housekeeping, agent_put_cache): Use our time() wrapper. + +2002-04-26 Werner Koch + + * cache.c (agent_put_cache): Reinitialize the creation time and + the ttl when reusing a slot. + + * call-scd.c (start_scd): Print debug messages only with debug + flags set. + * query.c (start_pinentry): Ditto. + +2002-04-25 Marcus Brinkmann + + * agent.h (agent_get_confirmation): Replace paramter prompt with + two parameters ok and cancel. + * query.c (agent_get_confirmation): Likewise. Implement this. + * trustlist.c (agent_marktrusted): Fix invocation of + agent_get_confirmation. + * divert-scd.c (ask_for_card): Likewise. + +2002-04-24 Marcus Brinkmann + + * agent.h (struct opt): Add members display, ttyname, ttytype, + lc_ctype, and lc_messages. + * gpg-agent.c (enum cmd_and_opt_values): Add oDisplay, oTTYname, + oTTYtype, oLCctype, and LCmessages. + (main): Handle these options. + * command.c (option_handler): New function. + (register_commands): Register option handler. + * query.c (start_pinentry): Pass the various display and tty + options to the pinentry. + +2002-04-05 Werner Koch + + * protect-tool.c (show_file): New. Used as default action. + +2002-03-28 Werner Koch + + * divert-scd.c (encode_md_for_card): Don't do the pkcs-1 padding, + the scdaemon should take care of it. + (ask_for_card): Hack to not display the trailing zero. + +2002-03-11 Werner Koch + + * learncard.c (kpinfo_cb): Remove the content restrictions from + the keyID. + +2002-03-06 Werner Koch + + * learncard.c: New. + * divert-scd.c (ask_for_card): The serial number is binary so + convert it to hex here. + * findkey.c (agent_write_private_key): New. + * genkey.c (store_key): And use it here. + + * pkdecrypt.c (agent_pkdecrypt): Changed the way the diversion is done. + * divert-scd.c (divert_pkdecrypt): Changed interface and + implemented it. + +2002-03-05 Werner Koch + + * call-scd.c (inq_needpin): New. + (agent_card_pksign): Add getpin_cb args. + (agent_card_pkdecrypt): New. + +2002-03-04 Werner Koch + + * pksign.c (agent_pksign): Changed how the diversion is done. + * divert-scd.c (divert_pksign): Changed interface and implemented it. + (encode_md_for_card): New. + * call-scd.c (agent_card_pksign): New. + +2002-02-28 Werner Koch + + * pksign.c (agent_pksign): Detect whether a Smartcard is to be + used and divert the operation in this case. + * pkdecrypt.c (agent_pkdecrypt): Likewise + * findkey.c (agent_key_from_file): Add optional arg shadow_info + and have it return information about a shadowed key. + * protect.c (agent_get_shadow_info): New. + + * protect.c (snext,sskip,smatch): Moved to + * sexp-parse.h: new file. + * divert-scd.c: New. + +2002-02-27 Werner Koch + + * protect.c (agent_shadow_key): New. + + * command.c (cmd_learn): New command LEARN. + * gpg-agent.c: New option --scdaemon-program. + * call-scd.c (start_scd): New. Based on query.c + * query.c: Add 2 more arguments to all uses of assuan_transact. + +2002-02-18 Werner Koch + + * findkey.c (unprotect): Show an error message for a bad passphrase. + + * command.c (cmd_marktrusted): Implemented. + * trustlist.c (agent_marktrusted): New. + (open_list): Add APPEND arg. + + * query.c (agent_get_confirmation): New. + +2002-02-06 Werner Koch + + * cache.c (housekeeping): Fixed linking in the remove case. + +2002-02-01 Werner Koch + + * gpg-agent.c: New option --default-cache-ttl. + * cache.c (agent_put_cache): Use it. + + * cache.c: Add a few debug outputs. + + * protect.c (agent_private_key_type): New. + * agent.h: Add PRIVATE_KEY_ enums. + * findkey.c (agent_key_from_file): Use it to decide whether we + have to unprotect a key. + (unprotect): Cache the passphrase. + + * findkey.c (agent_key_from_file,agent_key_available): The key + files do now require a ".key" suffix to make a script's life + easier. + * genkey.c (store_key): Ditto. + +2002-01-31 Werner Koch + + * genkey.c (store_key): Protect the key. + (agent_genkey): Ask for the passphrase. + * findkey.c (unprotect): Actually unprotect the key. + * query.c (agent_askpin): Add an optional start_err_text. + +2002-01-30 Werner Koch + + * protect.c: New. + (hash_passphrase): Based on the GnuPG 1.0.6 version. + * protect-tool.c: New + +2002-01-29 Werner Koch + + * findkey.c (agent_key_available): New. + * command.c (cmd_havekey): New. + (register_commands): And register new command. + +2002-01-20 Werner Koch + + * command.c (cmd_get_passphrase): Remove the plus signs. + + * query.c (start_pinentry): Send no-grab option to pinentry + * gpg-agent.c (main): Move variable grab as no_grab to agent.h. + +2002-01-19 Werner Koch + + * gpg-agent.c (main): Disable core dumps. + + * cache.c: New. + * command.c (cmd_get_passphrase): Use the cache. + (cmd_clear_passphrase): Ditto. + + * gpg-agent.c: Removed unused cruft and implement the socket + based server. + (my_strusage): Take bug report address from configure.ac. + * command.c (start_command_handler): Add an argument to start as + regular server. + (start_command_handler): Enable Assuan logging. + +2002-01-15 Werner Koch + + * trustlist.c: New. + * command.c (cmd_istrusted, cmd_listtrusted, cmd_marktrusted): New. + +2002-01-07 Werner Koch + + * genkey.c: Store the secret part and return the public part. + +2002-01-03 Werner Koch + + * command.c (cmd_get_passphrase): New. + (cmd_clear_passphrase): New. + * query.c (agent_get_passphrase): New. + +2002-01-02 Werner Koch + + * genkey.c: New. + * command.c (cmd_genkey): New. + + * command.c (rc_to_assuan_status): Removed and changed all callers + to use map_to_assuan_status. + +2001-12-19 Werner Koch + + * keyformat.txt: New. + +2001-12-19 Marcus Brinkmann + + * query.c (start_pinentry): Add new argument to assuan_pipe_connect. + +2001-12-18 Werner Koch + + * Makefile.am: Use LIBGCRYPT macros + +2001-12-14 Werner Koch + + * gpg-agent.c (main): New option --batch. New option --debug-wait + n, so that it is possible to attach gdb when used in server mode. + * query.c (agent_askpin): Don't ask in batch mode. + + * command.c: Removed the conversion macros as they are now in + ../common/util.h. + +2001-12-14 Marcus Brinkmann + + * query.c (LINELENGTH): Removed. + (agent_askpin): Use ASSUAN_LINELENGTH, not LINELENGTH. + +2001-11-19 Werner Koch + + * gpg-agent.c: Removed all GUI code, removed code for old + protocol. New code to use the Assuan protocol as a server and + also to communicate with a new ask-passphrase utility. + +2000-11-22 Werner Koch + + * gpg-agent.c (main): csh support by Dan Winship, new options --sh + and --csh and set default by consulting $SHELL. + +Mon Aug 21 17:59:17 CEST 2000 Werner Koch + + * gpg-agent.c (passphrase_dialog): Cleanup the window and added the + user supplied text to the window. + (main): Fixed segv in gtk_init when used without a command to start. + + * gpg-agent.c: --flush option. + (req_flush): New. + (req_clear_passphrase): Implemented. + +Fri Aug 18 14:27:14 CEST 2000 Werner Koch + + * gpg-agent.c: New. + * Makefile.am: New. + + + Copyright 2001, 2002 Free Software Foundation, Inc. + + This file is free software; as a special exception the author gives + unlimited permission to copy and/or distribute it, with or without + modifications, as long as this notice is preserved. + + This file is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY, to the extent permitted by law; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/agent/Makefile.am b/agent/Makefile.am new file mode 100644 index 000000000..400aa2fd2 --- /dev/null +++ b/agent/Makefile.am @@ -0,0 +1,62 @@ +# Copyright (C) 2001, 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 + +## Process this file with automake to produce Makefile.in + +localedir = $(datadir)/locale +INCLUDES = -I../intl -DLOCALEDIR=\"$(localedir)\" + +bin_PROGRAMS = gpg-agent +pkglib_PROGRAMS = gpg-protect-tool + +AM_CPPFLAGS = -I$(top_srcdir)/common $(LIBGCRYPT_CFLAGS) \ + $(LIBASSUAN_CFLAGS) $(PTH_CFLAGS) + +gpg_agent_SOURCES = \ + gpg-agent.c agent.h \ + command.c \ + query.c \ + cache.c \ + trans.c \ + findkey.c \ + pksign.c \ + pkdecrypt.c \ + genkey.c \ + protect.c \ + trustlist.c \ + divert-scd.c \ + call-scd.c \ + learncard.c \ + sexp-parse.h + + +gpg_agent_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \ + $(LIBGCRYPT_LIBS) $(PTH_LIBS) $(LIBASSUAN_LIBS) \ + -lgpg-error @INTLLIBS@ + +gpg_protect_tool_SOURCES = \ + protect-tool.c \ + protect.c \ + simple-pwquery.c simple-pwquery.h \ + minip12.c minip12.h + +gpg_protect_tool_LDADD = ../jnlib/libjnlib.a \ + ../common/libcommon.a ../common/libsimple-pwquery.a \ + $(LIBGCRYPT_LIBS) -lgpg-error @INTLLIBS@ + + diff --git a/agent/agent.h b/agent/agent.h new file mode 100644 index 000000000..eb4f4e32d --- /dev/null +++ b/agent/agent.h @@ -0,0 +1,226 @@ +/* agent.h - Global definitions for the agent + * Copyright (C) 2001, 2002, 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 AGENT_H +#define AGENT_H + +#ifdef GPG_ERR_SOURCE_DEFAULT +#error GPG_ERR_SOURCE_DEFAULT already defined +#endif +#define GPG_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_GPGAGENT +#include +#include + +#include +#include "../common/util.h" +#include "../common/errors.h" + +/* Convenience function to be used instead of returning the old + GNUPG_Out_Of_Core. */ +static __inline__ gpg_error_t +out_of_core (void) +{ + return gpg_error (gpg_err_code_from_errno (errno)); +} + +#define MAX_DIGEST_LEN 24 + +/* A large struct name "opt" to keep global flags */ +struct { + unsigned int debug; /* debug flags (DBG_foo_VALUE) */ + int verbose; /* verbosity level */ + int quiet; /* be as quiet as possible */ + int dry_run; /* don't change any persistent data */ + int batch; /* batch mode */ + const char *homedir; /* configuration directory name */ + const char *pinentry_program; + const char *scdaemon_program; + int no_grab; /* don't let the pinentry grab the keyboard */ + unsigned long def_cache_ttl; + + int running_detached; /* we are running detached from the tty. */ + + int ignore_cache_for_signing; + int keep_tty; /* don't switch the TTY (for pinentry) on request */ + int keep_display; /* don't switch the DISPLAY (for pinentry) on request */ +} opt; + + +#define DBG_COMMAND_VALUE 1 /* debug commands i/o */ +#define DBG_MPI_VALUE 2 /* debug mpi details */ +#define DBG_CRYPTO_VALUE 4 /* debug low level crypto */ +#define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */ +#define DBG_CACHE_VALUE 64 /* debug the caching */ +#define DBG_MEMSTAT_VALUE 128 /* show memory statistics */ +#define DBG_HASHING_VALUE 512 /* debug hashing operations */ +#define DBG_ASSUAN_VALUE 1024 + +#define DBG_COMMAND (opt.debug & DBG_COMMAND_VALUE) +#define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE) +#define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE) +#define DBG_CACHE (opt.debug & DBG_CACHE_VALUE) +#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE) +#define DBG_ASSUAN (opt.debug & DBG_ASSUAN_VALUE) + +struct server_local_s; + +struct server_control_s { + struct server_local_s *server_local; + char *display; + char *ttyname; + char *ttytype; + char *lc_ctype; + char *lc_messages; + struct { + int algo; + unsigned char value[MAX_DIGEST_LEN]; + int valuelen; + } digest; + char keygrip[20]; + int have_keygrip; + +}; +typedef struct server_control_s *CTRL; + + +struct pin_entry_info_s { + int min_digits; /* min. number of digits required or 0 for freeform entry */ + int max_digits; /* max. number of allowed digits allowed*/ + int max_tries; + int failed_tries; + int (*check_cb)(struct pin_entry_info_s *); /* CB used to check the PIN */ + void *check_cb_arg; /* optional argument which might be of use in the CB */ + const char *cb_errtext; /* used by the cb to displaye a specific error */ + size_t max_length; /* allocated length of the buffer */ + char pin[1]; +}; + + +enum { + PRIVATE_KEY_UNKNOWN = 0, + PRIVATE_KEY_CLEAR = 1, + PRIVATE_KEY_PROTECTED = 2, + PRIVATE_KEY_SHADOWED = 3 +}; + +/*-- gpg-agent.c --*/ +void agent_exit (int rc); /* also implemented in other tools */ +void agent_init_default_ctrl (struct server_control_s *ctrl); + +/*-- command.c --*/ +void start_command_handler (int, int); + +/*-- findkey.c --*/ +int agent_write_private_key (const unsigned char *grip, + const void *buffer, size_t length, int force); +gcry_sexp_t agent_key_from_file (CTRL ctrl, const unsigned char *grip, + unsigned char **shadow_info, + int ignore_cache); +int agent_key_available (const unsigned char *grip); + +/*-- query.c --*/ +int agent_askpin (CTRL ctrl, + const char *desc_text, struct pin_entry_info_s *pininfo); +int agent_get_passphrase (CTRL ctrl, char **retpass, + const char *desc, const char *prompt, + const char *errtext); +int agent_get_confirmation (CTRL ctrl, const char *desc, const char *ok, + const char *cancel); + +/*-- cache.c --*/ +void agent_flush_cache (void); +int agent_put_cache (const char *key, const char *data, int ttl); +const char *agent_get_cache (const char *key, void **cache_id); +void agent_unlock_cache_entry (void **cache_id); + + +/*-- pksign.c --*/ +int agent_pksign (CTRL ctrl, FILE *outfp, int ignore_cache); + +/*-- pkdecrypt.c --*/ +int agent_pkdecrypt (CTRL ctrl, const char *ciphertext, size_t ciphertextlen, + FILE *outfp); + +/*-- genkey.c --*/ +int agent_genkey (CTRL ctrl, + const char *keyparam, size_t keyparmlen, FILE *outfp); +int agent_protect_and_store (CTRL ctrl, gcry_sexp_t s_skey); + +/*-- protect.c --*/ +int agent_protect (const unsigned char *plainkey, const char *passphrase, + unsigned char **result, size_t *resultlen); +int agent_unprotect (const unsigned char *protectedkey, const char *passphrase, + unsigned char **result, size_t *resultlen); +int agent_private_key_type (const unsigned char *privatekey); +int agent_shadow_key (const unsigned char *pubkey, + const unsigned char *shadow_info, + unsigned char **result); +int agent_get_shadow_info (const unsigned char *shadowkey, + unsigned char const **shadow_info); + + +/*-- trustlist.c --*/ +int agent_istrusted (const char *fpr); +int agent_listtrusted (void *assuan_context); +int agent_marktrusted (CTRL ctrl, const char *name, const char *fpr, int flag); + + +/*-- divert-scd.c --*/ +int divert_pksign (CTRL ctrl, + const unsigned char *digest, size_t digestlen, int algo, + const unsigned char *shadow_info, unsigned char **r_sig); +int divert_pkdecrypt (CTRL ctrl, + const unsigned char *cipher, + const unsigned char *shadow_info, + char **r_buf, size_t *r_len); +int divert_generic_cmd (CTRL ctrl, const char *cmdline, void *assuan_context); + + +/*-- call-scd.c --*/ +int agent_card_learn (void (*kpinfo_cb)(void*, const char *), + void *kpinfo_cb_arg, + void (*certinfo_cb)(void*, const char *), + void *certinfo_cb_arg, + void (*sinfo_cb)(void*, const char *, + size_t, const char *), + void *sinfo_cb_arg); +int agent_card_serialno (char **r_serialno); +int agent_card_pksign (const char *keyid, + int (*getpin_cb)(void *, const char *, char*, size_t), + void *getpin_cb_arg, + const unsigned char *indata, size_t indatalen, + char **r_buf, size_t *r_buflen); +int agent_card_pkdecrypt (const char *keyid, + int (*getpin_cb)(void *, const char *, char*,size_t), + void *getpin_cb_arg, + const unsigned char *indata, size_t indatalen, + char **r_buf, size_t *r_buflen); +int agent_card_readcert (const char *id, char **r_buf, size_t *r_buflen); +int agent_card_readkey (const char *id, unsigned char **r_buf); +int agent_card_scd (const char *cmdline, + int (*getpin_cb)(void *, const char *, char*, size_t), + void *getpin_cb_arg, void *assuan_context); + + +/*-- learncard.c --*/ +int agent_handle_learn (void *assuan_context); + + +#endif /*AGENT_H*/ diff --git a/agent/call-scd.c b/agent/call-scd.c new file mode 100644 index 000000000..14487f1e3 --- /dev/null +++ b/agent/call-scd.c @@ -0,0 +1,661 @@ +/* call-scd.c - fork of the scdaemon to do SC operations + * Copyright (C) 2001, 2002 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 + */ + +/* Fixme: For now we have serialized all access to the scdaemon which + make sense becuase the scdaemon can't handle concurrent connections + right now. We should however keep a list of connections and lock + just that connection - it migth make sense to implemtn parts of + this in Assuan.*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef USE_GNU_PTH +# include +#endif + +#include "agent.h" +#include + +#ifdef _POSIX_OPEN_MAX +#define MAX_OPEN_FDS _POSIX_OPEN_MAX +#else +#define MAX_OPEN_FDS 20 +#endif + +static ASSUAN_CONTEXT scd_ctx = NULL; +#ifdef USE_GNU_PTH +static pth_mutex_t scd_lock = PTH_MUTEX_INIT; +#endif + +/* callback parameter for learn card */ +struct learn_parm_s { + void (*kpinfo_cb)(void*, const char *); + void *kpinfo_cb_arg; + void (*certinfo_cb)(void*, const char *); + void *certinfo_cb_arg; + void (*sinfo_cb)(void*, const char *, size_t, const char *); + void *sinfo_cb_arg; +}; + +struct inq_needpin_s { + ASSUAN_CONTEXT ctx; + int (*getpin_cb)(void *, const char *, char*, size_t); + void *getpin_cb_arg; +}; + +struct membuf { + size_t len; + size_t size; + char *buf; + int out_of_core; +}; + + + +/* A simple implementation of a dynamic buffer. Use init_membuf() to + create a buffer, put_membuf to append bytes and get_membuf to + release and return the buffer. Allocation errors are detected but + only returned at the final get_membuf(), this helps not to clutter + the code with out of core checks. */ + +static void +init_membuf (struct membuf *mb, int initiallen) +{ + mb->len = 0; + mb->size = initiallen; + mb->out_of_core = 0; + mb->buf = xtrymalloc (initiallen); + if (!mb->buf) + mb->out_of_core = 1; +} + +static void +put_membuf (struct membuf *mb, const void *buf, size_t len) +{ + if (mb->out_of_core) + return; + + if (mb->len + len >= mb->size) + { + char *p; + + mb->size += len + 1024; + p = xtryrealloc (mb->buf, mb->size); + if (!p) + { + mb->out_of_core = 1; + return; + } + mb->buf = p; + } + memcpy (mb->buf + mb->len, buf, len); + mb->len += len; +} + +static void * +get_membuf (struct membuf *mb, size_t *len) +{ + char *p; + + if (mb->out_of_core) + { + xfree (mb->buf); + mb->buf = NULL; + return NULL; + } + + p = mb->buf; + *len = mb->len; + mb->buf = NULL; + mb->out_of_core = 1; /* don't allow a reuse */ + return p; +} + + + + +static int +unlock_scd (int rc) +{ +#ifdef USE_GNU_PTH + if (!pth_mutex_release (&scd_lock)) + { + log_error ("failed to release the SCD lock\n"); + if (!rc) + rc = gpg_error (GPG_ERR_INTERNAL); + } +#endif + return rc; +} + +/* Fork off the SCdaemon if this has not already been done */ +static int +start_scd (void) +{ + int rc; + const char *pgmname; + ASSUAN_CONTEXT ctx; + const char *argv[3]; + int no_close_list[3]; + int i; + +#ifdef USE_GNU_PTH + if (!pth_mutex_acquire (&scd_lock, 0, NULL)) + { + log_error ("failed to acquire the SCD lock\n"); + return gpg_error (GPG_ERR_INTERNAL); + } +#endif + + if (scd_ctx) + return 0; /* No need to serialize things because the agent is + expected to tun as a single-thread (or may be in + future using libpth) */ + + if (opt.verbose) + log_info ("no running SCdaemon - starting it\n"); + + if (fflush (NULL)) + { + gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); + log_error ("error flushing pending output: %s\n", strerror (errno)); + return unlock_scd (tmperr); + } + + if (!opt.scdaemon_program || !*opt.scdaemon_program) + opt.scdaemon_program = GNUPG_DEFAULT_SCDAEMON; + if ( !(pgmname = strrchr (opt.scdaemon_program, '/'))) + pgmname = opt.scdaemon_program; + else + pgmname++; + + argv[0] = pgmname; + argv[1] = "--server"; + argv[2] = NULL; + + i=0; + if (!opt.running_detached) + { + if (log_get_fd () != -1) + no_close_list[i++] = log_get_fd (); + no_close_list[i++] = fileno (stderr); + } + no_close_list[i] = -1; + + /* connect to the pinentry and perform initial handshaking */ + rc = assuan_pipe_connect (&ctx, opt.scdaemon_program, (char**)argv, + no_close_list); + if (rc) + { + log_error ("can't connect to the SCdaemon: %s\n", + assuan_strerror (rc)); + return unlock_scd (gpg_error (GPG_ERR_NO_SCDAEMON)); + } + scd_ctx = ctx; + + if (DBG_ASSUAN) + log_debug ("connection to SCdaemon established\n"); + return 0; +} + + + +static AssuanError +learn_status_cb (void *opaque, const char *line) +{ + struct learn_parm_s *parm = opaque; + const char *keyword = line; + int keywordlen; + + for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) + ; + while (spacep (line)) + line++; + if (keywordlen == 8 && !memcmp (keyword, "CERTINFO", keywordlen)) + { + parm->certinfo_cb (parm->certinfo_cb_arg, line); + } + else if (keywordlen == 11 && !memcmp (keyword, "KEYPAIRINFO", keywordlen)) + { + parm->kpinfo_cb (parm->kpinfo_cb_arg, line); + } + else if (keywordlen && *line) + { + parm->sinfo_cb (parm->sinfo_cb_arg, keyword, keywordlen, line); + } + + return 0; +} + +/* Perform the learn command and return a list of all private keys + stored on the card. */ +int +agent_card_learn (void (*kpinfo_cb)(void*, const char *), + void *kpinfo_cb_arg, + void (*certinfo_cb)(void*, const char *), + void *certinfo_cb_arg, + void (*sinfo_cb)(void*, const char *, size_t, const char *), + void *sinfo_cb_arg) +{ + int rc; + struct learn_parm_s parm; + + rc = start_scd (); + if (rc) + return rc; + + memset (&parm, 0, sizeof parm); + parm.kpinfo_cb = kpinfo_cb; + parm.kpinfo_cb_arg = kpinfo_cb_arg; + parm.certinfo_cb = certinfo_cb; + parm.certinfo_cb_arg = certinfo_cb_arg; + parm.sinfo_cb = sinfo_cb; + parm.sinfo_cb_arg = sinfo_cb_arg; + rc = assuan_transact (scd_ctx, "LEARN --force", + NULL, NULL, NULL, NULL, + learn_status_cb, &parm); + if (rc) + return unlock_scd (map_assuan_err (rc)); + + return unlock_scd (0); +} + + + +static AssuanError +get_serialno_cb (void *opaque, const char *line) +{ + char **serialno = opaque; + const char *keyword = line; + const char *s; + int keywordlen, n; + + for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) + ; + while (spacep (line)) + line++; + + if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen)) + { + if (*serialno) + return ASSUAN_Unexpected_Status; + for (n=0,s=line; hexdigitp (s); s++, n++) + ; + if (!n || (n&1)|| !(spacep (s) || !*s) ) + return ASSUAN_Invalid_Status; + *serialno = xtrymalloc (n+1); + if (!*serialno) + return ASSUAN_Out_Of_Core; + memcpy (*serialno, line, n); + (*serialno)[n] = 0; + } + + return 0; +} + +/* Return the serial number of the card or an appropriate error. The + serial number is returned as a hexstring. */ +int +agent_card_serialno (char **r_serialno) +{ + int rc; + char *serialno = NULL; + + rc = start_scd (); + if (rc) + return rc; + + /* Hmm, do we really need this reset - scddaemon should do this or + we can do this if we for some reason figure out that the + operation might have failed due to a missing RESET. Hmmm, I feel + this is really SCdaemon's duty */ +/* rc = assuan_transact (scd_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); */ +/* if (rc) */ +/* return unlock_scd (map_assuan_err (rc)); */ + + rc = assuan_transact (scd_ctx, "SERIALNO", + NULL, NULL, NULL, NULL, + get_serialno_cb, &serialno); + if (rc) + { + xfree (serialno); + return unlock_scd (map_assuan_err (rc)); + } + *r_serialno = serialno; + return unlock_scd (0); +} + + +static AssuanError +membuf_data_cb (void *opaque, const void *buffer, size_t length) +{ + struct membuf *data = opaque; + + if (buffer) + put_membuf (data, buffer, length); + return 0; +} + +/* Handle the NEEDPIN inquiry. */ +static AssuanError +inq_needpin (void *opaque, const char *line) +{ + struct inq_needpin_s *parm = opaque; + char *pin; + size_t pinlen; + int rc; + + if (!(!strncmp (line, "NEEDPIN", 7) && (line[7] == ' ' || !line[7]))) + { + log_error ("unsupported inquiry `%s'\n", line); + return ASSUAN_Inquire_Unknown; + } + line += 7; + + pinlen = 90; + pin = gcry_malloc_secure (pinlen); + if (!pin) + return ASSUAN_Out_Of_Core; + + rc = parm->getpin_cb (parm->getpin_cb_arg, line, pin, pinlen); + if (rc) + rc = ASSUAN_Canceled; + if (!rc) + rc = assuan_send_data (parm->ctx, pin, pinlen); + xfree (pin); + + return rc; +} + + + +/* Create a signature using the current card */ +int +agent_card_pksign (const char *keyid, + int (*getpin_cb)(void *, const char *, char*, size_t), + void *getpin_cb_arg, + const unsigned char *indata, size_t indatalen, + char **r_buf, size_t *r_buflen) +{ + int rc, i; + char *p, line[ASSUAN_LINELENGTH]; + struct membuf data; + struct inq_needpin_s inqparm; + size_t len; + unsigned char *sigbuf; + size_t sigbuflen; + + *r_buf = NULL; + rc = start_scd (); + if (rc) + return rc; + + if (indatalen*2 + 50 > DIM(line)) + return unlock_scd (gpg_error (GPG_ERR_GENERAL)); + + sprintf (line, "SETDATA "); + p = line + strlen (line); + for (i=0; i < indatalen ; i++, p += 2 ) + sprintf (p, "%02X", indata[i]); + rc = assuan_transact (scd_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_scd (map_assuan_err (rc)); + + init_membuf (&data, 1024); + inqparm.ctx = scd_ctx; + inqparm.getpin_cb = getpin_cb; + inqparm.getpin_cb_arg = getpin_cb_arg; + snprintf (line, DIM(line)-1, "PKSIGN %s", keyid); + line[DIM(line)-1] = 0; + rc = assuan_transact (scd_ctx, line, + membuf_data_cb, &data, + inq_needpin, &inqparm, + NULL, NULL); + if (rc) + { + xfree (get_membuf (&data, &len)); + return unlock_scd (map_assuan_err (rc)); + } + sigbuf = get_membuf (&data, &sigbuflen); + + /* create an S-expression from it which is formatted like this: + "(7:sig-val(3:rsa(1:sSIGBUFLEN:SIGBUF)))" */ + *r_buflen = 21 + 11 + sigbuflen + 4; + *r_buf = xtrymalloc (*r_buflen); + if (!*r_buf) + { + gpg_error_t tmperr = out_of_core (); + xfree (*r_buf); + return unlock_scd (tmperr); + } + p = stpcpy (*r_buf, "(7:sig-val(3:rsa(1:s" ); + sprintf (p, "%u:", (unsigned int)sigbuflen); + p += strlen (p); + memcpy (p, sigbuf, sigbuflen); + p += sigbuflen; + strcpy (p, ")))"); + xfree (sigbuf); + + assert (gcry_sexp_canon_len (*r_buf, *r_buflen, NULL, NULL)); + return unlock_scd (0); +} + +/* Decipher INDATA using the current card. Note that the returned value is */ +int +agent_card_pkdecrypt (const char *keyid, + int (*getpin_cb)(void *, const char *, char*, size_t), + void *getpin_cb_arg, + const unsigned char *indata, size_t indatalen, + char **r_buf, size_t *r_buflen) +{ + int rc, i; + char *p, line[ASSUAN_LINELENGTH]; + struct membuf data; + struct inq_needpin_s inqparm; + size_t len; + + *r_buf = NULL; + rc = start_scd (); + if (rc) + return rc; + + /* FIXME: use secure memory where appropriate */ + if (indatalen*2 + 50 > DIM(line)) + return unlock_scd (gpg_error (GPG_ERR_GENERAL)); + + sprintf (line, "SETDATA "); + p = line + strlen (line); + for (i=0; i < indatalen ; i++, p += 2 ) + sprintf (p, "%02X", indata[i]); + rc = assuan_transact (scd_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_scd (map_assuan_err (rc)); + + init_membuf (&data, 1024); + inqparm.ctx = scd_ctx; + inqparm.getpin_cb = getpin_cb; + inqparm.getpin_cb_arg = getpin_cb_arg; + snprintf (line, DIM(line)-1, "PKDECRYPT %s", keyid); + line[DIM(line)-1] = 0; + rc = assuan_transact (scd_ctx, line, + membuf_data_cb, &data, + inq_needpin, &inqparm, + NULL, NULL); + if (rc) + { + xfree (get_membuf (&data, &len)); + return unlock_scd (map_assuan_err (rc)); + } + *r_buf = get_membuf (&data, r_buflen); + if (!*r_buf) + return unlock_scd (gpg_error (GPG_ERR_ENOMEM)); + + return unlock_scd (0); +} + + + +/* Read a certificate with ID into R_BUF and R_BUFLEN. */ +int +agent_card_readcert (const char *id, char **r_buf, size_t *r_buflen) +{ + int rc; + char line[ASSUAN_LINELENGTH]; + struct membuf data; + size_t len; + + *r_buf = NULL; + rc = start_scd (); + if (rc) + return rc; + + init_membuf (&data, 1024); + snprintf (line, DIM(line)-1, "READCERT %s", id); + line[DIM(line)-1] = 0; + rc = assuan_transact (scd_ctx, line, + membuf_data_cb, &data, + NULL, NULL, + NULL, NULL); + if (rc) + { + xfree (get_membuf (&data, &len)); + return unlock_scd (map_assuan_err (rc)); + } + *r_buf = get_membuf (&data, r_buflen); + if (!*r_buf) + return unlock_scd (gpg_error (GPG_ERR_ENOMEM)); + + return unlock_scd (0); +} + + + +/* Read a key with ID and return it in an allocate buffer pointed to + by r_BUF as a valid S-expression. */ +int +agent_card_readkey (const char *id, unsigned char **r_buf) +{ + int rc; + char line[ASSUAN_LINELENGTH]; + struct membuf data; + size_t len, buflen; + + *r_buf = NULL; + rc = start_scd (); + if (rc) + return rc; + + init_membuf (&data, 1024); + snprintf (line, DIM(line)-1, "READKEY %s", id); + line[DIM(line)-1] = 0; + rc = assuan_transact (scd_ctx, line, + membuf_data_cb, &data, + NULL, NULL, + NULL, NULL); + if (rc) + { + xfree (get_membuf (&data, &len)); + return unlock_scd (map_assuan_err (rc)); + } + *r_buf = get_membuf (&data, &buflen); + if (!*r_buf) + return unlock_scd (gpg_error (GPG_ERR_ENOMEM)); + + if (!gcry_sexp_canon_len (*r_buf, buflen, NULL, NULL)) + { + xfree (*r_buf); *r_buf = NULL; + return unlock_scd (gpg_error (GPG_ERR_INV_VALUE)); + } + + return unlock_scd (0); +} + + + + +static AssuanError +pass_status_thru (void *opaque, const char *line) +{ + ASSUAN_CONTEXT ctx = opaque; + char keyword[200]; + int i; + + for (i=0; *line && !spacep (line) && i < DIM(keyword)-1; line++, i++) + keyword[i] = *line; + keyword[i] = 0; + /* truncate any remaining keyword stuff. */ + for (; *line && !spacep (line); line++) + ; + while (spacep (line)) + line++; + + assuan_write_status (ctx, keyword, line); + return 0; +} + +static AssuanError +pass_data_thru (void *opaque, const void *buffer, size_t length) +{ + ASSUAN_CONTEXT ctx = opaque; + + assuan_send_data (ctx, buffer, length); + return 0; +} + + +/* Send the line CMDLINE with command for the SCDdaemon to it and send + all status messages back. This command is used as a general quoting + mechanism to pass everything verbatim to SCDAEMOPN. The PIN + inquirey is handled inside gpg-agent. */ +int +agent_card_scd (const char *cmdline, + int (*getpin_cb)(void *, const char *, char*, size_t), + void *getpin_cb_arg, void *assuan_context) +{ + int rc; + struct inq_needpin_s inqparm; + + rc = start_scd (); + if (rc) + return rc; + + inqparm.ctx = scd_ctx; + inqparm.getpin_cb = getpin_cb; + inqparm.getpin_cb_arg = getpin_cb_arg; + rc = assuan_transact (scd_ctx, cmdline, + pass_data_thru, assuan_context, + inq_needpin, &inqparm, + pass_status_thru, assuan_context); + if (rc) + { + return unlock_scd (map_assuan_err (rc)); + } + + return unlock_scd (0); +} + + diff --git a/agent/command.c b/agent/command.c new file mode 100644 index 000000000..ed4ea6b02 --- /dev/null +++ b/agent/command.c @@ -0,0 +1,782 @@ +/* command.c - gpg-agent command handler + * Copyright (C) 2001, 2002, 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 + */ + +/* FIXME: we should not use the default assuan buffering but setup + some buffering in secure mempory to protect session keys etc. */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "agent.h" + +/* maximum allowed size of the inquired ciphertext */ +#define MAXLEN_CIPHERTEXT 4096 +/* maximum allowed size of the key parameters */ +#define MAXLEN_KEYPARAM 1024 + +#define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t)) + + +#if MAX_DIGEST_LEN < 20 +#error MAX_DIGEST_LEN shorter than keygrip +#endif + +/* Data used to associate an Assuan context with local server data */ +struct server_local_s { + ASSUAN_CONTEXT assuan_ctx; + int message_fd; + int use_cache_for_signing; +}; + + + + + +static void +reset_notify (ASSUAN_CONTEXT ctx) +{ + CTRL ctrl = assuan_get_pointer (ctx); + + memset (ctrl->keygrip, 0, 20); + ctrl->have_keygrip = 0; + ctrl->digest.valuelen = 0; +} + + +/* Check whether the option NAME appears in LINE */ +static int +has_option (const char *line, const char *name) +{ + const char *s; + int n = strlen (name); + + s = strstr (line, name); + return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n))); +} + +/* Parse a hex string. Return an Assuan error code or 0 on success and the + length of the parsed string in LEN. */ +static int +parse_hexstring (ASSUAN_CONTEXT ctx, const char *string, size_t *len) +{ + const char *p; + size_t n; + + /* parse the hash value */ + for (p=string, n=0; hexdigitp (p); p++, n++) + ; + if (*p) + return set_error (Parameter_Error, "invalid hexstring"); + if ((n&1)) + return set_error (Parameter_Error, "odd number of digits"); + *len = n; + return 0; +} + +/* Parse the keygrip in STRING into the provided buffer BUF. BUF must + provide space for 20 bytes. BUF is not changed if the fucntions + returns an error. */ +static int +parse_keygrip (ASSUAN_CONTEXT ctx, const char *string, unsigned char *buf) +{ + int rc; + size_t n; + const unsigned char *p; + + rc = parse_hexstring (ctx, string, &n); + if (rc) + return rc; + n /= 2; + if (n != 20) + return set_error (Parameter_Error, "invalid length of keygrip"); + + for (p=string, n=0; n < 20; p += 2, n++) + buf[n] = xtoi_2 (p); + + return 0; +} + + + + +/* ISTRUSTED + + Return OK when we have an entry with this fingerprint in our + trustlist */ +static int +cmd_istrusted (ASSUAN_CONTEXT ctx, char *line) +{ + int rc, n, i; + char *p; + char fpr[41]; + + /* parse the fingerprint value */ + for (p=line,n=0; hexdigitp (p); p++, n++) + ; + if (*p || !(n == 40 || n == 32)) + return set_error (Parameter_Error, "invalid fingerprint"); + i = 0; + if (n==32) + { + strcpy (fpr, "00000000"); + i += 8; + } + for (p=line; i < 40; p++, i++) + fpr[i] = *p >= 'a'? (*p & 0xdf): *p; + fpr[i] = 0; + rc = agent_istrusted (fpr); + if (!rc) + return 0; + else if (rc == -1) + return ASSUAN_Not_Trusted; + else + { + log_error ("command is_trusted failed: %s\n", gpg_strerror (rc)); + return map_to_assuan_status (rc); + } +} + +/* LISTTRUSTED + + List all entries from the trustlist */ +static int +cmd_listtrusted (ASSUAN_CONTEXT ctx, char *line) +{ + int rc = agent_listtrusted (ctx); + if (rc) + log_error ("command listtrusted failed: %s\n", gpg_strerror (rc)); + return map_to_assuan_status (rc); +} + + +/* MARKTRUSTED + + Store a new key in into the trustlist*/ +static int +cmd_marktrusted (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int rc, n, i; + char *p; + char fpr[41]; + int flag; + + /* parse the fingerprint value */ + for (p=line,n=0; hexdigitp (p); p++, n++) + ; + if (!spacep (p) || !(n == 40 || n == 32)) + return set_error (Parameter_Error, "invalid fingerprint"); + i = 0; + if (n==32) + { + strcpy (fpr, "00000000"); + i += 8; + } + for (p=line; i < 40; p++, i++) + fpr[i] = *p >= 'a'? (*p & 0xdf): *p; + fpr[i] = 0; + + while (spacep (p)) + p++; + flag = *p++; + if ( (flag != 'S' && flag != 'P') || !spacep (p) ) + return set_error (Parameter_Error, "invalid flag - must be P or S"); + while (spacep (p)) + p++; + + rc = agent_marktrusted (ctrl, p, fpr, flag); + if (rc) + log_error ("command marktrusted failed: %s\n", gpg_strerror (rc)); + return map_to_assuan_status (rc); +} + + + + +/* HAVEKEY + + Return success when the secret key is available */ +static int +cmd_havekey (ASSUAN_CONTEXT ctx, char *line) +{ + int rc; + unsigned char buf[20]; + + rc = parse_keygrip (ctx, line, buf); + if (rc) + return rc; + + if (agent_key_available (buf)) + return ASSUAN_No_Secret_Key; + + return 0; +} + + +/* SIGKEY + SETKEY + + Set the key used for a sign or decrypt operation */ +static int +cmd_sigkey (ASSUAN_CONTEXT ctx, char *line) +{ + int rc; + CTRL ctrl = assuan_get_pointer (ctx); + + rc = parse_keygrip (ctx, line, ctrl->keygrip); + if (rc) + return rc; + ctrl->have_keygrip = 1; + return 0; +} + + +/* SETHASH + + The client can use this command to tell the server about the data + (which usually is a hash) to be signed. */ +static int +cmd_sethash (ASSUAN_CONTEXT ctx, char *line) +{ + int rc; + size_t n; + char *p; + CTRL ctrl = assuan_get_pointer (ctx); + unsigned char *buf; + char *endp; + int algo; + + /* parse the algo number and check it */ + algo = (int)strtoul (line, &endp, 10); + for (line = endp; *line == ' ' || *line == '\t'; line++) + ; + if (!algo || gcry_md_test_algo (algo)) + return set_error (Unsupported_Algorithm, NULL); + ctrl->digest.algo = algo; + + /* parse the hash value */ + rc = parse_hexstring (ctx, line, &n); + if (rc) + return rc; + n /= 2; + if (n != 16 && n != 20 && n != 24 && n != 32) + return set_error (Parameter_Error, "unsupported length of hash"); + if (n > MAX_DIGEST_LEN) + return set_error (Parameter_Error, "hash value to long"); + + buf = ctrl->digest.value; + ctrl->digest.valuelen = n; + for (p=line, n=0; n < ctrl->digest.valuelen; p += 2, n++) + buf[n] = xtoi_2 (p); + for (; n < ctrl->digest.valuelen; n++) + buf[n] = 0; + return 0; +} + + +/* PKSIGN + + Perform the actual sign operation. Neither input nor output are + sensitive to eavesdropping */ +static int +cmd_pksign (ASSUAN_CONTEXT ctx, char *line) +{ + int rc; + int ignore_cache = 0; + CTRL ctrl = assuan_get_pointer (ctx); + + if (opt.ignore_cache_for_signing) + ignore_cache = 1; + else if (!ctrl->server_local->use_cache_for_signing) + ignore_cache = 1; + + rc = agent_pksign (ctrl, assuan_get_data_fp (ctx), ignore_cache); + if (rc) + log_error ("command pksign failed: %s\n", gpg_strerror (rc)); + return map_to_assuan_status (rc); +} + +/* PKDECRYPT + + Perform the actual decrypt operation. Input is not + sensitive to eavesdropping */ +static int +cmd_pkdecrypt (ASSUAN_CONTEXT ctx, char *line) +{ + int rc; + CTRL ctrl = assuan_get_pointer (ctx); + char *value; + size_t valuelen; + + /* First inquire the data to decrypt */ + rc = assuan_inquire (ctx, "CIPHERTEXT", + &value, &valuelen, MAXLEN_CIPHERTEXT); + if (rc) + return rc; + + rc = agent_pkdecrypt (ctrl, value, valuelen, assuan_get_data_fp (ctx)); + xfree (value); + if (rc) + log_error ("command pkdecrypt failed: %s\n", gpg_strerror (rc)); + return map_to_assuan_status (rc); +} + + +/* GENKEY + + Generate a new key, store the secret part and return the public + part. Here is an example transaction: + + C: GENKEY + S: INQUIRE KEYPARM + C: D (genkey (rsa (nbits 1024))) + C: END + S: D (public-key + S: D (rsa (n 326487324683264) (e 10001))) + S OK key created +*/ + +static int +cmd_genkey (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int rc; + char *value; + size_t valuelen; + + /* First inquire the parameters */ + rc = assuan_inquire (ctx, "KEYPARAM", &value, &valuelen, MAXLEN_KEYPARAM); + if (rc) + return rc; + + rc = agent_genkey (ctrl, value, valuelen, assuan_get_data_fp (ctx)); + xfree (value); + if (rc) + log_error ("command genkey failed: %s\n", gpg_strerror (rc)); + return map_to_assuan_status (rc); +} + + +static void +plus_to_blank (char *s) +{ + for (; *s; s++) + { + if (*s == '+') + *s = ' '; + } +} + +/* GET_PASSPHRASE [ ] + + This function is usually used to ask for a passphrase to be used + for conventional encryption, but may also be used by programs which + need specal handling of passphrases. This command uses a syntax + which helps clients to use the agent with minimum effort. The + agent either returns with an error or with a OK followed by the hex + encoded passphrase. Note that the length of the strings is + implicitly limited by the maximum length of a command. +*/ + +static int +cmd_get_passphrase (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int rc; + const char *pw; + char *response; + char *cacheid = NULL, *desc = NULL, *prompt = NULL, *errtext = NULL; + char *p; + void *cache_marker; + + /* parse the stuff */ + for (p=line; *p == ' '; p++) + ; + cacheid = p; + p = strchr (cacheid, ' '); + if (p) + { + *p++ = 0; + while (*p == ' ') + p++; + errtext = p; + p = strchr (errtext, ' '); + if (p) + { + *p++ = 0; + while (*p == ' ') + p++; + prompt = p; + p = strchr (prompt, ' '); + if (p) + { + *p++ = 0; + while (*p == ' ') + p++; + desc = p; + p = strchr (desc, ' '); + if (p) + *p = 0; /* ignore garbage */ + } + } + } + if (!cacheid || !*cacheid || strlen (cacheid) > 50) + return set_error (Parameter_Error, "invalid length of cacheID"); + if (!desc) + return set_error (Parameter_Error, "no description given"); + + if (!strcmp (cacheid, "X")) + cacheid = NULL; + if (!strcmp (errtext, "X")) + errtext = NULL; + if (!strcmp (prompt, "X")) + prompt = NULL; + if (!strcmp (desc, "X")) + desc = NULL; + + /* Note: we store the hexified versions in the cache. */ + pw = cacheid ? agent_get_cache (cacheid, &cache_marker) : NULL; + if (pw) + { + assuan_begin_confidential (ctx); + rc = assuan_set_okay_line (ctx, pw); + agent_unlock_cache_entry (&cache_marker); + } + else + { + /* Note, that we only need to replace the + characters and + should leave the other escaping in place because the escaped + string is send verbatim to the pinentry which does the + unescaping (but not the + replacing) */ + if (errtext) + plus_to_blank (errtext); + if (prompt) + plus_to_blank (prompt); + if (desc) + plus_to_blank (desc); + + rc = agent_get_passphrase (ctrl, &response, desc, prompt, errtext); + if (!rc) + { + if (cacheid) + agent_put_cache (cacheid, response, 0); + assuan_begin_confidential (ctx); + rc = assuan_set_okay_line (ctx, response); + xfree (response); + } + } + + if (rc) + log_error ("command get_passphrase failed: %s\n", gpg_strerror (rc)); + return map_to_assuan_status (rc); +} + + +/* CLEAR_PASSPHRASE + + may be used to invalidate the cache entry for a passphrase. The + function returns with OK even when there is no cached passphrase. +*/ + +static int +cmd_clear_passphrase (ASSUAN_CONTEXT ctx, char *line) +{ + char *cacheid = NULL; + char *p; + + /* parse the stuff */ + for (p=line; *p == ' '; p++) + ; + cacheid = p; + p = strchr (cacheid, ' '); + if (p) + *p = 0; /* ignore garbage */ + if (!cacheid || !*cacheid || strlen (cacheid) > 50) + return set_error (Parameter_Error, "invalid length of cacheID"); + + agent_put_cache (cacheid, NULL, 0); + return 0; +} + + +/* LEARN [--send] + + Learn something about the currently inserted smartcard. With + --send the new certificates are send back. */ +static int +cmd_learn (ASSUAN_CONTEXT ctx, char *line) +{ + int rc; + + rc = agent_handle_learn (has_option (line, "--send")? ctx : NULL); + if (rc) + log_error ("command learn failed: %s\n", gpg_strerror (rc)); + return map_to_assuan_status (rc); +} + + + +/* PASSWD + + Change the passphrase/PID for the key identified by keygrip in LINE. */ +static int +cmd_passwd (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int rc; + unsigned char grip[20]; + gcry_sexp_t s_skey = NULL; + unsigned char *shadow_info = NULL; + + rc = parse_keygrip (ctx, line, grip); + if (rc) + return rc; /* we can't jump to leave because this is already an + Assuan error code. */ + + s_skey = agent_key_from_file (ctrl, grip, &shadow_info, 1); + if (!s_skey && !shadow_info) + rc = gpg_error (GPG_ERR_NO_SECKEY); + else if (!s_skey) + { + log_error ("changing a smartcard PIN is not yet supported\n"); + rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED); + } + else + rc = agent_protect_and_store (ctrl, s_skey); + + gcry_sexp_release (s_skey); + xfree (shadow_info); + if (rc) + log_error ("command passwd failed: %s\n", gpg_strerror (rc)); + return map_to_assuan_status (rc); +} + + +/* SCD + + This is a general quote command to redirect everything to the + SCDAEMON. */ +static int +cmd_scd (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int rc; + + rc = divert_generic_cmd (ctrl, line, ctx); + + return map_to_assuan_status (rc); +} + + + +static int +option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value) +{ + CTRL ctrl = assuan_get_pointer (ctx); + + if (!strcmp (key, "display")) + { + if (ctrl->display) + free (ctrl->display); + ctrl->display = strdup (value); + if (!ctrl->display) + return ASSUAN_Out_Of_Core; + } + else if (!strcmp (key, "ttyname")) + { + if (!opt.keep_tty) + { + if (ctrl->ttyname) + free (ctrl->ttyname); + ctrl->ttyname = strdup (value); + if (!ctrl->ttyname) + return ASSUAN_Out_Of_Core; + } + } + else if (!strcmp (key, "ttytype")) + { + if (!opt.keep_tty) + { + if (ctrl->ttytype) + free (ctrl->ttytype); + ctrl->ttytype = strdup (value); + if (!ctrl->ttytype) + return ASSUAN_Out_Of_Core; + } + } + else if (!strcmp (key, "lc-ctype")) + { + if (ctrl->lc_ctype) + free (ctrl->lc_ctype); + ctrl->lc_ctype = strdup (value); + if (!ctrl->lc_ctype) + return ASSUAN_Out_Of_Core; + } + else if (!strcmp (key, "lc-messages")) + { + if (ctrl->lc_messages) + free (ctrl->lc_messages); + ctrl->lc_messages = strdup (value); + if (!ctrl->lc_messages) + return ASSUAN_Out_Of_Core; + } + else if (!strcmp (key, "use-cache-for-signing")) + ctrl->server_local->use_cache_for_signing = *value? atoi (value) : 0; + else + return ASSUAN_Invalid_Option; + + return 0; +} + + +/* Tell the assuan library about our commands */ +static int +register_commands (ASSUAN_CONTEXT ctx) +{ + static struct { + const char *name; + int (*handler)(ASSUAN_CONTEXT, char *line); + } table[] = { + { "ISTRUSTED", cmd_istrusted }, + { "HAVEKEY", cmd_havekey }, + { "SIGKEY", cmd_sigkey }, + { "SETKEY", cmd_sigkey }, + { "SETHASH", cmd_sethash }, + { "PKSIGN", cmd_pksign }, + { "PKDECRYPT", cmd_pkdecrypt }, + { "GENKEY", cmd_genkey }, + { "GET_PASSPHRASE", cmd_get_passphrase }, + { "CLEAR_PASSPHRASE", cmd_clear_passphrase }, + { "LISTTRUSTED", cmd_listtrusted }, + { "MARKTRUSTED", cmd_marktrusted }, + { "LEARN", cmd_learn }, + { "PASSWD", cmd_passwd }, + { "INPUT", NULL }, + { "OUTPUT", NULL }, + { "SCD", cmd_scd }, + { NULL } + }; + int i, rc; + + for (i=0; table[i].name; i++) + { + rc = assuan_register_command (ctx, table[i].name, table[i].handler); + if (rc) + return rc; + } + assuan_register_reset_notify (ctx, reset_notify); + assuan_register_option_handler (ctx, option_handler); + return 0; +} + + +/* Startup the server. If LISTEN_FD and FD is given as -1, this is a simple + piper server, otherwise it is a regular server */ +void +start_command_handler (int listen_fd, int fd) +{ + int rc; + ASSUAN_CONTEXT ctx; + struct server_control_s ctrl; + + memset (&ctrl, 0, sizeof ctrl); + agent_init_default_ctrl (&ctrl); + + if (listen_fd == -1 && fd == -1) + { + int filedes[2]; + + filedes[0] = 0; + filedes[1] = 1; + rc = assuan_init_pipe_server (&ctx, filedes); + } + else if (listen_fd != -1) + { + rc = assuan_init_socket_server (&ctx, listen_fd); + } + else + { + rc = assuan_init_connected_socket_server (&ctx, fd); + } + if (rc) + { + log_error ("failed to initialize the server: %s\n", + assuan_strerror(rc)); + agent_exit (2); + } + rc = register_commands (ctx); + if (rc) + { + log_error ("failed to register commands with Assuan: %s\n", + assuan_strerror(rc)); + agent_exit (2); + } + + assuan_set_pointer (ctx, &ctrl); + ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local); + ctrl.server_local->assuan_ctx = ctx; + ctrl.server_local->message_fd = -1; + ctrl.server_local->use_cache_for_signing = 1; + + if (DBG_ASSUAN) + assuan_set_log_stream (ctx, log_get_stream ()); + + for (;;) + { + rc = assuan_accept (ctx); + if (rc == -1) + { + break; + } + else if (rc) + { + log_info ("Assuan accept problem: %s\n", assuan_strerror (rc)); + break; + } + + rc = assuan_process (ctx); + if (rc) + { + log_info ("Assuan processing failed: %s\n", assuan_strerror (rc)); + continue; + } + } + + + assuan_deinit_server (ctx); + if (ctrl.display) + free (ctrl.display); + if (ctrl.ttyname) + free (ctrl.ttyname); + if (ctrl.ttytype) + free (ctrl.ttytype); + if (ctrl.lc_ctype) + free (ctrl.lc_ctype); + if (ctrl.lc_messages) + free (ctrl.lc_messages); +} + diff --git a/agent/divert-scd.c b/agent/divert-scd.c new file mode 100644 index 000000000..69f184474 --- /dev/null +++ b/agent/divert-scd.c @@ -0,0 +1,319 @@ +/* divert-scd.c - divert operations to the scdaemon + * Copyright (C) 2002, 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 +#include +#include + +#include "agent.h" +#include "sexp-parse.h" +#include "i18n.h" + + +static int +ask_for_card (CTRL ctrl, const unsigned char *shadow_info, char **r_kid) +{ + int rc, i; + const unsigned char *s; + size_t n; + char *serialno; + int no_card = 0; + char *desc; + char *want_sn, *want_kid; + int want_sn_displen; + + *r_kid = NULL; + s = shadow_info; + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + want_sn = xtrymalloc (n*2+1); + if (!want_sn) + return out_of_core (); + for (i=0; i < n; i++) + sprintf (want_sn+2*i, "%02X", s[i]); + s += n; + /* We assume that a 20 byte serial number is a standard one which + seems to have the property to have a zero in the last nibble. We + don't display this '0' because it may confuse the user */ + want_sn_displen = strlen (want_sn); + if (want_sn_displen == 20 && want_sn[19] == '0') + want_sn_displen--; + + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + want_kid = xtrymalloc (n+1); + if (!want_kid) + { + gpg_error_t tmperr = out_of_core (); + xfree (want_sn); + return tmperr; + } + memcpy (want_kid, s, n); + want_kid[n] = 0; + + for (;;) + { + rc = agent_card_serialno (&serialno); + if (!rc) + { + log_debug ("detected card with S/N %s\n", serialno); + i = strcmp (serialno, want_sn); + xfree (serialno); + serialno = NULL; + if (!i) + { + xfree (want_sn); + *r_kid = want_kid; + return 0; /* yes, we have the correct card */ + } + } + else if (gpg_err_code (rc) == GPG_ERR_CARD_NOT_PRESENT) + { + log_debug ("no card present\n"); + rc = 0; + no_card = 1; + } + else + { + log_error ("error accesing card: %s\n", gpg_strerror (rc)); + } + + if (!rc) + { + if (asprintf (&desc, + "%s:%%0A%%0A" + " \"%.*s\"", + no_card? "Please insert the card with serial number" + : "Please remove the current card and " + "insert the one with serial number", + want_sn_displen, want_sn) < 0) + { + rc = out_of_core (); + } + else + { + rc = agent_get_confirmation (ctrl, desc, NULL, NULL); + free (desc); + } + } + if (rc) + { + xfree (want_sn); + xfree (want_kid); + return rc; + } + } +} + + +/* Put the DIGEST into an DER encoded comtainer and return it in R_VAL. */ +static int +encode_md_for_card (const unsigned char *digest, size_t digestlen, int algo, + unsigned char **r_val, size_t *r_len) +{ + byte *frame; + byte asn[100]; + size_t asnlen; + + asnlen = DIM(asn); + if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen)) + { + log_error ("no object identifier for algo %d\n", algo); + return gpg_error (GPG_ERR_INTERNAL); + } + + frame = xtrymalloc (asnlen + digestlen); + if (!frame) + return out_of_core (); + memcpy (frame, asn, asnlen); + memcpy (frame+asnlen, digest, digestlen); + if (DBG_CRYPTO) + log_printhex ("encoded hash:", frame, asnlen+digestlen); + + *r_val = frame; + *r_len = asnlen+digestlen; + return 0; +} + + +/* Callback used to ask for the PIN which should be set into BUF. The + buf has been allocated by the caller and is of size MAXBUF which + includes the terminating null. The function should return an UTF-8 + string with the passphrase, the buffer may optionally be padded + with arbitrary characters */ +static int +getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf) +{ + struct pin_entry_info_s *pi; + int rc; + char *desc; + CTRL ctrl = opaque; + + if (maxbuf < 2) + return gpg_error (GPG_ERR_INV_VALUE); + + + /* FIXME: keep PI and TRIES in OPAQUE. Frankly this is a whole + mess because we should call the card's verify function from the + pinentry check pin CB. */ + pi = gcry_calloc_secure (1, sizeof (*pi) + 100); + pi->max_length = maxbuf-1; + pi->min_digits = 0; /* we want a real passphrase */ + pi->max_digits = 8; + pi->max_tries = 3; + + if ( asprintf (&desc, _("Please enter the PIN%s%s%s to unlock the card"), + info? " (`":"", + info? info:"", + info? "')":"") < 0) + desc = NULL; + rc = agent_askpin (ctrl, desc?desc:info, pi); + free (desc); + if (!rc) + { + strncpy (buf, pi->pin, maxbuf-1); + buf[maxbuf-1] = 0; + } + xfree (pi); + return rc; +} + + + + +int +divert_pksign (CTRL ctrl, + const unsigned char *digest, size_t digestlen, int algo, + const unsigned char *shadow_info, unsigned char **r_sig) +{ + int rc; + char *kid; + size_t siglen; + char *sigval; + unsigned char *data; + size_t ndata; + + rc = ask_for_card (ctrl, shadow_info, &kid); + if (rc) + return rc; + + rc = encode_md_for_card (digest, digestlen, algo, + &data, &ndata); + if (rc) + return rc; + + rc = agent_card_pksign (kid, getpin_cb, ctrl, + data, ndata, &sigval, &siglen); + if (!rc) + *r_sig = sigval; + xfree (data); + xfree (kid); + + return rc; +} + + +/* Decrypt the the value given asn an S-expression in CIPHER using the + key identified by SHADOW_INFO and return the plaintext in an + allocated buffer in R_BUF. */ +int +divert_pkdecrypt (CTRL ctrl, + const unsigned char *cipher, + const unsigned char *shadow_info, + char **r_buf, size_t *r_len) +{ + int rc; + char *kid; + const unsigned char *s; + size_t n; + const unsigned char *ciphertext; + size_t ciphertextlen; + char *plaintext; + size_t plaintextlen; + + s = cipher; + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (!smatch (&s, n, "enc-val")) + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + if (*s != '(') + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (!smatch (&s, n, "rsa")) + return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + if (*s != '(') + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (!smatch (&s, n, "a")) + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + ciphertext = s; + ciphertextlen = n; + + rc = ask_for_card (ctrl, shadow_info, &kid); + if (rc) + return rc; + + rc = agent_card_pkdecrypt (kid, getpin_cb, ctrl, + ciphertext, ciphertextlen, + &plaintext, &plaintextlen); + if (!rc) + { + *r_buf = plaintext; + *r_len = plaintextlen; + } + xfree (kid); + return rc; +} + + +int +divert_generic_cmd (CTRL ctrl, const char *cmdline, void *assuan_context) +{ + return agent_card_scd (cmdline, getpin_cb, ctrl, assuan_context); +} + + + + + diff --git a/agent/findkey.c b/agent/findkey.c new file mode 100644 index 000000000..db36cb1b9 --- /dev/null +++ b/agent/findkey.c @@ -0,0 +1,359 @@ +/* findkey.c - locate the secret key + * Copyright (C) 2001, 2002, 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 +#include +#include +#include +#include + +#include "agent.h" + +/* Helper to pass data to the check callback of the unprotect function. */ +struct try_unprotect_arg_s { + const unsigned char *protected_key; + unsigned char *unprotected_key; +}; + + + +int +agent_write_private_key (const unsigned char *grip, + const void *buffer, size_t length, int force) +{ + int i; + char *fname; + FILE *fp; + char hexgrip[40+4+1]; + + for (i=0; i < 20; i++) + sprintf (hexgrip+2*i, "%02X", grip[i]); + strcpy (hexgrip+40, ".key"); + + fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL); + if (force) + fp = fopen (fname, "wb"); + else + { + int fd; + + if (!access (fname, F_OK)) + { + log_error ("secret key file `%s' already exists\n", fname); + xfree (fname); + return gpg_error (GPG_ERR_GENERAL); + } + + /* We would like to create FNAME but only if it does not already + exist. We cannot make this guarantee just using POSIX (GNU + provides the "x" opentype for fopen, however, this is not + portable). Thus, we use the more flexible open function and + then use fdopen to obtain a stream. + + The mode parameter to open is what fopen uses. It will be + combined with the process' umask automatically. */ + fd = open (fname, O_CREAT | O_EXCL | O_RDWR, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + if (fd < 0) + fp = 0; + else + { + fp = fdopen (fd, "wb"); + if (!fp) + { + int save_e = errno; + close (fd); + errno = save_e; + } + } + } + + if (!fp) + { + gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); + log_error ("can't create `%s': %s\n", fname, strerror (errno)); + xfree (fname); + return tmperr; + } + + if (fwrite (buffer, length, 1, fp) != 1) + { + gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); + log_error ("error writing `%s': %s\n", fname, strerror (errno)); + fclose (fp); + remove (fname); + xfree (fname); + return tmperr; + } + if ( fclose (fp) ) + { + gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); + log_error ("error closing `%s': %s\n", fname, strerror (errno)); + remove (fname); + xfree (fname); + return tmperr; + } + + xfree (fname); + return 0; +} + + +/* Callback function to try the unprotection from the passpharse query + code. */ +static int +try_unprotect_cb (struct pin_entry_info_s *pi) +{ + struct try_unprotect_arg_s *arg = pi->check_cb_arg; + size_t dummy; + + assert (!arg->unprotected_key); + return agent_unprotect (arg->protected_key, pi->pin, + &arg->unprotected_key, &dummy); +} + + +/* Unprotect the canconical encoded S-expression key in KEYBUF. GRIP + should be the hex encoded keygrip of that key to be used with the + caching mechanism. */ +static int +unprotect (CTRL ctrl, + unsigned char **keybuf, const unsigned char *grip, int ignore_cache) +{ + struct pin_entry_info_s *pi; + struct try_unprotect_arg_s arg; + int rc, i; + unsigned char *result; + size_t resultlen; + char hexgrip[40+1]; + + for (i=0; i < 20; i++) + sprintf (hexgrip+2*i, "%02X", grip[i]); + hexgrip[40] = 0; + + /* first try to get it from the cache - if there is none or we can't + unprotect it, we fall back to ask the user */ + if (!ignore_cache) + { + void *cache_marker; + const char *pw = agent_get_cache (hexgrip, &cache_marker); + if (pw) + { + rc = agent_unprotect (*keybuf, pw, &result, &resultlen); + agent_unlock_cache_entry (&cache_marker); + if (!rc) + { + xfree (*keybuf); + *keybuf = result; + return 0; + } + rc = 0; + } + } + + pi = gcry_calloc_secure (1, sizeof (*pi) + 100); + pi->max_length = 100; + pi->min_digits = 0; /* we want a real passphrase */ + pi->max_digits = 8; + pi->max_tries = 3; + pi->check_cb = try_unprotect_cb; + arg.protected_key = *keybuf; + arg.unprotected_key = NULL; + pi->check_cb_arg = &arg; + + rc = agent_askpin (ctrl, NULL, pi); + if (!rc) + { + assert (arg.unprotected_key); + agent_put_cache (hexgrip, pi->pin, 0); + xfree (*keybuf); + *keybuf = arg.unprotected_key; + } + xfree (pi); + return rc; +} + + + +/* Return the secret key as an S-Exp after locating it using the grip. + Returns NULL if key is not available or the operation should be + diverted to a token. In the latter case shadow_info will point to + an allocated S-Expression with the shadow_info part from the file. + With IGNORE_CACHE passed as true the passphrase is not taken from + the cache.*/ +gcry_sexp_t +agent_key_from_file (CTRL ctrl, + const unsigned char *grip, unsigned char **shadow_info, + int ignore_cache) +{ + int i, rc; + char *fname; + FILE *fp; + struct stat st; + unsigned char *buf; + size_t len, buflen, erroff; + gcry_sexp_t s_skey; + char hexgrip[40+4+1]; + + if (shadow_info) + *shadow_info = NULL; + + for (i=0; i < 20; i++) + sprintf (hexgrip+2*i, "%02X", grip[i]); + strcpy (hexgrip+40, ".key"); + + fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL); + fp = fopen (fname, "rb"); + if (!fp) + { + log_error ("can't open `%s': %s\n", fname, strerror (errno)); + xfree (fname); + return NULL; + } + + if (fstat (fileno(fp), &st)) + { + log_error ("can't stat `%s': %s\n", fname, strerror (errno)); + xfree (fname); + fclose (fp); + return NULL; + } + + buflen = st.st_size; + buf = xmalloc (buflen+1); + if (fread (buf, buflen, 1, fp) != 1) + { + log_error ("error reading `%s': %s\n", fname, strerror (errno)); + xfree (fname); + fclose (fp); + xfree (buf); + return NULL; + } + + rc = gcry_sexp_sscan (&s_skey, &erroff, buf, buflen); + xfree (fname); + fclose (fp); + xfree (buf); + if (rc) + { + log_error ("failed to build S-Exp (off=%u): %s\n", + (unsigned int)erroff, gpg_strerror (rc)); + return NULL; + } + len = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, NULL, 0); + assert (len); + buf = xtrymalloc (len); + if (!buf) + { + gcry_sexp_release (s_skey); + return NULL; + } + len = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, buf, len); + assert (len); + gcry_sexp_release (s_skey); + + switch (agent_private_key_type (buf)) + { + case PRIVATE_KEY_CLEAR: + break; /* no unprotection needed */ + case PRIVATE_KEY_PROTECTED: + rc = unprotect (ctrl, &buf, grip, ignore_cache); + if (rc) + log_error ("failed to unprotect the secret key: %s\n", + gpg_strerror (rc)); + break; + case PRIVATE_KEY_SHADOWED: + if (shadow_info) + { + const unsigned char *s; + size_t n; + + rc = agent_get_shadow_info (buf, &s); + if (!rc) + { + n = gcry_sexp_canon_len (s, 0, NULL,NULL); + assert (n); + *shadow_info = xtrymalloc (n); + if (!*shadow_info) + rc = out_of_core (); + else + { + memcpy (*shadow_info, s, n); + rc = 0; + } + } + if (rc) + log_error ("get_shadow_info failed: %s\n", gpg_strerror (rc)); + } + rc = -1; /* ugly interface: we return an error but keep a value + in shadow_info. */ + break; + default: + log_error ("invalid private key format\n"); + rc = gpg_error (GPG_ERR_BAD_SECKEY); + break; + } + if (rc) + { + xfree (buf); + return NULL; + } + + /* arggg FIXME: does scan support secure memory? */ + rc = gcry_sexp_sscan (&s_skey, &erroff, + buf, gcry_sexp_canon_len (buf, 0, NULL, NULL)); + xfree (buf); + if (rc) + { + log_error ("failed to build S-Exp (off=%u): %s\n", + (unsigned int)erroff, gpg_strerror (rc)); + return NULL; + } + + return s_skey; +} + +/* Return the secret key as an S-Exp after locating it using the grip. + Returns NULL if key is not available. 0 = key is available */ +int +agent_key_available (const unsigned char *grip) +{ + int i; + char *fname; + char hexgrip[40+4+1]; + + for (i=0; i < 20; i++) + sprintf (hexgrip+2*i, "%02X", grip[i]); + strcpy (hexgrip+40, ".key"); + + fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL); + i = !access (fname, R_OK)? 0 : -1; + xfree (fname); + return i; +} + + + diff --git a/agent/genkey.c b/agent/genkey.c new file mode 100644 index 000000000..0a0577f17 --- /dev/null +++ b/agent/genkey.c @@ -0,0 +1,240 @@ +/* pksign.c - Generate a keypair + * Copyright (C) 2002, 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 + +#include "agent.h" +#include "i18n.h" + +static int +store_key (gcry_sexp_t private, const char *passphrase, int force) +{ + int rc; + char *buf; + size_t len; + unsigned char grip[20]; + + if ( !gcry_pk_get_keygrip (private, grip) ) + { + log_error ("can't calculate keygrip\n"); + return gpg_error (GPG_ERR_GENERAL); + } + + len = gcry_sexp_sprint (private, GCRYSEXP_FMT_CANON, NULL, 0); + assert (len); + buf = gcry_malloc_secure (len); + if (!buf) + return out_of_core (); + len = gcry_sexp_sprint (private, GCRYSEXP_FMT_CANON, buf, len); + assert (len); + + if (passphrase) + { + unsigned char *p; + + rc = agent_protect (buf, passphrase, &p, &len); + if (rc) + { + xfree (buf); + return rc; + } + xfree (buf); + buf = p; + } + + rc = agent_write_private_key (grip, buf, len, force); + xfree (buf); + return rc; +} + +/* Callback function to compare the first entered PIN with the one + currently being entered. */ +static int +reenter_compare_cb (struct pin_entry_info_s *pi) +{ + const char *pin1 = pi->check_cb_arg; + + if (!strcmp (pin1, pi->pin)) + return 0; /* okay */ + pi->cb_errtext = _("does not match - try again"); + return -1; +} + + + +/* Generate a new keypair according to the parameters given in + KEYPARAM */ +int +agent_genkey (CTRL ctrl, const char *keyparam, size_t keyparamlen, + FILE *outfp) +{ + gcry_sexp_t s_keyparam, s_key, s_private, s_public; + struct pin_entry_info_s *pi, *pi2; + int rc; + size_t len; + char *buf; + + rc = gcry_sexp_sscan (&s_keyparam, NULL, keyparam, keyparamlen); + if (rc) + { + log_error ("failed to convert keyparam: %s\n", gpg_strerror (rc)); + return gpg_error (GPG_ERR_INV_DATA); + } + + /* Get the passphrase now, cause key generation may take a while. */ + { + const char *text1 = _("Please enter the passphrase to%0A" + "to protect your new key"); + const char *text2 = _("Please re-enter this passphrase"); + + pi = gcry_calloc_secure (2, sizeof (*pi) + 100); + pi2 = pi + (sizeof *pi + 100); + pi->max_length = 100; + pi->max_tries = 3; + pi2->max_length = 100; + pi2->max_tries = 3; + pi2->check_cb = reenter_compare_cb; + pi2->check_cb_arg = pi->pin; + + rc = agent_askpin (ctrl, text1, pi); + if (!rc) + rc = agent_askpin (ctrl, text2, pi2); + if (rc) + return rc; + if (!*pi->pin) + { + xfree (pi); + pi = NULL; /* User does not want a passphrase. */ + } + } + + rc = gcry_pk_genkey (&s_key, s_keyparam ); + gcry_sexp_release (s_keyparam); + if (rc) + { + log_error ("key generation failed: %s\n", gpg_strerror (rc)); + xfree (pi); + return map_gcry_err (rc); + } + + /* break out the parts */ + s_private = gcry_sexp_find_token (s_key, "private-key", 0); + if (!s_private) + { + log_error ("key generation failed: invalid return value\n"); + gcry_sexp_release (s_key); + xfree (pi); + return gpg_error (GPG_ERR_INV_DATA); + } + s_public = gcry_sexp_find_token (s_key, "public-key", 0); + if (!s_public) + { + log_error ("key generation failed: invalid return value\n"); + gcry_sexp_release (s_private); + gcry_sexp_release (s_key); + xfree (pi); + return gpg_error (GPG_ERR_INV_DATA); + } + gcry_sexp_release (s_key); s_key = NULL; + + /* store the secret key */ + log_debug ("storing private key\n"); + rc = store_key (s_private, pi? pi->pin:NULL, 0); + xfree (pi); pi = NULL; + gcry_sexp_release (s_private); + if (rc) + { + gcry_sexp_release (s_public); + return rc; + } + + /* return the public key */ + log_debug ("returning public key\n"); + len = gcry_sexp_sprint (s_public, GCRYSEXP_FMT_CANON, NULL, 0); + assert (len); + buf = xtrymalloc (len); + if (!buf) + { + gpg_error_t tmperr = out_of_core (); + gcry_sexp_release (s_private); + gcry_sexp_release (s_public); + return tmperr; + } + len = gcry_sexp_sprint (s_public, GCRYSEXP_FMT_CANON, buf, len); + assert (len); + if (fwrite (buf, len, 1, outfp) != 1) + { + gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); + log_error ("error writing public key: %s\n", strerror (errno)); + gcry_sexp_release (s_private); + gcry_sexp_release (s_public); + xfree (buf); + return tmperr; + } + gcry_sexp_release (s_public); + xfree (buf); + + return 0; +} + + + +/* Apply a new passpahrse to the key S_SKEY and store it. */ +int +agent_protect_and_store (CTRL ctrl, gcry_sexp_t s_skey) +{ + struct pin_entry_info_s *pi, *pi2; + int rc; + + { + const char *text1 = _("Please enter the new passphrase"); + const char *text2 = _("Please re-enter this passphrase"); + + pi = gcry_calloc_secure (2, sizeof (*pi) + 100); + pi2 = pi + (sizeof *pi + 100); + pi->max_length = 100; + pi->max_tries = 3; + pi2->max_length = 100; + pi2->max_tries = 3; + pi2->check_cb = reenter_compare_cb; + pi2->check_cb_arg = pi->pin; + + rc = agent_askpin (ctrl, text1, pi); + if (!rc) + rc = agent_askpin (ctrl, text2, pi2); + if (rc) + return rc; + if (!*pi->pin) + { + xfree (pi); + pi = NULL; /* User does not want a passphrase. */ + } + } + + rc = store_key (s_skey, pi? pi->pin:NULL, 1); + xfree (pi); + return 0; +} diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c new file mode 100644 index 000000000..675f2be3f --- /dev/null +++ b/agent/gpg-agent.c @@ -0,0 +1,1063 @@ +/* gpg-agent.c - The GnuPG Agent + * Copyright (C) 2000, 2001, 2002, 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 +#include +#include +#include +#include +#include +#include +#include +#ifdef USE_GNU_PTH +# include +#endif + +#define JNLIB_NEED_LOG_LOGV +#include "agent.h" +#include /* malloc hooks */ + +#include "i18n.h" +#include "sysutils.h" + + +enum cmd_and_opt_values +{ aNull = 0, + oCsh = 'c', + oQuiet = 'q', + oSh = 's', + oVerbose = 'v', + + oNoVerbose = 500, + oOptions, + oDebug, + oDebugAll, + oDebugWait, + oNoGreeting, + oNoOptions, + oHomedir, + oNoDetach, + oNoGrab, + oLogFile, + oServer, + oDaemon, + oBatch, + + oPinentryProgram, + oDisplay, + oTTYname, + oTTYtype, + oLCctype, + oLCmessages, + oScdaemonProgram, + oDefCacheTTL, + oDisablePth, + + oIgnoreCacheForSigning, + oKeepTTY, + oKeepDISPLAY, + +aTest }; + + + +static ARGPARSE_OPTS opts[] = { + + { 301, NULL, 0, N_("@Options:\n ") }, + + { oServer, "server", 0, N_("run in server mode (foreground)") }, + { oDaemon, "daemon", 0, N_("run in daemon mode (background)") }, + { oVerbose, "verbose", 0, N_("verbose") }, + { oQuiet, "quiet", 0, N_("be somewhat more quiet") }, + { oSh, "sh", 0, N_("sh-style command output") }, + { oCsh, "csh", 0, N_("csh-style command output") }, + { oOptions, "options" , 2, N_("read options from file")}, + { oDebug, "debug" ,4|16, N_("set debugging flags")}, + { oDebugAll, "debug-all" ,0, N_("enable full debugging")}, + { oDebugWait,"debug-wait",1, "@"}, + { oNoDetach, "no-detach" ,0, N_("do not detach from the console")}, + { oNoGrab, "no-grab" ,0, N_("do not grab keyboard and mouse")}, + { oLogFile, "log-file" ,2, N_("use a log file for the server")}, + { oDisablePth, "disable-pth", 0, N_("do not allow multiple connections")}, + + { oPinentryProgram, "pinentry-program", 2 , "path to PIN Entry program" }, + { oDisplay, "display", 2, "set the display" }, + { oTTYname, "ttyname", 2, "set the tty terminal node name" }, + { oTTYtype, "ttytype", 2, "set the tty terminal type" }, + { oLCctype, "lc-ctype", 2, "set the tty LC_CTYPE value" }, + { oLCmessages, "lc-messages", 2, "set the tty LC_MESSAGES value" }, + + { oScdaemonProgram, "scdaemon-program", 2 , "path to SCdaemon program" }, + { oDefCacheTTL, "default-cache-ttl", 4, + "|N|expire cached PINs after N seconds"}, + { oIgnoreCacheForSigning, "ignore-cache-for-signing", 0, + "do not use the PIN cache when signing"}, + { oKeepTTY, "keep-tty", 0, N_("ignore requests to change the TTY")}, + { oKeepDISPLAY, "keep-display", + 0, N_("ignore requests to change the X display")}, + {0} +}; + + +static volatile int caught_fatal_sig = 0; + +/* flag to indicate that a shutdown was requested */ +static int shutdown_pending; + + +/* It is possible that we are currently running under setuid permissions */ +static int maybe_setuid = 1; + +/* Name of the communication socket */ +static char socket_name[128]; + +/* Default values for options passed to the pinentry. */ +static char *default_display; +static char *default_ttyname; +static char *default_ttytype; +static char *default_lc_ctype; +static char *default_lc_messages; + +/* Name of a config file, which will be reread on a HUP if it is not NULL. */ +static char *config_filename; + + +/* Local prototypes. */ +static void create_directories (void); +#ifdef USE_GNU_PTH +static void handle_connections (int listen_fd); +#endif + + + +static const char * +my_strusage (int level) +{ + const char *p; + switch (level) + { + case 11: p = "gpg-agent (GnuPG)"; + break; + case 13: p = VERSION; break; + case 17: p = PRINTABLE_OS_NAME; break; + case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n"); + break; + case 1: + case 40: p = _("Usage: gpg-agent [options] (-h for help)"); + break; + case 41: p = _("Syntax: gpg-agent [options] [command [args]]\n" + "Secret key management for GnuPG\n"); + break; + + default: p = NULL; + } + return p; +} + + + +static void +i18n_init (void) +{ +#ifdef USE_SIMPLE_GETTEXT + set_gettext_file( PACKAGE ); +#else +#ifdef ENABLE_NLS + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); +#endif +#endif +} + + + +/* Used by gcry for logging */ +static void +my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr) +{ + /* translate the log levels */ + switch (level) + { + case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break; + case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break; + case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break; + case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break; + case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break; + case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break; + case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break; + default: level = JNLIB_LOG_ERROR; break; + } + log_logv (level, fmt, arg_ptr); +} + + +static void +cleanup (void) +{ + if (*socket_name) + { + char *p; + + remove (socket_name); + p = strrchr (socket_name, '/'); + if (p) + { + *p = 0; + rmdir (socket_name); + *p = '/'; + } + *socket_name = 0; + } +} + + +static RETSIGTYPE +cleanup_sh (int sig) +{ + if (caught_fatal_sig) + raise (sig); + caught_fatal_sig = 1; + + /* gcry_control( GCRYCTL_TERM_SECMEM );*/ + cleanup (); + +#ifndef HAVE_DOSISH_SYSTEM + { /* reset action to default action and raise signal again */ + struct sigaction nact; + nact.sa_handler = SIG_DFL; + sigemptyset( &nact.sa_mask ); + nact.sa_flags = 0; + sigaction( sig, &nact, NULL); + } +#endif + raise( sig ); +} + + +/* Handle options which are allowed to be reset after program start. + Return true when the current option in PARGS could be handled and + false if not. As a special feature, passing a value of NULL for + PARGS, resets the options to the default. */ +static int +parse_rereadable_options (ARGPARSE_ARGS *pargs) +{ + if (!pargs) + { /* reset mode */ + opt.quiet = 0; + opt.verbose = 0; + opt.debug = 0; + opt.no_grab = 0; + opt.pinentry_program = NULL; + opt.scdaemon_program = NULL; + opt.def_cache_ttl = 10*60; /* default to 10 minutes */ + opt.ignore_cache_for_signing = 0; + return 1; + } + + switch (pargs->r_opt) + { + case oQuiet: opt.quiet = 1; break; + case oVerbose: opt.verbose++; break; + + case oDebug: opt.debug |= pargs->r.ret_ulong; break; + case oDebugAll: opt.debug = ~0; break; + + case oNoGrab: opt.no_grab = 1; break; + + case oPinentryProgram: opt.pinentry_program = pargs->r.ret_str; break; + case oScdaemonProgram: opt.scdaemon_program = pargs->r.ret_str; break; + + case oDefCacheTTL: opt.def_cache_ttl = pargs->r.ret_ulong; break; + + case oIgnoreCacheForSigning: opt.ignore_cache_for_signing = 1; break; + + default: + return 0; /* not handled */ + } + return 1; /* handled */ +} + + +int +main (int argc, char **argv ) +{ + ARGPARSE_ARGS pargs; + int orig_argc; + int may_coredump; + char **orig_argv; + FILE *configfp = NULL; + char *configname = NULL; + const char *shell; + unsigned configlineno; + int parse_debug = 0; + int default_config =1; + int greeting = 0; + int nogreeting = 0; + int pipe_server = 0; + int is_daemon = 0; + int nodetach = 0; + int csh_style = 0; + char *logfile = NULL; + int debug_wait = 0; + int disable_pth = 0; + + set_strusage (my_strusage); + gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); + /* Please note that we may running SUID(ROOT), so be very CAREFUL + when adding any stuff between here and the call to INIT_SECMEM() + somewhere after the option parsing */ + log_set_prefix ("gpg-agent", 1|4); + i18n_init (); + + /* We need to initialize Pth before libgcrypt, because the libgcrypt + initialization done by gcry_check_version internally sets up its + mutex system. Note that one must not link against pth if + USE_GNU_PTH is not defined. */ +#ifdef USE_GNU_PTH + if (!pth_init ()) + { + log_error ("failed to initialize the Pth library\n"); + exit (1); + } +#endif /*USE_GNU_PTH*/ + + /* check that the libraries are suitable. Do it here because + the option parsing may need services of the library */ + if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) ) + { + log_fatal( _("libgcrypt is too old (need %s, have %s)\n"), + NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) ); + } + + assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free); + + gcry_set_log_handler (my_gcry_logger, NULL); + gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); + + may_coredump = disable_core_dumps (); + + parse_rereadable_options (NULL); /* Reset them to default values. */ + + shell = getenv ("SHELL"); + if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") ) + csh_style = 1; + + opt.homedir = getenv("GNUPGHOME"); + if (!opt.homedir || !*opt.homedir) + opt.homedir = GNUPG_DEFAULT_HOMEDIR; + + + /* check whether we have a config file on the commandline */ + orig_argc = argc; + orig_argv = argv; + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags= 1|(1<<6); /* do not remove the args, ignore version */ + while (arg_parse( &pargs, opts)) + { + if (pargs.r_opt == oDebug || pargs.r_opt == oDebugAll) + parse_debug++; + else if (pargs.r_opt == oOptions) + { /* yes there is one, so we do not try the default one, but + read the option file when it is encountered at the + commandline */ + default_config = 0; + } + else if (pargs.r_opt == oNoOptions) + default_config = 0; /* --no-options */ + else if (pargs.r_opt == oHomedir) + opt.homedir = pargs.r.ret_str; + } + + /* initialize the secure memory. */ + gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); + maybe_setuid = 0; + + /* + Now we are now working under our real uid + */ + + + if (default_config) + configname = make_filename (opt.homedir, "gpg-agent.conf", NULL ); + + argc = orig_argc; + argv = orig_argv; + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags= 1; /* do not remove the args */ + next_pass: + if (configname) + { + configlineno = 0; + configfp = fopen (configname, "r"); + if (!configfp) + { + if (default_config) + { + if( parse_debug ) + log_info (_("NOTE: no default option file `%s'\n"), + configname ); + } + else + { + log_error (_("option file `%s': %s\n"), + configname, strerror(errno) ); + exit(2); + } + xfree (configname); + configname = NULL; + } + if (parse_debug && configname ) + log_info (_("reading options from `%s'\n"), configname ); + default_config = 0; + } + + while (optfile_parse( configfp, configname, &configlineno, &pargs, opts) ) + { + if (parse_rereadable_options (&pargs)) + continue; /* Already handled */ + switch (pargs.r_opt) + { + case oBatch: opt.batch=1; break; + + case oDebugWait: debug_wait = pargs.r.ret_int; break; + + case oOptions: + /* config files may not be nested (silently ignore them) */ + if (!configfp) + { + xfree(configname); + configname = xstrdup(pargs.r.ret_str); + goto next_pass; + } + break; + case oNoGreeting: nogreeting = 1; break; + case oNoVerbose: opt.verbose = 0; break; + case oNoOptions: break; /* no-options */ + case oHomedir: opt.homedir = pargs.r.ret_str; break; + case oNoDetach: nodetach = 1; break; + case oLogFile: logfile = pargs.r.ret_str; break; + case oCsh: csh_style = 1; break; + case oSh: csh_style = 0; break; + case oServer: pipe_server = 1; break; + case oDaemon: is_daemon = 1; break; + case oDisablePth: disable_pth = 1; break; + + case oDisplay: default_display = xstrdup (pargs.r.ret_str); break; + case oTTYname: default_ttyname = xstrdup (pargs.r.ret_str); break; + case oTTYtype: default_ttytype = xstrdup (pargs.r.ret_str); break; + case oLCctype: default_lc_ctype = xstrdup (pargs.r.ret_str); break; + case oLCmessages: default_lc_messages = xstrdup (pargs.r.ret_str); break; + + case oKeepTTY: opt.keep_tty = 1; break; + case oKeepDISPLAY: opt.keep_display = 1; break; + + default : pargs.err = configfp? 1:2; break; + } + } + if (configfp) + { + fclose( configfp ); + configfp = NULL; + /* Keep a copy of the name so that it can be read on SIGHUP. */ + config_filename = configname; + configname = NULL; + goto next_pass; + } + xfree (configname); + configname = NULL; + if (log_get_errorcount(0)) + exit(2); + if (nogreeting ) + greeting = 0; + + if (greeting) + { + fprintf (stderr, "%s %s; %s\n", + strusage(11), strusage(13), strusage(14) ); + fprintf (stderr, "%s\n", strusage(15) ); + } +#ifdef IS_DEVELOPMENT_VERSION + log_info ("NOTE: this is a development version!\n"); +#endif + + + if (atexit (cleanup)) + { + log_error ("atexit failed\n"); + cleanup (); + exit (1); + } + + create_directories (); + + if (debug_wait && pipe_server) + { + log_debug ("waiting for debugger - my pid is %u .....\n", + (unsigned int)getpid()); + sleep (debug_wait); + log_debug ("... okay\n"); + } + + if (!pipe_server && !is_daemon) + log_info (_("please use the option `--daemon'" + " to run the program in the background\n")); + +#ifdef ENABLE_NLS + /* gpg-agent usdually does not ooutput any messages becuase it runs + in the background. For log files it is acceptable to have + messages always encoded in utf-8. We switch here to utf-8, so + that commands like --help still give native messages. It is far + easier to swicthnonly once instead of for every message and it + actually helps when more then one thread is active (avoids + required an extra copy step). */ + bind_textdomain_codeset (PACKAGE, "UTF-8"); +#endif + + /* now start with logging to a file if this is desired */ + if (logfile) + { + log_set_file (logfile); + log_set_prefix (NULL, 1|2|4); + } + + /* Make sure that we have a default ttyname. */ + if (!default_ttyname && ttyname (1)) + default_ttyname = xstrdup (ttyname (1)); + if (!default_ttytype && getenv ("TERM")) + default_ttytype = xstrdup (getenv ("TERM")); + + if (pipe_server) + { /* this is the simple pipe based server */ + start_command_handler (-1, -1); + } + else if (!is_daemon) + ; + else + { /* regular server mode */ + int fd; + pid_t pid; + int len; + struct sockaddr_un serv_addr; + char *p; + + /* Remove the DISPLAY variable so that a pinentry does not + default to a specific display. There is still a default + display when gpg-agent weas started using --display or a + client requested this using an OPTION command. */ + if (!opt.keep_display) + unsetenv ("DISPLAY"); + + *socket_name = 0; + snprintf (socket_name, DIM(socket_name)-1, + "/tmp/gpg-XXXXXX/S.gpg-agent"); + socket_name[DIM(socket_name)-1] = 0; + p = strrchr (socket_name, '/'); + if (!p) + BUG (); + *p = 0;; + if (!mkdtemp(socket_name)) + { + log_error ("can't create directory `%s': %s\n", + socket_name, strerror(errno) ); + exit (1); + } + *p = '/'; + + if (strchr (socket_name, ':') ) + { + log_error ("colons are not allowed in the socket name\n"); + exit (1); + } + if (strlen (socket_name)+1 >= sizeof serv_addr.sun_path ) + { + log_error ("name of socket too long\n"); + exit (1); + } + + + fd = socket (AF_UNIX, SOCK_STREAM, 0); + if (fd == -1) + { + log_error ("can't create socket: %s\n", strerror(errno) ); + exit (1); + } + + memset (&serv_addr, 0, sizeof serv_addr); + serv_addr.sun_family = AF_UNIX; + strcpy (serv_addr.sun_path, socket_name); + len = (offsetof (struct sockaddr_un, sun_path) + + strlen(serv_addr.sun_path) + 1); + + if (bind (fd, (struct sockaddr*)&serv_addr, len) == -1) + { + log_error ("error binding socket to `%s': %s\n", + serv_addr.sun_path, strerror (errno) ); + close (fd); + exit (1); + } + + if (listen (fd, 5 ) == -1) + { + log_error ("listen() failed: %s\n", strerror (errno)); + close (fd); + exit (1); + } + + if (opt.verbose) + log_info ("listening on socket `%s'\n", socket_name ); + + + fflush (NULL); + pid = fork (); + if (pid == (pid_t)-1) + { + log_fatal ("fork failed: %s\n", strerror (errno) ); + exit (1); + } + else if (pid) + { /* we are the parent */ + char *infostr; + + close (fd); + + /* create the info string: :: */ + if (asprintf (&infostr, "GPG_AGENT_INFO=%s:%lu:1", + socket_name, (ulong)pid ) < 0) + { + log_error ("out of core\n"); + kill (pid, SIGTERM); + exit (1); + } + *socket_name = 0; /* don't let cleanup() remove the socket - + the child should do this from now on */ + if (argc) + { /* run the program given on the commandline */ + if (putenv (infostr)) + { + log_error ("failed to set environment: %s\n", + strerror (errno) ); + kill (pid, SIGTERM ); + exit (1); + } + execvp (argv[0], argv); + log_error ("failed to run the command: %s\n", strerror (errno)); + kill (pid, SIGTERM); + exit (1); + } + else + { + /* print the environment string, so that the caller can use + shell's eval to set it */ + if (csh_style) + { + *strchr (infostr, '=') = ' '; + printf ( "setenv %s\n", infostr); + } + else + { + printf ( "%s; export GPG_AGENT_INFO;\n", infostr); + } + free (infostr); + exit (0); + } + /*NEVER REACHED*/ + } /* end parent */ + + + /* this is the child */ + + /* detach from tty and put process into a new session */ + if (!nodetach ) + { + int i; + + /* close stdin, stdout and stderr unless it is the log stream */ + for (i=0; i <= 2; i++) + { + if ( log_get_fd () != i) + close (i); + } + if (setsid() == -1) + { + log_error ("setsid() failed: %s\n", strerror(errno) ); + cleanup (); + exit (1); + } + opt.running_detached = 1; + } + + if (chdir("/")) + { + log_error ("chdir to / failed: %s\n", strerror (errno)); + exit (1); + } + + +#ifdef USE_GNU_PTH + if (!disable_pth) + { + struct sigaction sa; + + sa.sa_handler = SIG_IGN; + sigemptyset (&sa.sa_mask); + sa.sa_flags = 0; + sigaction (SIGPIPE, &sa, NULL); + handle_connections (fd); + } + else +#endif /*!USE_GNU_PTH*/ + /* setup signals */ + { + struct sigaction oact, nact; + + nact.sa_handler = cleanup_sh; + sigemptyset (&nact.sa_mask); + nact.sa_flags = 0; + + sigaction (SIGHUP, NULL, &oact); + if (oact.sa_handler != SIG_IGN) + sigaction (SIGHUP, &nact, NULL); + sigaction( SIGTERM, NULL, &oact ); + if (oact.sa_handler != SIG_IGN) + sigaction (SIGTERM, &nact, NULL); + nact.sa_handler = SIG_IGN; + sigaction (SIGPIPE, &nact, NULL); + sigaction (SIGINT, &nact, NULL); + + start_command_handler (fd, -1); + } + close (fd); + } + + return 0; +} + +void +agent_exit (int rc) +{ + /*FIXME: update_random_seed_file();*/ +#if 1 + /* at this time a bit annoying */ + if (opt.debug & DBG_MEMSTAT_VALUE) + { + gcry_control( GCRYCTL_DUMP_MEMORY_STATS ); + gcry_control( GCRYCTL_DUMP_RANDOM_STATS ); + } + if (opt.debug) + gcry_control (GCRYCTL_DUMP_SECMEM_STATS ); +#endif + gcry_control (GCRYCTL_TERM_SECMEM ); + rc = rc? rc : log_get_errorcount(0)? 2 : 0; + exit (rc); +} + + +void +agent_init_default_ctrl (struct server_control_s *ctrl) +{ + /* Note we ignore malloc errors because we can't do much about it + and the request will fail anyway shortly after this + initialization. */ + if (ctrl->display) + free (ctrl->display); + ctrl->display = default_display? strdup (default_display) : NULL; + + if (ctrl->ttyname) + free (ctrl->ttyname); + ctrl->ttyname = default_ttyname? strdup (default_ttyname) : NULL; + + if (ctrl->ttytype) + free (ctrl->ttytype); + ctrl->ttytype = default_ttytype? strdup (default_ttytype) : NULL; + + if (ctrl->lc_ctype) + free (ctrl->lc_ctype); + ctrl->lc_ctype = default_lc_ctype? strdup (default_lc_ctype) : NULL; + + if (ctrl->lc_messages) + free (ctrl->lc_messages); + ctrl->lc_messages = default_lc_messages? strdup (default_lc_messages) : NULL; +} + + +/* Reread parts of the configuration. Note, that this function is + obviously not thread-safe and should only be called from the PTH + signal handler. + + Fixme: Due to the way the argument parsing works, we create a + memory leak here for all string type arguments. There is currently + no clean way to tell whether the memory for the argument has been + allocated or points into the process' original arguments. Unless + we have a mechanism to tell this, we need to live on with this. */ +static void +reread_configuration (void) +{ + ARGPARSE_ARGS pargs; + FILE *fp; + unsigned int configlineno = 0; + int dummy; + + if (!config_filename) + return; /* No config file. */ + + fp = fopen (config_filename, "r"); + if (!fp) + { + log_error (_("option file `%s': %s\n"), + config_filename, strerror(errno) ); + return; + } + + parse_rereadable_options (NULL); /* Start from the default values. */ + + memset (&pargs, 0, sizeof pargs); + dummy = 0; + pargs.argc = &dummy; + pargs.flags = 1; /* do not remove the args */ + while (optfile_parse (fp, config_filename, &configlineno, &pargs, opts) ) + { + if (pargs.r_opt < -1) + pargs.err = 1; /* Print a warning. */ + else /* Try to parse this option - ignore unchangeable ones. */ + parse_rereadable_options (&pargs); + } + fclose (fp); +} + + +static void +create_private_keys_directory (const char *home) +{ + char *fname; + struct stat statbuf; + + fname = make_filename (home, GNUPG_PRIVATE_KEYS_DIR, NULL); + if (stat (fname, &statbuf) && errno == ENOENT) + { + if (mkdir (fname, S_IRUSR|S_IWUSR|S_IXUSR )) + log_error (_("can't create directory `%s': %s\n"), + fname, strerror(errno) ); + else if (!opt.quiet) + log_info (_("directory `%s' created\n"), fname); + } + xfree (fname); +} + +/* Create the directory only if the supplied directory name is the + same as the default one. This way we avoid to create arbitrary + directories when a non-default home directory is used. To cope + with HOME, we compare only the suffix if we see that the default + homedir does start with a tilde. We don't stop here in case of + problems because other functions will throw an error anyway.*/ +static void +create_directories (void) +{ + struct stat statbuf; + const char *defhome = GNUPG_DEFAULT_HOMEDIR; + char *home; + + home = make_filename (opt.homedir, NULL); + if ( stat (home, &statbuf) ) + { + if (errno == ENOENT) + { + if ( (*defhome == '~' + && (strlen (home) >= strlen (defhome+1) + && !strcmp (home + strlen(home) + - strlen (defhome+1), defhome+1))) + || (*defhome != '~' && !strcmp (home, defhome) ) + ) + { + if (mkdir (home, S_IRUSR|S_IWUSR|S_IXUSR )) + log_error (_("can't create directory `%s': %s\n"), + home, strerror(errno) ); + else + { + if (!opt.quiet) + log_info (_("directory `%s' created\n"), home); + create_private_keys_directory (home); + } + } + } + else + log_error ("error stat-ing `%s': %s\n", home, strerror (errno)); + } + else if ( !S_ISDIR(statbuf.st_mode)) + { + log_error ("can't use `%s' as home directory\n", home); + } + else /* exists and is a directory. */ + { + create_private_keys_directory (home); + } + xfree (home); +} + + + +#ifdef USE_GNU_PTH +static void +handle_signal (int signo) +{ + switch (signo) + { + case SIGHUP: + log_info ("SIGHUP received - " + "re-reading configuration and flushing cache\n"); + agent_flush_cache (); + reread_configuration (); + break; + + case SIGUSR1: + if (opt.verbose < 5) + opt.verbose++; + log_info ("SIGUSR1 received - verbosity set to %d\n", opt.verbose); + break; + + case SIGUSR2: + if (opt.verbose) + opt.verbose--; + log_info ("SIGUSR2 received - verbosity set to %d\n", opt.verbose ); + break; + + case SIGTERM: + if (!shutdown_pending) + log_info ("SIGTERM received - shutting down ...\n"); + else + log_info ("SIGTERM received - still %ld running threads\n", + pth_ctrl( PTH_CTRL_GETTHREADS )); + shutdown_pending++; + if (shutdown_pending > 2) + { + log_info ("shutdown forced\n"); + log_info ("%s %s stopped\n", strusage(11), strusage(13) ); + cleanup (); + agent_exit (0); + } + break; + + case SIGINT: + log_info ("SIGINT received - immediate shutdown\n"); + log_info( "%s %s stopped\n", strusage(11), strusage(13)); + cleanup (); + agent_exit (0); + break; + + default: + log_info ("signal %d received - no action defined\n", signo); + } +} + + +static void * +start_connection_thread (void *arg) +{ + int fd = (int)arg; + + if (opt.verbose) + log_info ("handler for fd %d started\n", fd); + start_command_handler (-1, fd); + if (opt.verbose) + log_info ("handler for fd %d terminated\n", fd); + + return NULL; +} + + +static void +handle_connections (int listen_fd) +{ + pth_attr_t tattr; + pth_event_t ev; + sigset_t sigs; + int signo; + struct sockaddr_un paddr; + socklen_t plen = sizeof( paddr ); + int fd; + + tattr = pth_attr_new(); + pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0); + pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 32*1024); + pth_attr_set (tattr, PTH_ATTR_NAME, "gpg-agent"); + + sigemptyset (&sigs ); + sigaddset (&sigs, SIGHUP); + sigaddset (&sigs, SIGUSR1); + sigaddset (&sigs, SIGUSR2); + sigaddset (&sigs, SIGINT); + sigaddset (&sigs, SIGTERM); + ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo); + + for (;;) + { + if (shutdown_pending) + { + if (pth_ctrl (PTH_CTRL_GETTHREADS) == 1) + break; /* ready */ + + /* Do not accept anymore connections and wait for existing + connections to terminate */ + signo = 0; + pth_wait (ev); + if (pth_event_occurred (ev) && signo) + handle_signal (signo); + continue; + } + + fd = pth_accept_ev (listen_fd, (struct sockaddr *)&paddr, &plen, ev); + if (fd == -1) + { +#ifdef PTH_STATUS_OCCURRED /* This is Pth 2 */ + if (pth_event_status (ev) == PTH_STATUS_OCCURRED) +#else + if (pth_event_occurred (ev)) +#endif + { + handle_signal (signo); + continue; + } + log_error ("accept failed: %s - waiting 1s\n", strerror (errno)); + pth_sleep(1); + continue; + } + + if (!pth_spawn (tattr, start_connection_thread, (void*)fd)) + { + log_error ("error spawning connection handler: %s\n", + strerror (errno) ); + close (fd); + } + } + + pth_event_free (ev, PTH_FREE_ALL); + cleanup (); + log_info ("%s %s stopped\n", strusage(11), strusage(13)); +} +#endif /*USE_GNU_PTH*/ diff --git a/agent/learncard.c b/agent/learncard.c new file mode 100644 index 000000000..28a74f972 --- /dev/null +++ b/agent/learncard.c @@ -0,0 +1,448 @@ +/* learncard.c - Handle the LEARN command + * Copyright (C) 2002, 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 +#include +#include + +#include "agent.h" +#include + +struct keypair_info_s { + struct keypair_info_s *next; + int no_cert; + char *id; /* points into grip */ + char hexgrip[1]; +}; +typedef struct keypair_info_s *KEYPAIR_INFO; + +struct kpinfo_cb_parm_s { + int error; + KEYPAIR_INFO info; +}; + + +struct certinfo_s { + struct certinfo_s *next; + int type; + int done; + char id[1]; +}; +typedef struct certinfo_s *CERTINFO; + +struct certinfo_cb_parm_s { + int error; + CERTINFO info; +}; + + +struct sinfo_s { + struct sinfo_s *next; + char *data; /* Points into keyword. */ + char keyword[1]; +}; +typedef struct sinfo_s *SINFO; + +struct sinfo_cb_parm_s { + int error;; + SINFO info; +}; + + + +static void +release_keypair_info (KEYPAIR_INFO info) +{ + while (info) + { + KEYPAIR_INFO tmp = info->next; + xfree (info); + info = tmp; + } +} + +static void +release_certinfo (CERTINFO info) +{ + while (info) + { + CERTINFO tmp = info->next; + xfree (info); + info = tmp; + } +} + +static void +release_sinfo (SINFO info) +{ + while (info) + { + SINFO tmp = info->next; + xfree (info); + info = tmp; + } +} + + + +/* This callback is used by agent_card_learn and passed the content of + all KEYPAIRINFO lines. It merely stores this data away */ +static void +kpinfo_cb (void *opaque, const char *line) +{ + struct kpinfo_cb_parm_s *parm = opaque; + KEYPAIR_INFO item; + char *p; + + if (parm->error) + return; /* no need to gather data after an error coccured */ + item = xtrycalloc (1, sizeof *item + strlen (line)); + if (!item) + { + parm->error = out_of_core (); + return; + } + strcpy (item->hexgrip, line); + for (p = item->hexgrip; hexdigitp (p); p++) + ; + if (p == item->hexgrip && *p == 'X' && spacep (p+1)) + { + item->no_cert = 1; + p++; + } + else if ((p - item->hexgrip) != 40 || !spacep (p)) + { /* not a 20 byte hex keygrip or not followed by a space */ + parm->error = gpg_error (GPG_ERR_INV_RESPONSE); + xfree (item); + return; + } + *p++ = 0; + while (spacep (p)) + p++; + item->id = p; + while (*p && !spacep (p)) + p++; + if (p == item->id) + { /* invalid ID string */ + parm->error = gpg_error (GPG_ERR_INV_RESPONSE); + xfree (item); + return; + } + *p = 0; /* ignore trailing stuff */ + + /* store it */ + item->next = parm->info; + parm->info = item; +} + + +/* This callback is used by agent_card_learn and passed the content of + all CERTINFO lines. It merely stores this data away */ +static void +certinfo_cb (void *opaque, const char *line) +{ + struct certinfo_cb_parm_s *parm = opaque; + CERTINFO item; + int type; + char *p, *pend; + + if (parm->error) + return; /* no need to gather data after an error coccured */ + + type = strtol (line, &p, 10); + while (spacep (p)) + p++; + for (pend = p; *pend && !spacep (pend); pend++) + ; + if (p == pend || !*p) + { + parm->error = gpg_error (GPG_ERR_INV_RESPONSE); + return; + } + *pend = 0; /* ignore trailing stuff */ + + item = xtrycalloc (1, sizeof *item + strlen (p)); + if (!item) + { + parm->error = out_of_core (); + return; + } + item->type = type; + strcpy (item->id, p); + /* store it */ + item->next = parm->info; + parm->info = item; +} + + +/* This callback is used by agent_card_learn and passed the content of + all SINFO lines. It merely stores this data away */ +static void +sinfo_cb (void *opaque, const char *keyword, size_t keywordlen, + const char *data) +{ + struct sinfo_cb_parm_s *sparm = opaque; + SINFO item; + + if (sparm->error) + return; /* no need to gather data after an error coccured */ + + item = xtrycalloc (1, sizeof *item + keywordlen + 1 + strlen (data)); + if (!item) + { + sparm->error = out_of_core (); + return; + } + memcpy (item->keyword, keyword, keywordlen); + item->data = item->keyword + keywordlen; + *item->data = 0; + item->data++; + strcpy (item->data, data); + /* store it */ + item->next = sparm->info; + sparm->info = item; +} + + +/* Create an S-expression with the shadow info. */ +static unsigned char * +make_shadow_info (const char *serialno, const char *idstring) +{ + const char *s; + unsigned char *info, *p; + char numbuf[21]; + int n; + + for (s=serialno, n=0; *s && s[1]; s += 2) + n++; + + info = p = xtrymalloc (1 + 21 + n + + 21 + strlen (idstring) + 1 + 1); + *p++ = '('; + sprintf (numbuf, "%d:", n); + p = stpcpy (p, numbuf); + for (s=serialno; *s && s[1]; s += 2) + *p++ = xtoi_2 (s); + sprintf (numbuf, "%d:", strlen (idstring)); + p = stpcpy (p, numbuf); + p = stpcpy (p, idstring); + *p++ = ')'; + *p = 0; + return info; +} + +static int +send_cert_back (const char *id, void *assuan_context) +{ + int rc; + char *derbuf; + size_t derbuflen; + + rc = agent_card_readcert (id, &derbuf, &derbuflen); + if (rc) + { + log_error ("error reading certificate: %s\n", + gpg_strerror (rc)); + return rc; + } + + rc = assuan_send_data (assuan_context, derbuf, derbuflen); + xfree (derbuf); + if (!rc) + rc = assuan_send_data (assuan_context, NULL, 0); + if (!rc) + rc = assuan_write_line (assuan_context, "END"); + if (rc) + { + log_error ("sending certificate failed: %s\n", + assuan_strerror (rc)); + return map_assuan_err (rc); + } + return 0; +} + +/* Perform the learn operation. If ASSUAN_CONTEXT is not NULL all new + certificates are send via Assuan */ +int +agent_handle_learn (void *assuan_context) +{ + int rc; + struct kpinfo_cb_parm_s parm; + struct certinfo_cb_parm_s cparm; + struct sinfo_cb_parm_s sparm; + char *serialno = NULL; + KEYPAIR_INFO item; + SINFO sitem; + unsigned char grip[20]; + char *p; + int i; + static int certtype_list[] = { + 101, /* trusted */ + 102, /* useful */ + 100, /* regular */ + -1 /* end of list */ + }; + + + memset (&parm, 0, sizeof parm); + memset (&cparm, 0, sizeof cparm); + memset (&sparm, 0, sizeof sparm); + + /* Check whether a card is present and get the serial number */ + rc = agent_card_serialno (&serialno); + if (rc) + goto leave; + + /* now gather all the available info */ + rc = agent_card_learn (kpinfo_cb, &parm, certinfo_cb, &cparm, + sinfo_cb, &sparm); + if (!rc && (parm.error || cparm.error || sparm.error)) + rc = parm.error? parm.error : cparm.error? cparm.error : sparm.error; + if (rc) + { + log_debug ("agent_card_learn failed: %s\n", gpg_strerror (rc)); + goto leave; + } + + log_info ("card has S/N: %s\n", serialno); + + /* Pass on all the collected status information. */ + if (assuan_context) + { + for (sitem = sparm.info; sitem; sitem = sitem->next) + { + assuan_write_status (assuan_context, sitem->keyword, sitem->data); + } + } + + /* Write out the certificates in a standard order. */ + for (i=0; certtype_list[i] != -1; i++) + { + CERTINFO citem; + for (citem = cparm.info; citem; citem = citem->next) + { + if (certtype_list[i] != citem->type) + continue; + + if (opt.verbose) + log_info (" id: %s (type=%d)\n", + citem->id, citem->type); + + if (assuan_context) + { + rc = send_cert_back (citem->id, assuan_context); + if (rc) + goto leave; + citem->done = 1; + } + } + } + + for (item = parm.info; item; item = item->next) + { + unsigned char *pubkey, *shdkey; + size_t n; + + if (opt.verbose) + log_info (" id: %s (grip=%s)\n", item->id, item->hexgrip); + + if (item->no_cert) + continue; /* no public key yet available */ + + for (p=item->hexgrip, i=0; i < 20; p += 2, i++) + grip[i] = xtoi_2 (p); + + if (!agent_key_available (grip)) + continue; + + /* unknown - store it */ + rc = agent_card_readkey (item->id, &pubkey); + if (rc) + { + log_debug ("agent_card_readkey failed: %s\n", gpg_strerror (rc)); + goto leave; + } + + { + unsigned char *shadow_info = make_shadow_info (serialno, item->id); + if (!shadow_info) + { + rc = gpg_error (GPG_ERR_ENOMEM); + xfree (pubkey); + goto leave; + } + rc = agent_shadow_key (pubkey, shadow_info, &shdkey); + xfree (shadow_info); + } + xfree (pubkey); + if (rc) + { + log_error ("shadowing the key failed: %s\n", gpg_strerror (rc)); + goto leave; + } + n = gcry_sexp_canon_len (shdkey, 0, NULL, NULL); + assert (n); + + rc = agent_write_private_key (grip, shdkey, n, 0); + xfree (shdkey); + if (rc) + { + log_error ("error writing key: %s\n", gpg_strerror (rc)); + goto leave; + } + + if (opt.verbose) + log_info ("stored\n"); + + if (assuan_context) + { + CERTINFO citem; + + /* only send the certificate if we have not done so before */ + for (citem = cparm.info; citem; citem = citem->next) + { + if (!strcmp (citem->id, item->id)) + break; + } + if (!citem) + { + rc = send_cert_back (item->id, assuan_context); + if (rc) + goto leave; + } + } + } + + + leave: + xfree (serialno); + release_keypair_info (parm.info); + release_certinfo (cparm.info); + release_sinfo (sparm.info); + return rc; +} + + diff --git a/agent/minip12.c b/agent/minip12.c new file mode 100644 index 000000000..255fef096 --- /dev/null +++ b/agent/minip12.c @@ -0,0 +1,1140 @@ +/* minip12.c - A minimal pkcs-12 implementation. + * Copyright (C) 2002, 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 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include + +#undef TEST + +#ifdef TEST +#include +#include +#include +#endif + +#include "../jnlib/logging.h" +#include "minip12.h" + +#ifndef DIM +#define DIM(v) (sizeof(v)/sizeof((v)[0])) +#endif + +enum +{ + UNIVERSAL = 0, + APPLICATION = 1, + CONTEXT = 2, + PRIVATE = 3 +}; + + +enum +{ + TAG_NONE = 0, + TAG_BOOLEAN = 1, + TAG_INTEGER = 2, + TAG_BIT_STRING = 3, + TAG_OCTET_STRING = 4, + TAG_NULL = 5, + TAG_OBJECT_ID = 6, + TAG_OBJECT_DESCRIPTOR = 7, + TAG_EXTERNAL = 8, + TAG_REAL = 9, + TAG_ENUMERATED = 10, + TAG_EMBEDDED_PDV = 11, + TAG_UTF8_STRING = 12, + TAG_REALTIVE_OID = 13, + TAG_SEQUENCE = 16, + TAG_SET = 17, + TAG_NUMERIC_STRING = 18, + TAG_PRINTABLE_STRING = 19, + TAG_TELETEX_STRING = 20, + TAG_VIDEOTEX_STRING = 21, + TAG_IA5_STRING = 22, + TAG_UTC_TIME = 23, + TAG_GENERALIZED_TIME = 24, + TAG_GRAPHIC_STRING = 25, + TAG_VISIBLE_STRING = 26, + TAG_GENERAL_STRING = 27, + TAG_UNIVERSAL_STRING = 28, + TAG_CHARACTER_STRING = 29, + TAG_BMP_STRING = 30 +}; + + +static unsigned char const oid_data[9] = { + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 }; +static unsigned char const oid_encryptedData[9] = { + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x06 }; +static unsigned char const oid_pkcs_12_pkcs_8ShroudedKeyBag[11] = { + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x0A, 0x01, 0x02 }; +static unsigned char const oid_pbeWithSHAAnd3_KeyTripleDES_CBC[10] = { + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x03 }; + +static unsigned char const oid_rsaEncryption[9] = { + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }; + + +static unsigned char const data_3desiter1024[30] = { + 0x30, 0x1C, 0x06, 0x0A, 0x2A, 0x86, 0x48, 0x86, + 0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x03, 0x30, 0x0E, + 0x04, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x02, 0x02, 0x04, 0x00 }; +#define DATA_3DESITER1024_SALT_OFF 18 + + +struct buffer_s +{ + unsigned char *buffer; + size_t length; +}; + + +struct tag_info +{ + int class; + int is_constructed; + unsigned long tag; + unsigned long length; /* length part of the TLV */ + int nhdr; + int ndef; /* It is an indefinite length */ +}; + + +/* Parse the buffer at the address BUFFER which is of SIZE and return + the tag and the length part from the TLV triplet. Update BUFFER + and SIZE on success. */ +static int +parse_tag (unsigned char const **buffer, size_t *size, struct tag_info *ti) +{ + int c; + unsigned long tag; + const unsigned char *buf = *buffer; + size_t length = *size; + + ti->length = 0; + ti->ndef = 0; + ti->nhdr = 0; + + /* Get the tag */ + if (!length) + return -1; /* premature eof */ + c = *buf++; length--; + ti->nhdr++; + + ti->class = (c & 0xc0) >> 6; + ti->is_constructed = !!(c & 0x20); + tag = c & 0x1f; + + if (tag == 0x1f) + { + tag = 0; + do + { + tag <<= 7; + if (!length) + return -1; /* premature eof */ + c = *buf++; length--; + ti->nhdr++; + tag |= c & 0x7f; + } + while (c & 0x80); + } + ti->tag = tag; + + /* Get the length */ + if (!length) + return -1; /* prematureeof */ + c = *buf++; length--; + ti->nhdr++; + + if ( !(c & 0x80) ) + ti->length = c; + else if (c == 0x80) + ti->ndef = 1; + else if (c == 0xff) + return -1; /* forbidden length value */ + else + { + unsigned long len = 0; + int count = c & 0x7f; + + for (; count; count--) + { + len <<= 8; + if (!length) + return -1; /* premature_eof */ + c = *buf++; length--; + ti->nhdr++; + len |= c & 0xff; + } + ti->length = len; + } + + if (ti->class == UNIVERSAL && !ti->tag) + ti->length = 0; + + if (ti->length > length) + return -1; /* data larger than buffer. */ + + *buffer = buf; + *size = length; + return 0; +} + + +static int +string_to_key (int id, char *salt, int iter, const char *pw, + int req_keylen, unsigned char *keybuf) +{ + int rc, i, j; + gcry_md_hd_t md; + gcry_mpi_t num_b1 = NULL; + int pwlen; + unsigned char hash[20], buf_b[64], buf_i[128], *p; + size_t cur_keylen; + size_t n; + + cur_keylen = 0; + pwlen = strlen (pw); + if (pwlen > 63/2) + { + log_error ("password too long\n"); + return -1; + } + + /* Store salt and password in BUF_I */ + p = buf_i; + for(i=0; i < 64; i++) + *p++ = salt [i%8]; + for(i=j=0; i < 64; i += 2) + { + *p++ = 0; + *p++ = pw[j]; + if (++j > pwlen) /* Note, that we include the trailing zero */ + j = 0; + } + + for (;;) + { + rc = gcry_md_open (&md, GCRY_MD_SHA1, 0); + if (rc) + { + log_error ( "gcry_md_open failed: %s\n", gpg_strerror (rc)); + return rc; + } + for(i=0; i < 64; i++) + gcry_md_putc (md, id); + gcry_md_write (md, buf_i, 128); + memcpy (hash, gcry_md_read (md, 0), 20); + gcry_md_close (md); + for (i=1; i < iter; i++) + gcry_md_hash_buffer (GCRY_MD_SHA1, hash, hash, 20); + + for (i=0; i < 20 && cur_keylen < req_keylen; i++) + keybuf[cur_keylen++] = hash[i]; + if (cur_keylen == req_keylen) + { + gcry_mpi_release (num_b1); + return 0; /* ready */ + } + + /* need more bytes. */ + for(i=0; i < 64; i++) + buf_b[i] = hash[i % 20]; + rc = gcry_mpi_scan (&num_b1, GCRYMPI_FMT_USG, buf_b, 64, &n); + if (rc) + { + log_error ( "gcry_mpi_scan failed: %s\n", gpg_strerror (rc)); + return -1; + } + gcry_mpi_add_ui (num_b1, num_b1, 1); + for (i=0; i < 128; i += 64) + { + gcry_mpi_t num_ij; + + rc = gcry_mpi_scan (&num_ij, GCRYMPI_FMT_USG, buf_i + i, 64, &n); + if (rc) + { + log_error ( "gcry_mpi_scan failed: %s\n", + gpg_strerror (rc)); + return -1; + } + gcry_mpi_add (num_ij, num_ij, num_b1); + gcry_mpi_clear_highbit (num_ij, 64*8); + rc = gcry_mpi_print (GCRYMPI_FMT_USG, buf_i + i, 64, &n, num_ij); + if (rc) + { + log_error ( "gcry_mpi_print failed: %s\n", + gpg_strerror (rc)); + return -1; + } + gcry_mpi_release (num_ij); + } + } +} + + +static int +set_key_iv (gcry_cipher_hd_t chd, char *salt, int iter, const char *pw) +{ + unsigned char keybuf[24]; + int rc; + + if (string_to_key (1, salt, iter, pw, 24, keybuf)) + return -1; + rc = gcry_cipher_setkey (chd, keybuf, 24); + if (rc) + { + log_error ( "gcry_cipher_setkey failed: %s\n", gpg_strerror (rc)); + return -1; + } + + if (string_to_key (2, salt, iter, pw, 8, keybuf)) + return -1; + rc = gcry_cipher_setiv (chd, keybuf, 8); + if (rc) + { + log_error ("gcry_cipher_setiv failed: %s\n", gpg_strerror (rc)); + return -1; + } + return 0; +} + + +static void +crypt_block (unsigned char *buffer, size_t length, char *salt, int iter, + const char *pw, int encrypt) +{ + gcry_cipher_hd_t chd; + int rc; + + rc = gcry_cipher_open (&chd, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC, 0); + if (rc) + { + log_error ( "gcry_cipher_open failed: %s\n", gpg_strerror(-1)); + return; + } + if (set_key_iv (chd, salt, iter, pw)) + goto leave; + + rc = encrypt? gcry_cipher_encrypt (chd, buffer, length, NULL, 0) + : gcry_cipher_decrypt (chd, buffer, length, NULL, 0); + + if (rc) + { + log_error ( "en/de-crytion failed: %s\n", gpg_strerror (rc)); + goto leave; + } + +/* { */ +/* FILE *fp = fopen("inner.der", "wb"); */ +/* fwrite (buffer, 1, length, fp); */ +/* fclose (fp); */ +/* } */ + + leave: + gcry_cipher_close (chd); +} + + + + +static int +parse_bag_encrypted_data (const unsigned char *buffer, size_t length, + int startoffset) +{ + struct tag_info ti; + const unsigned char *p = buffer; + size_t n = length; + const char *where; + + where = "start"; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class != CONTEXT || ti.tag) + goto bailout; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.tag != TAG_SEQUENCE) + goto bailout; + + where = "bag.encryptedData.version"; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.tag != TAG_INTEGER || ti.length != 1 || *p != 0) + goto bailout; + p++; n--; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.tag != TAG_SEQUENCE) + goto bailout; + + where = "bag.encryptedData.data"; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.tag != TAG_OBJECT_ID || ti.length != DIM(oid_data) + || memcmp (p, oid_data, DIM(oid_data))) + goto bailout; + p += DIM(oid_data); + n -= DIM(oid_data); + + /* fixme: continue parsing */ + + return 0; + bailout: + log_error ("encrptedData error at \"%s\", offset %u\n", + where, (p - buffer)+startoffset); + return -1; +} + +static gcry_mpi_t * +parse_bag_data (const unsigned char *buffer, size_t length, int startoffset, + const char *pw) +{ + int rc; + struct tag_info ti; + const unsigned char *p = buffer; + size_t n = length; + const char *where; + char salt[8]; + unsigned int iter; + int len; + unsigned char *plain = NULL; + gcry_mpi_t *result = NULL; + int result_count, i; + + where = "start"; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class != CONTEXT || ti.tag) + goto bailout; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class || ti.tag != TAG_OCTET_STRING) + goto bailout; + + where = "data.outerseqs"; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class || ti.tag != TAG_SEQUENCE) + goto bailout; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class || ti.tag != TAG_SEQUENCE) + goto bailout; + + where = "data.objectidentifier"; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class || ti.tag != TAG_OBJECT_ID + || ti.length != DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag) + || memcmp (p, oid_pkcs_12_pkcs_8ShroudedKeyBag, + DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag))) + goto bailout; + p += DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag); + n -= DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag); + + where = "shrouded,outerseqs"; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class != CONTEXT || ti.tag) + goto bailout; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class || ti.tag != TAG_SEQUENCE) + goto bailout; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class || ti.tag != TAG_SEQUENCE) + goto bailout; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class || ti.tag != TAG_OBJECT_ID + || ti.length != DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC) + || memcmp (p, oid_pbeWithSHAAnd3_KeyTripleDES_CBC, + DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC))) + goto bailout; + p += DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC); + n -= DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC); + + where = "3des-params"; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class || ti.tag != TAG_SEQUENCE) + goto bailout; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class || ti.tag != TAG_OCTET_STRING || ti.length != 8 ) + goto bailout; + memcpy (salt, p, 8); + p += 8; + n -= 8; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class || ti.tag != TAG_INTEGER || !ti.length ) + goto bailout; + for (iter=0; ti.length; ti.length--) + { + iter <<= 8; + iter |= (*p++) & 0xff; + n--; + } + + where = "3des-ciphertext"; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class || ti.tag != TAG_OCTET_STRING || !ti.length ) + goto bailout; + + log_info ("%lu bytes of 3DES encrypted text\n", ti.length); + + plain = gcry_malloc_secure (ti.length); + if (!plain) + { + log_error ("error allocating decryption buffer\n"); + goto bailout; + } + memcpy (plain, p, ti.length); + crypt_block (plain, ti.length, salt, iter, pw, 0); + n = ti.length; + startoffset = 0; + buffer = p = plain; + + where = "decrypted-text"; + if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) + goto bailout; + if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER + || ti.length != 1 || *p) + goto bailout; + p++; n--; + if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) + goto bailout; + len = ti.length; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (len < ti.nhdr) + goto bailout; + len -= ti.nhdr; + if (ti.class || ti.tag != TAG_OBJECT_ID + || ti.length != DIM(oid_rsaEncryption) + || memcmp (p, oid_rsaEncryption, + DIM(oid_rsaEncryption))) + goto bailout; + p += DIM (oid_rsaEncryption); + n -= DIM (oid_rsaEncryption); + if (len < ti.length) + goto bailout; + len -= ti.length; + if (n < len) + goto bailout; + p += len; + n -= len; + if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_OCTET_STRING) + goto bailout; + if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) + goto bailout; + len = ti.length; + + result = gcry_calloc (10, sizeof *result); + if (!result) + { + log_error ( "error allocating result array\n"); + goto bailout; + } + result_count = 0; + + where = "reading.key-parameters"; + for (result_count=0; len && result_count < 9;) + { + if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER) + goto bailout; + if (len < ti.nhdr) + goto bailout; + len -= ti.nhdr; + if (len < ti.length) + goto bailout; + len -= ti.length; + if (!result_count && ti.length == 1 && !*p) + ; /* ignore the very first one if it is a 0 */ + else + { + rc = gcry_mpi_scan (result+result_count, GCRYMPI_FMT_USG, p, + ti.length, NULL); + if (rc) + { + log_error ("error parsing key parameter: %s\n", + gpg_strerror (rc)); + goto bailout; + } + result_count++; + } + p += ti.length; + n -= ti.length; + } + if (len) + goto bailout; + + return result; + + bailout: + gcry_free (plain); + if (result) + { + for (i=0; result[i]; i++) + gcry_mpi_release (result[i]); + gcry_free (result); + } + log_error ( "data error at \"%s\", offset %u\n", + where, (p - buffer) + startoffset); + return NULL; +} + + +/* Parse a PKCS12 object and return an array of MPI representing the + secret key parameters. This is a very limited inplementation in + that it is only able to look for 3DES encoded enctyptedData and + tries to extract the first private key object it finds. In case of + an error NULL is returned. */ +gcry_mpi_t * +p12_parse (const unsigned char *buffer, size_t length, const char *pw) +{ + struct tag_info ti; + const unsigned char *p = buffer; + size_t n = length; + const char *where; + int bagseqlength, len; + + where = "pfx"; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.tag != TAG_SEQUENCE) + goto bailout; + + where = "pfxVersion"; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.tag != TAG_INTEGER || ti.length != 1 || *p != 3) + goto bailout; + p++; n--; + + where = "authSave"; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.tag != TAG_SEQUENCE) + goto bailout; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.tag != TAG_OBJECT_ID || ti.length != DIM(oid_data) + || memcmp (p, oid_data, DIM(oid_data))) + goto bailout; + p += DIM(oid_data); + n -= DIM(oid_data); + + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class != CONTEXT || ti.tag) + goto bailout; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class != UNIVERSAL || ti.tag != TAG_OCTET_STRING) + goto bailout; + + where = "bags"; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class != UNIVERSAL || ti.tag != TAG_SEQUENCE) + goto bailout; + bagseqlength = ti.length; + while (bagseqlength) + { + /*log_debug ( "at offset %u\n", (p - buffer));*/ + where = "bag-sequence"; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class != UNIVERSAL || ti.tag != TAG_SEQUENCE) + goto bailout; + + if (bagseqlength < ti.nhdr) + goto bailout; + bagseqlength -= ti.nhdr; + if (bagseqlength < ti.length) + goto bailout; + bagseqlength -= ti.length; + len = ti.length; + + if (parse_tag (&p, &n, &ti)) + goto bailout; + len -= ti.nhdr; + if (ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_encryptedData) + && !memcmp (p, oid_encryptedData, DIM(oid_encryptedData))) + { + p += DIM(oid_encryptedData); + n -= DIM(oid_encryptedData); + len -= DIM(oid_encryptedData); + where = "bag.encryptedData"; + if (parse_bag_encrypted_data (p, n, (p - buffer))) + goto bailout; + } + else if (ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_data) + && !memcmp (p, oid_data, DIM(oid_data))) + { + p += DIM(oid_data); + n -= DIM(oid_data); + len -= DIM(oid_data); + return parse_bag_data (p, n, (p-buffer), pw); + } + else + log_info ( "unknown bag type - skipped\n"); + + if (len < 0 || len > n) + goto bailout; + p += len; + n -= len; + } + + return NULL; + bailout: + log_error ("error at \"%s\", offset %u\n", where, (p - buffer)); + return NULL; +} + + + +static size_t +compute_tag_length (size_t n) +{ + int needed = 0; + + if (n < 128) + needed += 2; /* tag and one length byte */ + else if (n < 256) + needed += 3; /* tag, number of length bytes, 1 length byte */ + else if (n < 65536) + needed += 4; /* tag, number of length bytes, 2 length bytes */ + else + { + log_error ("object too larger to encode\n"); + return 0; + } + return needed; +} + +static unsigned char * +store_tag_length (unsigned char *p, int tag, size_t n) +{ + if (tag == TAG_SEQUENCE) + tag |= 0x20; /* constructed */ + + *p++ = tag; + if (n < 128) + *p++ = n; + else if (n < 256) + { + *p++ = 0x81; + *p++ = n; + } + else if (n < 65536) + { + *p++ = 0x82; + *p++ = n >> 8; + *p++ = n; + } + + return p; +} + + +/* Create the final PKCS-12 object from the sequences contained in + SEQLIST. That array is terminated with an NULL object */ +static unsigned char * +create_final (struct buffer_s *sequences, size_t *r_length) +{ + int i; + size_t needed = 0; + size_t n, outseqlen, notsooutseqlen, out0taglen, octstrlen, inseqlen; + unsigned char *result, *p; + size_t resultlen; + + for (i=0; sequences[i].buffer; i++) + needed += sequences[i].length; + /* This goes into a sequences. */ + inseqlen = needed; + n = compute_tag_length (needed); + needed += n; + /* And encapsulate all in an octet string. */ + octstrlen = needed; + n = compute_tag_length (needed); + needed += n; + /* And tag it with [0]. */ + out0taglen = needed; + n = compute_tag_length (needed); + needed += n; + /* Prepend an data OID. */ + needed += 2 + DIM (oid_data); + /* This all into a sequences. */ + notsooutseqlen = needed; + n = compute_tag_length (needed); + needed += n; + /* Prepend the version integer 3. */ + needed += 3; + /* And the final sequence. */ + outseqlen = needed; + n = compute_tag_length (needed); + needed += n; + + result = gcry_malloc (needed); + if (!result) + { + log_error ("error allocating buffer\n"); + return NULL; + } + p = result; + + /* Store the very outer sequence. */ + p = store_tag_length (p, TAG_SEQUENCE, outseqlen); + /* Store the version integer 3. */ + *p++ = TAG_INTEGER; + *p++ = 1; + *p++ = 3; + /* Store another sequence. */ + p = store_tag_length (p, TAG_SEQUENCE, notsooutseqlen); + /* Store the data OID. */ + p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data)); + memcpy (p, oid_data, DIM (oid_data)); + p += DIM (oid_data); + /* Next comes a context tag. */ + p = store_tag_length (p, 0xa0, out0taglen); + /* And an octet string. */ + p = store_tag_length (p, TAG_OCTET_STRING, octstrlen); + /* And the inner sequence. */ + p = store_tag_length (p, TAG_SEQUENCE, inseqlen); + /* And append all the buffers. */ + for (i=0; sequences[i].buffer; i++) + { + memcpy (p, sequences[i].buffer, sequences[i].length); + p += sequences[i].length; + } + + /* Ready. */ + resultlen = p - result; + if (needed != resultlen) + log_debug ("length mismatch: %u, %u\n", needed, resultlen); + + *r_length = resultlen; + return result; +} + + +/* Expect the RSA key parameters in KPARMS and a password in + PW. Create a PKCS structure from it and return it as well as the + length in R_LENGTH; return NULL in case of an error. */ +unsigned char * +p12_build (gcry_mpi_t *kparms, const char *pw, size_t *r_length) +{ + int rc, i; + size_t needed, n; + unsigned char *plain, *p, *cipher; + size_t plainlen, cipherlen; + size_t outseqlen, oidseqlen, octstrlen, inseqlen; + size_t out0taglen, in0taglen, outoctstrlen; + size_t aseq1len, aseq2len, aseq3len; + char salt[8]; + + needed = 3; /* The version(?) integer of value 0. */ + for (i=0; kparms[i]; i++) + { + n = 0; + rc = gcry_mpi_print (GCRYMPI_FMT_STD, NULL, 0, &n, kparms[i]); + if (rc) + { + log_error ("error formatting parameter: %s\n", gpg_strerror (rc)); + return NULL; + } + needed += n; + n = compute_tag_length (n); + if (!n) + return NULL; + needed += n; + } + if (i != 8) + { + log_error ("invalid paramters for p12_build\n"); + return NULL; + } + /* Now this all goes into a sequence. */ + inseqlen = needed; + n = compute_tag_length (needed); + if (!n) + return NULL; + needed += n; + /* Encapsulate all into an octet string. */ + octstrlen = needed; + n = compute_tag_length (needed); + if (!n) + return NULL; + needed += n; + /* Prepend the object identifier sequence. */ + oidseqlen = 2 + DIM (oid_rsaEncryption) + 2; + needed += 2 + oidseqlen; + /* The version number. */ + needed += 3; + /* And finally put the whole thing into a sequence. */ + outseqlen = needed; + n = compute_tag_length (needed); + if (!n) + return NULL; + needed += n; + + /* allocate 8 extra bytes for padding */ + plain = gcry_malloc_secure (needed+8); + if (!plain) + { + log_error ("error allocating encryption buffer\n"); + return NULL; + } + + /* And now fill the plaintext buffer. */ + p = plain; + p = store_tag_length (p, TAG_SEQUENCE, outseqlen); + /* Store version. */ + *p++ = TAG_INTEGER; + *p++ = 1; + *p++ = 0; + /* Store object identifier sequence. */ + p = store_tag_length (p, TAG_SEQUENCE, oidseqlen); + p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_rsaEncryption)); + memcpy (p, oid_rsaEncryption, DIM (oid_rsaEncryption)); + p += DIM (oid_rsaEncryption); + *p++ = TAG_NULL; + *p++ = 0; + /* Start with the octet string. */ + p = store_tag_length (p, TAG_OCTET_STRING, octstrlen); + p = store_tag_length (p, TAG_SEQUENCE, inseqlen); + /* Store the key parameters. */ + *p++ = TAG_INTEGER; + *p++ = 1; + *p++ = 0; + for (i=0; kparms[i]; i++) + { + n = 0; + rc = gcry_mpi_print (GCRYMPI_FMT_STD, NULL, 0, &n, kparms[i]); + if (rc) + { + log_error ("oops: error formatting parameter: %s\n", + gpg_strerror (rc)); + gcry_free (plain); + return NULL; + } + p = store_tag_length (p, TAG_INTEGER, n); + + n = plain + needed - p; + rc = gcry_mpi_print (GCRYMPI_FMT_STD, p, n, &n, kparms[i]); + if (rc) + { + log_error ("oops: error storing parameter: %s\n", + gpg_strerror (rc)); + gcry_free (plain); + return NULL; + } + p += n; + } + + plainlen = p - plain; + assert (needed == plainlen); + /* Append some pad characters; we already allocated extra space. */ + n = 8 - plainlen % 8; + for (;(plainlen % 8); plainlen++) + *p++ = n; + + { + FILE *fp = fopen("inner-out.der", "wb"); + fwrite (plain, 1, plainlen, fp); + fclose (fp); + } + + + /* Encrypt it and prepend a lot of stupid things. */ + gcry_randomize (salt, 8, GCRY_STRONG_RANDOM); + crypt_block (plain, plainlen, salt, 1024, pw, 1); + /* the data goes into an octet string. */ + needed = compute_tag_length (plainlen); + needed += plainlen; + /* we prepend the the algorithm identifier (we use a pre-encoded one)*/ + needed += DIM (data_3desiter1024); + /* we put a sequence around. */ + aseq3len = needed; + needed += compute_tag_length (needed); + /* Prepend it with a [0] tag. */ + in0taglen = needed; + needed += compute_tag_length (needed); + /* Prepend that shroudedKeyBag OID. */ + needed += 2 + DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag); + /* Put it all into two sequence. */ + aseq2len = needed; + needed += compute_tag_length ( needed); + aseq1len = needed; + needed += compute_tag_length (needed); + /* This all goes into an octet string. */ + outoctstrlen = needed; + needed += compute_tag_length (needed); + /* Prepend it with a [0] tag. */ + out0taglen = needed; + needed += compute_tag_length (needed); + /* Prepend the data OID. */ + needed += 2 + DIM (oid_data); + /* And a sequence. */ + outseqlen = needed; + needed += compute_tag_length (needed); + + cipher = gcry_malloc (needed); + if (!cipher) + { + log_error ("error allocating buffer\n"); + gcry_free (plain); + return NULL; + } + p = cipher; + /* Store the first sequence. */ + p = store_tag_length (p, TAG_SEQUENCE, outseqlen); + /* Store the data OID. */ + p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data)); + memcpy (p, oid_data, DIM (oid_data)); + p += DIM (oid_data); + /* Next comes a context tag. */ + p = store_tag_length (p, 0xa0, out0taglen); + /* And an octet string. */ + p = store_tag_length (p, TAG_OCTET_STRING, outoctstrlen); + /* Two sequences. */ + p = store_tag_length (p, TAG_SEQUENCE, aseq1len); + p = store_tag_length (p, TAG_SEQUENCE, aseq2len); + /* Store the shroudedKeyBag OID. */ + p = store_tag_length (p, TAG_OBJECT_ID, + DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag)); + memcpy (p, oid_pkcs_12_pkcs_8ShroudedKeyBag, + DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag)); + p += DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag); + /* Next comes a context tag. */ + p = store_tag_length (p, 0xa0, in0taglen); + /* And a sequence. */ + p = store_tag_length (p, TAG_SEQUENCE, aseq3len); + /* Now for the pre-encoded algorithm indentifier and the salt. */ + memcpy (p, data_3desiter1024, DIM (data_3desiter1024)); + memcpy (p + DATA_3DESITER1024_SALT_OFF, salt, 8); + p += DIM (data_3desiter1024); + /* And finally the octet string with the encrypted data. */ + p = store_tag_length (p, TAG_OCTET_STRING, plainlen); + memcpy (p, plain, plainlen); + p += plainlen; + cipherlen = p - cipher; + + if (needed != cipherlen) + log_debug ("length mismatch: %u, %u\n", needed, cipherlen); + gcry_free (plain); + + { + struct buffer_s seqlist[2]; + + seqlist[0].buffer = cipher; + seqlist[0].length = cipherlen; + seqlist[1].buffer = NULL; + seqlist[1].length = 0; + + cipher = create_final (seqlist, &cipherlen); + gcry_free (seqlist[0].buffer); + } + + *r_length = cipherlen; + return cipher; +} + + +#ifdef TEST +int +main (int argc, char **argv) +{ + FILE *fp; + struct stat st; + char *buf; + size_t buflen; + GcryMPI *result; + + if (argc != 3) + { + fprintf (stderr, "usage: testp12 file passphrase\n"); + return 1; + } + + gcry_control (GCRYCTL_DISABLE_SECMEM, NULL); + gcry_control (GCRYCTL_INITIALIZATION_FINISHED, NULL); + + fp = fopen (argv[1], "rb"); + if (!fp) + { + fprintf (stderr, "can't open `%s': %s\n", argv[1], strerror (errno)); + return 1; + } + + if (fstat (fileno(fp), &st)) + { + fprintf (stderr, "can't stat `%s': %s\n", argv[1], strerror (errno)); + return 1; + } + + buflen = st.st_size; + buf = gcry_malloc (buflen+1); + if (!buf || fread (buf, buflen, 1, fp) != 1) + { + fprintf (stderr, "error reading `%s': %s\n", argv[1], strerror (errno)); + return 1; + } + fclose (fp); + + result = p12_parse (buf, buflen, argv[2]); + if (result) + { + int i, rc; + char *buf; + + for (i=0; result[i]; i++) + { + rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, (void**)&buf, + NULL, result[i]); + if (rc) + printf ("%d: [error printing number: %s]\n", + i, gpg_strerror (rc)); + else + { + printf ("%d: %s\n", i, buf); + gcry_free (buf); + } + } + } + + return 0; + +} +#endif /* TEST */ diff --git a/agent/minip12.h b/agent/minip12.h new file mode 100644 index 000000000..122215549 --- /dev/null +++ b/agent/minip12.h @@ -0,0 +1,33 @@ +/* minip12.h - Global definitions for the minimal pkcs-12 implementation. + * Copyright (C) 2002, 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 MINIP12_H +#define MINIP12_H + +#include + +gcry_mpi_t *p12_parse (const unsigned char *buffer, size_t length, + const char *pw); + +unsigned char *p12_build (gcry_mpi_t *kparms, const char *pw, + size_t *r_length); + + +#endif /*MINIP12_H*/ diff --git a/agent/pkdecrypt.c b/agent/pkdecrypt.c new file mode 100644 index 000000000..543a82737 --- /dev/null +++ b/agent/pkdecrypt.c @@ -0,0 +1,138 @@ +/* pkdecrypt.c - public key decryption (well, acually using a secret key) + * Copyright (C) 2001, 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 +#include +#include + +#include "agent.h" + + +/* DECRYPT the stuff in ciphertext which is expected to be a S-Exp. + Try to get the key from CTRL and write the decoded stuff back to + OUTFP. */ +int +agent_pkdecrypt (CTRL ctrl, const char *ciphertext, size_t ciphertextlen, + FILE *outfp) +{ + gcry_sexp_t s_skey = NULL, s_cipher = NULL, s_plain = NULL; + unsigned char *shadow_info = NULL; + int rc; + char *buf = NULL; + size_t len; + + if (!ctrl->have_keygrip) + { + log_error ("speculative decryption not yet supported\n"); + rc = gpg_error (GPG_ERR_NO_SECKEY); + goto leave; + } + + rc = gcry_sexp_sscan (&s_cipher, NULL, ciphertext, ciphertextlen); + if (rc) + { + log_error ("failed to convert ciphertext: %s\n", gpg_strerror (rc)); + rc = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + + if (DBG_CRYPTO) + { + log_printhex ("keygrip:", ctrl->keygrip, 20); + log_printhex ("cipher: ", ciphertext, ciphertextlen); + } + s_skey = agent_key_from_file (ctrl, ctrl->keygrip, &shadow_info, 0); + if (!s_skey && !shadow_info) + { + log_error ("failed to read the secret key\n"); + rc = gpg_error (GPG_ERR_NO_SECKEY); + goto leave; + } + + if (!s_skey) + { /* divert operation to the smartcard */ + + if (!gcry_sexp_canon_len (ciphertext, ciphertextlen, NULL, NULL)) + { + rc = gpg_error (GPG_ERR_INV_SEXP); + goto leave; + } + + rc = divert_pkdecrypt (ctrl, ciphertext, shadow_info, &buf, &len ); + if (rc) + { + log_error ("smartcard decryption failed: %s\n", gpg_strerror (rc)); + goto leave; + } + /* FIXME: don't use buffering and change the protocol to return + a complete S-expression and not just a part. */ + fprintf (outfp, "%u:", (unsigned int)len); + fwrite (buf, 1, len, outfp); + putc (0, outfp); + } + else + { /* no smartcard, but a private key */ + if (DBG_CRYPTO) + { + log_debug ("skey: "); + gcry_sexp_dump (s_skey); + } + + rc = gcry_pk_decrypt (&s_plain, s_cipher, s_skey); + if (rc) + { + log_error ("decryption failed: %s\n", gpg_strerror (rc)); + rc = map_gcry_err (rc); + goto leave; + } + + if (DBG_CRYPTO) + { + log_debug ("plain: "); + gcry_sexp_dump (s_plain); + } + len = gcry_sexp_sprint (s_plain, GCRYSEXP_FMT_CANON, NULL, 0); + assert (len); + buf = xmalloc (len); + len = gcry_sexp_sprint (s_plain, GCRYSEXP_FMT_CANON, buf, len); + assert (len); + /* FIXME: we must make sure that no buffering takes place or we are + in full control of the buffer memory (easy to do) - should go + into assuan. */ + fwrite (buf, 1, len, outfp); + } + + + leave: + gcry_sexp_release (s_skey); + gcry_sexp_release (s_plain); + gcry_sexp_release (s_cipher); + xfree (buf); + xfree (shadow_info); + return rc; +} + + diff --git a/agent/pksign.c b/agent/pksign.c new file mode 100644 index 000000000..fba2c652c --- /dev/null +++ b/agent/pksign.c @@ -0,0 +1,185 @@ +/* pksign.c - public key signing (well, acually using a secret key) + * Copyright (C) 2001, 2002, 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 +#include +#include + +#include "agent.h" + + +static int +do_encode_md (const unsigned char *digest, size_t digestlen, int algo, + unsigned int nbits, gcry_mpi_t *r_val) +{ + int nframe = (nbits+7) / 8; + byte *frame; + int i, n; + byte asn[100]; + size_t asnlen; + + asnlen = DIM(asn); + if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen)) + { + log_error ("no object identifier for algo %d\n", algo); + return gpg_error (GPG_ERR_INTERNAL); + } + + if (digestlen + asnlen + 4 > nframe ) + { + log_error ("can't encode a %d bit MD into a %d bits frame\n", + (int)(digestlen*8), (int)nbits); + return gpg_error (GPG_ERR_INTERNAL); + } + + /* We encode the MD in this way: + * + * 0 1 PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes) + * + * PAD consists of FF bytes. + */ + frame = xtrymalloc (nframe); + if (!frame) + return out_of_core (); + n = 0; + frame[n++] = 0; + frame[n++] = 1; /* block type */ + i = nframe - digestlen - asnlen -3 ; + assert ( i > 1 ); + memset ( frame+n, 0xff, i ); n += i; + frame[n++] = 0; + memcpy ( frame+n, asn, asnlen ); n += asnlen; + memcpy ( frame+n, digest, digestlen ); n += digestlen; + assert ( n == nframe ); + if (DBG_CRYPTO) + log_printhex ("encoded hash:", frame, nframe); + + gcry_mpi_scan (r_val, GCRYMPI_FMT_USG, frame, n, &nframe); + xfree (frame); + return 0; +} + + +/* SIGN whatever information we have accumulated in CTRL and write it + back to OUTFP. */ +int +agent_pksign (CTRL ctrl, FILE *outfp, int ignore_cache) +{ + gcry_sexp_t s_skey = NULL, s_hash = NULL, s_sig = NULL; + gcry_mpi_t frame = NULL; + unsigned char *shadow_info = NULL; + int rc; + char *buf = NULL; + size_t len; + + if (!ctrl->have_keygrip) + return gpg_error (GPG_ERR_NO_SECKEY); + + s_skey = agent_key_from_file (ctrl, + ctrl->keygrip, &shadow_info, ignore_cache); + if (!s_skey && !shadow_info) + { + log_error ("failed to read the secret key\n"); + rc = gpg_error (GPG_ERR_NO_SECKEY); + goto leave; + } + + if (!s_skey) + { /* divert operation to the smartcard */ + unsigned char *sigbuf; + + rc = divert_pksign (ctrl, + ctrl->digest.value, + ctrl->digest.valuelen, + ctrl->digest.algo, + shadow_info, &sigbuf); + if (rc) + { + log_error ("smartcard signing failed: %s\n", gpg_strerror (rc)); + goto leave; + } + len = gcry_sexp_canon_len (sigbuf, 0, NULL, NULL); + assert (len); + buf = sigbuf; + } + else + { /* no smartcard, but a private key */ + + /* put the hash into a sexp */ + rc = do_encode_md (ctrl->digest.value, + ctrl->digest.valuelen, + ctrl->digest.algo, + gcry_pk_get_nbits (s_skey), + &frame); + if (rc) + goto leave; + if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) ) + BUG (); + + if (DBG_CRYPTO) + { + log_debug ("skey: "); + gcry_sexp_dump (s_skey); + } + + /* sign */ + rc = gcry_pk_sign (&s_sig, s_hash, s_skey); + if (rc) + { + log_error ("signing failed: %s\n", gpg_strerror (rc)); + rc = map_gcry_err (rc); + goto leave; + } + + if (DBG_CRYPTO) + { + log_debug ("result: "); + gcry_sexp_dump (s_sig); + } + + len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, NULL, 0); + assert (len); + buf = xmalloc (len); + len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, buf, len); + assert (len); + } + + /* FIXME: we must make sure that no buffering takes place or we are + in full control of the buffer memory (easy to do) - should go + into assuan. */ + fwrite (buf, 1, len, outfp); + + leave: + gcry_sexp_release (s_skey); + gcry_sexp_release (s_hash); + gcry_sexp_release (s_sig); + gcry_mpi_release (frame); + xfree (buf); + xfree (shadow_info); + return rc; +} + + diff --git a/agent/protect-tool.c b/agent/protect-tool.c new file mode 100644 index 000000000..e518c5672 --- /dev/null +++ b/agent/protect-tool.c @@ -0,0 +1,977 @@ +/* protect-tool.c - A tool to test the secret key protection + * Copyright (C) 2002, 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 +#include +#include +#include + +#define JNLIB_NEED_LOG_LOGV +#include "agent.h" +#include "minip12.h" +#include "simple-pwquery.h" +#include "i18n.h" + +enum cmd_and_opt_values +{ aNull = 0, + oVerbose = 'v', + oArmor = 'a', + oPassphrase = 'P', + + oProtect = 'p', + oUnprotect = 'u', + + oNoVerbose = 500, + oShadow, + oShowShadowInfo, + oShowKeygrip, + + oP12Import, + oP12Export, + oStore, + oForce, + +aTest }; + +struct rsa_secret_key_s + { + gcry_mpi_t n; /* public modulus */ + gcry_mpi_t e; /* public exponent */ + gcry_mpi_t d; /* exponent */ + gcry_mpi_t p; /* prime p. */ + gcry_mpi_t q; /* prime q. */ + gcry_mpi_t u; /* inverse of p mod q. */ + }; + + +static int opt_armor; +static int opt_store; +static int opt_force; +static const char *passphrase; + +static const char *get_passphrase (void); +static int store_private_key (const unsigned char *grip, + const void *buffer, size_t length, int force); + + +static ARGPARSE_OPTS opts[] = { + + { 301, NULL, 0, N_("@Options:\n ") }, + + { oVerbose, "verbose", 0, "verbose" }, + { oArmor, "armor", 0, "write output in advanced format" }, + { oPassphrase, "passphrase", 2, "|STRING|use passphrase STRING" }, + { oProtect, "protect", 256, "protect a private key"}, + { oUnprotect, "unprotect", 256, "unprotect a private key"}, + { oShadow, "shadow", 256, "create a shadow entry for a priblic key"}, + { oShowShadowInfo, "show-shadow-info", 256, "return the shadow info"}, + { oShowKeygrip, "show-keygrip", 256, "show the \"keygrip\""}, + + { oP12Import, "p12-import", 256, "import a PKCS-12 encoded private key"}, + { oP12Export, "p12-export", 256, "export a private key PKCS-12 encoded"}, + { oStore, "store", 0, "store the created key in the appropriate place"}, + { oForce, "force", 0, "force overwriting"}, + {0} +}; + +static const char * +my_strusage (int level) +{ + const char *p; + switch (level) + { + case 11: p = "gpg-protect-tool (GnuPG)"; + break; + case 13: p = VERSION; break; + case 17: p = PRINTABLE_OS_NAME; break; + case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n"); + break; + case 1: + case 40: p = _("Usage: gpg-protect-tool [options] (-h for help)\n"); + break; + case 41: p = _("Syntax: gpg-protect-tool [options] [args]]\n" + "Secret key maintenance tool\n"); + break; + + default: p = NULL; + } + return p; +} + + + +static void +i18n_init (void) +{ +#ifdef USE_SIMPLE_GETTEXT + set_gettext_file( PACKAGE ); +#else +#ifdef ENABLE_NLS + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); +#endif +#endif +} + + + +/* Used by gcry for logging */ +static void +my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr) +{ + /* translate the log levels */ + switch (level) + { + case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break; + case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break; + case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break; + case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break; + case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break; + case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break; + case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break; + default: level = JNLIB_LOG_ERROR; break; } + log_logv (level, fmt, arg_ptr); +} + + +/* static void */ +/* print_mpi (const char *text, gcry_mpi_t a) */ +/* { */ +/* char *buf; */ +/* void *bufaddr = &buf; */ +/* int rc; */ + +/* rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, bufaddr, NULL, a); */ +/* if (rc) */ +/* log_info ("%s: [error printing number: %s]\n", text, gpg_strerror (rc)); */ +/* else */ +/* { */ +/* log_info ("%s: %s\n", text, buf); */ +/* gcry_free (buf); */ +/* } */ +/* } */ + + + +static unsigned char * +make_canonical (const char *fname, const char *buf, size_t buflen) +{ + int rc; + size_t erroff, len; + gcry_sexp_t sexp; + unsigned char *result; + + rc = gcry_sexp_sscan (&sexp, &erroff, buf, buflen); + if (rc) + { + log_error ("invalid S-Expression in `%s' (off=%u): %s\n", + fname, (unsigned int)erroff, gpg_strerror (rc)); + return NULL; + } + len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, NULL, 0); + assert (len); + result = xmalloc (len); + len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, result, len); + assert (len); + gcry_sexp_release (sexp); + return result; +} + +static char * +make_advanced (const unsigned char *buf, size_t buflen) +{ + int rc; + size_t erroff, len; + gcry_sexp_t sexp; + unsigned char *result; + + rc = gcry_sexp_sscan (&sexp, &erroff, buf, buflen); + if (rc) + { + log_error ("invalid canonical S-Expression (off=%u): %s\n", + (unsigned int)erroff, gpg_strerror (rc)); + return NULL; + } + len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0); + assert (len); + result = xmalloc (len); + len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len); + assert (len); + gcry_sexp_release (sexp); + return result; +} + + +static char * +read_file (const char *fname, size_t *r_length) +{ + FILE *fp; + struct stat st; + char *buf; + size_t buflen; + + fp = fopen (fname, "rb"); + if (!fp) + { + log_error ("can't open `%s': %s\n", fname, strerror (errno)); + return NULL; + } + + if (fstat (fileno(fp), &st)) + { + log_error ("can't stat `%s': %s\n", fname, strerror (errno)); + fclose (fp); + return NULL; + } + + buflen = st.st_size; + buf = xmalloc (buflen+1); + if (fread (buf, buflen, 1, fp) != 1) + { + log_error ("error reading `%s': %s\n", fname, strerror (errno)); + fclose (fp); + xfree (buf); + return NULL; + } + fclose (fp); + + *r_length = buflen; + return buf; +} + + +static unsigned char * +read_key (const char *fname) +{ + char *buf; + size_t buflen; + unsigned char *key; + + buf = read_file (fname, &buflen); + if (!buf) + return NULL; + key = make_canonical (fname, buf, buflen); + xfree (buf); + return key; +} + + + +static void +read_and_protect (const char *fname) +{ + int rc; + unsigned char *key; + unsigned char *result; + size_t resultlen; + + key = read_key (fname); + if (!key) + return; + + rc = agent_protect (key, get_passphrase (), &result, &resultlen); + xfree (key); + if (rc) + { + log_error ("protecting the key failed: %s\n", gpg_strerror (rc)); + return; + } + + if (opt_armor) + { + char *p = make_advanced (result, resultlen); + xfree (result); + if (!p) + return; + result = p; + resultlen = strlen (p); + } + + fwrite (result, resultlen, 1, stdout); + xfree (result); +} + + +static void +read_and_unprotect (const char *fname) +{ + int rc; + unsigned char *key; + unsigned char *result; + size_t resultlen; + + key = read_key (fname); + if (!key) + return; + + rc = agent_unprotect (key, get_passphrase (), &result, &resultlen); + xfree (key); + if (rc) + { + log_error ("unprotecting the key failed: %s\n", gpg_strerror (rc)); + return; + } + + if (opt_armor) + { + char *p = make_advanced (result, resultlen); + xfree (result); + if (!p) + return; + result = p; + resultlen = strlen (p); + } + + fwrite (result, resultlen, 1, stdout); + xfree (result); +} + + + +static void +read_and_shadow (const char *fname) +{ + int rc; + unsigned char *key; + unsigned char *result; + size_t resultlen; + + key = read_key (fname); + if (!key) + return; + + rc = agent_shadow_key (key, "(8:313233342:43)", &result); + xfree (key); + if (rc) + { + log_error ("shadowing the key failed: %s\n", gpg_strerror (rc)); + return; + } + resultlen = gcry_sexp_canon_len (result, 0, NULL,NULL); + assert (resultlen); + + if (opt_armor) + { + char *p = make_advanced (result, resultlen); + xfree (result); + if (!p) + return; + result = p; + resultlen = strlen (p); + } + + fwrite (result, resultlen, 1, stdout); + xfree (result); +} + +static void +show_shadow_info (const char *fname) +{ + int rc; + unsigned char *key; + const unsigned char *info; + size_t infolen; + + key = read_key (fname); + if (!key) + return; + + rc = agent_get_shadow_info (key, &info); + xfree (key); + if (rc) + { + log_error ("get_shadow_info failed: %s\n", gpg_strerror (rc)); + return; + } + infolen = gcry_sexp_canon_len (info, 0, NULL,NULL); + assert (infolen); + + if (opt_armor) + { + char *p = make_advanced (info, infolen); + if (!p) + return; + fwrite (p, strlen (p), 1, stdout); + xfree (p); + } + else + fwrite (info, infolen, 1, stdout); +} + + +static void +show_file (const char *fname) +{ + unsigned char *key; + size_t keylen; + char *p; + + key = read_key (fname); + if (!key) + return; + + keylen = gcry_sexp_canon_len (key, 0, NULL,NULL); + assert (keylen); + + p = make_advanced (key, keylen); + xfree (key); + if (p) + { + fwrite (p, strlen (p), 1, stdout); + xfree (p); + } +} + +static void +show_keygrip (const char *fname) +{ + unsigned char *key; + gcry_sexp_t private; + unsigned char grip[20]; + int i; + + key = read_key (fname); + if (!key) + return; + + if (gcry_sexp_new (&private, key, 0, 0)) + { + log_error ("gcry_sexp_new failed\n"); + return; + } + xfree (key); + + if (!gcry_pk_get_keygrip (private, grip)) + { + log_error ("can't calculate keygrip\n"); + return; + } + gcry_sexp_release (private); + + for (i=0; i < 20; i++) + printf ("%02X", grip[i]); + putchar ('\n'); +} + + +static int +rsa_key_check (struct rsa_secret_key_s *skey) +{ + int err = 0; + gcry_mpi_t t = gcry_mpi_snew (0); + gcry_mpi_t t1 = gcry_mpi_snew (0); + gcry_mpi_t t2 = gcry_mpi_snew (0); + gcry_mpi_t phi = gcry_mpi_snew (0); + + /* check that n == p * q */ + gcry_mpi_mul (t, skey->p, skey->q); + if (gcry_mpi_cmp( t, skey->n) ) + { + log_error ("RSA oops: n != p * q\n"); + err++; + } + + /* check that p is less than q */ + if (gcry_mpi_cmp (skey->p, skey->q) > 0) + { + gcry_mpi_t tmp; + + log_info ("swapping secret primes\n"); + tmp = gcry_mpi_copy (skey->p); + gcry_mpi_set (skey->p, skey->q); + gcry_mpi_set (skey->q, tmp); + gcry_mpi_release (tmp); + /* and must recompute u of course */ + gcry_mpi_invm (skey->u, skey->p, skey->q); + } + + /* check that e divides neither p-1 nor q-1 */ + gcry_mpi_sub_ui (t, skey->p, 1 ); + gcry_mpi_div (NULL, t, t, skey->e, 0); + if (!gcry_mpi_cmp_ui( t, 0) ) + { + log_error ("RSA oops: e divides p-1\n"); + err++; + } + gcry_mpi_sub_ui (t, skey->q, 1); + gcry_mpi_div (NULL, t, t, skey->e, 0); + if (!gcry_mpi_cmp_ui( t, 0)) + { + log_info ( "RSA oops: e divides q-1\n" ); + err++; + } + + /* check that d is correct. */ + gcry_mpi_sub_ui (t1, skey->p, 1); + gcry_mpi_sub_ui (t2, skey->q, 1); + gcry_mpi_mul (phi, t1, t2); + gcry_mpi_invm (t, skey->e, phi); + if (gcry_mpi_cmp (t, skey->d)) + { /* no: try universal exponent. */ + gcry_mpi_gcd (t, t1, t2); + gcry_mpi_div (t, NULL, phi, t, 0); + gcry_mpi_invm (t, skey->e, t); + if (gcry_mpi_cmp (t, skey->d)) + { + log_error ("RSA oops: bad secret exponent\n"); + err++; + } + } + + /* check for correctness of u */ + gcry_mpi_invm (t, skey->p, skey->q); + if (gcry_mpi_cmp (t, skey->u)) + { + log_info ( "RSA oops: bad u parameter\n"); + err++; + } + + if (err) + log_info ("RSA secret key check failed\n"); + + gcry_mpi_release (t); + gcry_mpi_release (t1); + gcry_mpi_release (t2); + gcry_mpi_release (phi); + + return err? -1:0; +} + + +static void +import_p12_file (const char *fname) +{ + char *buf; + unsigned char *result; + size_t buflen, resultlen; + int i; + int rc; + gcry_mpi_t *kparms; + struct rsa_secret_key_s sk; + gcry_sexp_t s_key; + unsigned char *key; + unsigned char grip[20]; + + /* fixme: we should release some stuff on error */ + + buf = read_file (fname, &buflen); + if (!buf) + return; + + kparms = p12_parse (buf, buflen, get_passphrase ()); + xfree (buf); + if (!kparms) + { + log_error ("error parsing or decrypting the PKCS-1 file\n"); + return; + } + for (i=0; kparms[i]; i++) + ; + if (i != 8) + { + log_error ("invalid structure of private key\n"); + return; + } + + +/* print_mpi (" n", kparms[0]); */ +/* print_mpi (" e", kparms[1]); */ +/* print_mpi (" d", kparms[2]); */ +/* print_mpi (" p", kparms[3]); */ +/* print_mpi (" q", kparms[4]); */ +/* print_mpi ("dmp1", kparms[5]); */ +/* print_mpi ("dmq1", kparms[6]); */ +/* print_mpi (" u", kparms[7]); */ + + sk.n = kparms[0]; + sk.e = kparms[1]; + sk.d = kparms[2]; + sk.q = kparms[3]; + sk.p = kparms[4]; + sk.u = kparms[7]; + if (rsa_key_check (&sk)) + return; +/* print_mpi (" n", sk.n); */ +/* print_mpi (" e", sk.e); */ +/* print_mpi (" d", sk.d); */ +/* print_mpi (" p", sk.p); */ +/* print_mpi (" q", sk.q); */ +/* print_mpi (" u", sk.u); */ + + /* Create an S-expresion from the parameters. */ + rc = gcry_sexp_build (&s_key, NULL, + "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))", + sk.n, sk.e, sk.d, sk.p, sk.q, sk.u, NULL); + for (i=0; i < 8; i++) + gcry_mpi_release (kparms[i]); + gcry_free (kparms); + if (rc) + { + log_error ("failed to created S-expression from key: %s\n", + gpg_strerror (rc)); + return; + } + + /* Compute the keygrip. */ + if (!gcry_pk_get_keygrip (s_key, grip)) + { + log_error ("can't calculate keygrip\n"); + return; + } + log_info ("keygrip: "); + for (i=0; i < 20; i++) + log_printf ("%02X", grip[i]); + log_printf ("\n"); + + /* convert to canonical encoding */ + buflen = gcry_sexp_sprint (s_key, GCRYSEXP_FMT_CANON, NULL, 0); + assert (buflen); + key = gcry_xmalloc_secure (buflen); + buflen = gcry_sexp_sprint (s_key, GCRYSEXP_FMT_CANON, key, buflen); + assert (buflen); + gcry_sexp_release (s_key); + + + rc = agent_protect (key, get_passphrase (), &result, &resultlen); + xfree (key); + if (rc) + { + log_error ("protecting the key failed: %s\n", gpg_strerror (rc)); + return; + } + + if (opt_armor) + { + char *p = make_advanced (result, resultlen); + xfree (result); + if (!p) + return; + result = p; + resultlen = strlen (p); + } + + if (opt_store) + store_private_key (grip, result, resultlen, opt_force); + else + fwrite (result, resultlen, 1, stdout); + + xfree (result); +} + + + +static gcry_mpi_t * +sexp_to_kparms (gcry_sexp_t sexp) +{ + gcry_sexp_t list, l2; + const char *name; + const char *s; + size_t n; + int i, idx; + const char *elems; + gcry_mpi_t *array; + + list = gcry_sexp_find_token (sexp, "private-key", 0 ); + if(!list) + return NULL; + l2 = gcry_sexp_cadr (list); + gcry_sexp_release (list); + list = l2; + name = gcry_sexp_nth_data (list, 0, &n); + if(!name || n != 3 || memcmp (name, "rsa", 3)) + { + gcry_sexp_release (list); + return NULL; + } + + /* Parameter names used with RSA. */ + elems = "nedpqu"; + array = xcalloc (strlen(elems) + 1, sizeof *array); + for (idx=0, s=elems; *s; s++, idx++ ) + { + l2 = gcry_sexp_find_token (list, s, 1); + if (!l2) + { + for (i=0; i +#include +#include +#include +#include +#include +#include +#include +#include + +#include "agent.h" + +#include "sexp-parse.h" + +#define PROT_CIPHER GCRY_CIPHER_AES +#define PROT_CIPHER_STRING "aes" +#define PROT_CIPHER_KEYLEN (128/8) + + +/* A table containing the information needed to create a protected + private key */ +static struct { + const char *algo; + const char *parmlist; + int prot_from, prot_to; +} protect_info[] = { + { "rsa", "nedpqu", 2, 5 }, + { NULL } +}; + + +static int +hash_passphrase (const char *passphrase, int hashalgo, + int s2kmode, + const unsigned char *s2ksalt, unsigned long s2kcount, + unsigned char *key, size_t keylen); + + + +/* Calculate the MIC for a private key S-Exp. SHA1HASH should pint to + a 20 byte buffer. This function is suitable for any algorithms. */ +static int +calculate_mic (const unsigned char *plainkey, unsigned char *sha1hash) +{ + const unsigned char *hash_begin, *hash_end; + const unsigned char *s; + size_t n; + + s = plainkey; + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (!smatch (&s, n, "private-key")) + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + if (*s != '(') + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + hash_begin = s; + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + s += n; /* skip over the algorithm name */ + + while (*s == '(') + { + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + s += n; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + s += n; + if ( *s != ')' ) + return gpg_error (GPG_ERR_INV_SEXP); + s++; + } + if (*s != ')') + return gpg_error (GPG_ERR_INV_SEXP); + s++; + hash_end = s; + + gcry_md_hash_buffer (GCRY_MD_SHA1, sha1hash, + hash_begin, hash_end - hash_begin); + + return 0; +} + + + +/* Encrypt the parameter block starting at PROTBEGIN with length + PROTLEN using the utf8 encoded key PASSPHRASE and return the entire + encrypted block in RESULT or ereturn with an error code. SHA1HASH + is the 20 byte SHA-1 hash required for the integrity code. + + The parameter block is expected to be an incomplete S-Expression of + the form (example in advanced format): + + (d #046129F..[some bytes not shown]..81#) + (p #00e861b..[some bytes not shown]..f1#) + (q #00f7a7c..[some bytes not shown]..61#) + (u #304559a..[some bytes not shown]..9b#) + + the returned block is the S-Expression: + + (protected mode (parms) encrypted_octet_string) + +*/ +static int +do_encryption (const char *protbegin, size_t protlen, + const char *passphrase, const unsigned char *sha1hash, + unsigned char **result, size_t *resultlen) +{ + gcry_cipher_hd_t hd; + const char *modestr = "openpgp-s2k3-sha1-" PROT_CIPHER_STRING "-cbc"; + int blklen, enclen, outlen; + char *iv = NULL; + int rc; + char *outbuf = NULL; + char *p; + int saltpos, ivpos, encpos; + + rc = gcry_cipher_open (&hd, PROT_CIPHER, GCRY_CIPHER_MODE_CBC, + GCRY_CIPHER_SECURE); + if (rc) + return rc; + + + /* We need to work on a copy of the data because this makes it + easier to add the trailer and the padding and more important we + have to prefix the text with 2 parenthesis, so we have to + allocate enough space for: + + (()(4:hash4:sha120:)) + padding + + We always append a full block of random bytes as padding but + encrypt only what is needed for a full blocksize */ + blklen = gcry_cipher_get_algo_blklen (PROT_CIPHER); + outlen = 2 + protlen + 2 + 6 + 6 + 23 + 2 + blklen; + enclen = outlen/blklen * blklen; + outbuf = gcry_malloc_secure (outlen); + if (!outbuf) + rc = out_of_core (); + if (!rc) + { + /* allocate random bytes to be used as IV, padding and s2k salt*/ + iv = gcry_random_bytes (blklen*2+8, GCRY_WEAK_RANDOM); + if (!iv) + rc = gpg_error (GPG_ERR_ENOMEM); + else + rc = gcry_cipher_setiv (hd, iv, blklen); + } + if (!rc) + { + unsigned char *key; + size_t keylen = PROT_CIPHER_KEYLEN; + + key = gcry_malloc_secure (keylen); + if (!key) + rc = out_of_core (); + else + { + rc = hash_passphrase (passphrase, GCRY_MD_SHA1, + 3, iv+2*blklen, 96, key, keylen); + if (!rc) + rc = gcry_cipher_setkey (hd, key, keylen); + xfree (key); + } + } + if (!rc) + { + p = outbuf; + *p++ = '('; + *p++ = '('; + memcpy (p, protbegin, protlen); + p += protlen; + memcpy (p, ")(4:hash4:sha120:", 17); + p += 17; + memcpy (p, sha1hash, 20); + p += 20; + *p++ = ')'; + *p++ = ')'; + memcpy (p, iv+blklen, blklen); + p += blklen; + assert ( p - outbuf == outlen); + rc = gcry_cipher_encrypt (hd, outbuf, enclen, NULL, 0); + } + gcry_cipher_close (hd); + if (rc) + { + xfree (iv); + xfree (outbuf); + return rc; + } + + /* Now allocate the buffer we want to return. This is + + (protected openpgp-s2k3-sha1-aes-cbc + ((sha1 salt no_of_iterations) 16byte_iv) + encrypted_octet_string) + + in canoncical format of course. We use asprintf and %n modifier + and spaces as palceholders. */ + asprintf (&p, + "(9:protected%d:%s((4:sha18:%n_8bytes_2:96)%d:%n%*s)%d:%n%*s)", + (int)strlen (modestr), modestr, + &saltpos, + blklen, &ivpos, blklen, "", + enclen, &encpos, enclen, ""); + if (p) + { /* asprintf does not use our malloc system */ + char *psave = p; + p = xtrymalloc (strlen (psave)+1); + if (p) + strcpy (p, psave); + free (psave); + } + if (!p) + { + gpg_error_t tmperr = out_of_core (); + xfree (iv); + xfree (outbuf); + return tmperr; + } + *resultlen = strlen (p); + *result = p; + memcpy (p+saltpos, iv+2*blklen, 8); + memcpy (p+ivpos, iv, blklen); + memcpy (p+encpos, outbuf, enclen); + xfree (iv); + xfree (outbuf); + return 0; +} + + + +/* Protect the key encoded in canonical format in plainkey. We assume + a valid S-Exp here. */ +int +agent_protect (const unsigned char *plainkey, const char *passphrase, + unsigned char **result, size_t *resultlen) +{ + int rc; + const unsigned char *s; + const unsigned char *hash_begin, *hash_end; + const unsigned char *prot_begin, *prot_end, *real_end; + size_t n; + int c, infidx, i; + unsigned char hashvalue[20]; + unsigned char *protected; + size_t protectedlen; + int depth = 0; + unsigned char *p; + + s = plainkey; + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + depth++; + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (!smatch (&s, n, "private-key")) + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + if (*s != '(') + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + depth++; + hash_begin = s; + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + + for (infidx=0; protect_info[infidx].algo + && !smatch (&s, n, protect_info[infidx].algo); infidx++) + ; + if (!protect_info[infidx].algo) + return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + + prot_begin = prot_end = NULL; + for (i=0; (c=protect_info[infidx].parmlist[i]); i++) + { + if (i == protect_info[infidx].prot_from) + prot_begin = s; + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + depth++; + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (n != 1 || c != *s) + return gpg_error (GPG_ERR_INV_SEXP); + s += n; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + s +=n; /* skip value */ + if (*s != ')') + return gpg_error (GPG_ERR_INV_SEXP); + depth--; + if (i == protect_info[infidx].prot_to) + prot_end = s; + s++; + } + if (*s != ')' || !prot_begin || !prot_end ) + return gpg_error (GPG_ERR_INV_SEXP); + depth--; + hash_end = s; + s++; + /* skip to the end of the S-exp */ + assert (depth == 1); + rc = sskip (&s, &depth); + if (rc) + return rc; + assert (!depth); + real_end = s-1; + + gcry_md_hash_buffer (GCRY_MD_SHA1, hashvalue, + hash_begin, hash_end - hash_begin + 1); + + rc = do_encryption (prot_begin, prot_end - prot_begin + 1, + passphrase, hashvalue, + &protected, &protectedlen); + if (rc) + return rc; + + /* Now create the protected version of the key. Note that the 10 + extra bytes are for for the inserted "protected-" string (the + beginning of the plaintext reads: "((11:private-key(" ). */ + *resultlen = (10 + + (prot_begin-plainkey) + + protectedlen + + (real_end-prot_end)); + *result = p = xtrymalloc (*resultlen); + if (!p) + { + gpg_error_t tmperr = out_of_core (); + xfree (protected); + return tmperr; + } + memcpy (p, "(21:protected-", 14); + p += 14; + memcpy (p, plainkey+4, prot_begin - plainkey - 4); + p += prot_begin - plainkey - 4; + memcpy (p, protected, protectedlen); + p += protectedlen; + memcpy (p, prot_end+1, real_end - prot_end); + p += real_end - prot_end; + assert ( p - *result == *resultlen); + xfree (protected); + return 0; +} + + +/* Do the actual decryption and check the return list for consistency. */ +static int +do_decryption (const unsigned char *protected, size_t protectedlen, + const char *passphrase, + const unsigned char *s2ksalt, unsigned long s2kcount, + const unsigned char *iv, size_t ivlen, + unsigned char **result) +{ + int rc = 0; + int blklen; + gcry_cipher_hd_t hd; + unsigned char *outbuf; + size_t reallen; + + blklen = gcry_cipher_get_algo_blklen (PROT_CIPHER); + if (protectedlen < 4 || (protectedlen%blklen)) + return gpg_error (GPG_ERR_CORRUPTED_PROTECTION); + + rc = gcry_cipher_open (&hd, PROT_CIPHER, GCRY_CIPHER_MODE_CBC, + GCRY_CIPHER_SECURE); + if (rc) + return rc; + + outbuf = gcry_malloc_secure (protectedlen); + if (!outbuf) + rc = out_of_core (); + if (!rc) + rc = gcry_cipher_setiv (hd, iv, ivlen); + if (!rc) + { + unsigned char *key; + size_t keylen = PROT_CIPHER_KEYLEN; + + key = gcry_malloc_secure (keylen); + if (!key) + rc = out_of_core (); + else + { + rc = hash_passphrase (passphrase, GCRY_MD_SHA1, + 3, s2ksalt, s2kcount, key, keylen); + if (!rc) + rc = gcry_cipher_setkey (hd, key, keylen); + xfree (key); + } + } + if (!rc) + rc = gcry_cipher_decrypt (hd, outbuf, protectedlen, + protected, protectedlen); + gcry_cipher_close (hd); + if (rc) + { + xfree (outbuf); + return rc; + } + /* do a quick check first */ + if (*outbuf != '(' && outbuf[1] != '(') + { + xfree (outbuf); + return gpg_error (GPG_ERR_BAD_PASSPHRASE); + } + /* check that we have a consistent S-Exp */ + reallen = gcry_sexp_canon_len (outbuf, protectedlen, NULL, NULL); + if (!reallen || (reallen + blklen < protectedlen) ) + { + xfree (outbuf); + return gpg_error (GPG_ERR_BAD_PASSPHRASE); + } + *result = outbuf; + return 0; +} + + +/* Merge the parameter list contained in CLEARTEXT with the original + protect lists PROTECTEDKEY by replacing the list at REPLACEPOS. + Return the new list in RESULT and the MIC value in the 20 byte + buffer SHA1HASH. */ +static int +merge_lists (const unsigned char *protectedkey, + size_t replacepos, + const unsigned char *cleartext, + unsigned char *sha1hash, unsigned char **result) +{ + size_t n, newlistlen; + unsigned char *newlist, *p; + const unsigned char *s; + const unsigned char *startpos, *endpos; + int i, rc; + + if (replacepos < 26) + return gpg_error (GPG_ERR_BUG); + + /* Estimate the required size of the resulting list. We have a large + safety margin of >20 bytes (MIC hash from CLEARTEXT and the + removed "protected-" */ + newlistlen = gcry_sexp_canon_len (protectedkey, 0, NULL, NULL); + if (!newlistlen) + return gpg_error (GPG_ERR_BUG); + n = gcry_sexp_canon_len (cleartext, 0, NULL, NULL); + if (!n) + return gpg_error (GPG_ERR_BUG); + newlistlen += n; + newlist = gcry_malloc_secure (newlistlen); + if (!newlist) + return out_of_core (); + + /* Copy the initial segment */ + strcpy (newlist, "(11:private-key"); + p = newlist + 15; + memcpy (p, protectedkey+15+10, replacepos-15-10); + p += replacepos-15-10; + + /* copy the cleartext */ + s = cleartext; + if (*s != '(' && s[1] != '(') + return gpg_error (GPG_ERR_BUG); /*we already checked this */ + s += 2; + startpos = s; + while ( *s == '(' ) + { + s++; + n = snext (&s); + if (!n) + goto invalid_sexp; + s += n; + n = snext (&s); + if (!n) + goto invalid_sexp; + s += n; + if ( *s != ')' ) + goto invalid_sexp; + s++; + } + if ( *s != ')' ) + goto invalid_sexp; + endpos = s; + s++; + /* short intermezzo: Get the MIC */ + if (*s != '(') + goto invalid_sexp; + s++; + n = snext (&s); + if (!smatch (&s, n, "hash")) + goto invalid_sexp; + n = snext (&s); + if (!smatch (&s, n, "sha1")) + goto invalid_sexp; + n = snext (&s); + if (n != 20) + goto invalid_sexp; + memcpy (sha1hash, s, 20); + s += n; + if (*s != ')') + goto invalid_sexp; + /* end intermezzo */ + + /* append the parameter list */ + memcpy (p, startpos, endpos - startpos); + p += endpos - startpos; + + /* skip overt the protected list element in the original list */ + s = protectedkey + replacepos; + assert (*s == '('); + s++; + i = 1; + rc = sskip (&s, &i); + if (rc) + goto failure; + startpos = s; + i = 2; /* we are inside this level */ + rc = sskip (&s, &i); + if (rc) + goto failure; + assert (s[-1] == ')'); + endpos = s; /* one behind the end of the list */ + + /* append the rest */ + memcpy (p, startpos, endpos - startpos); + p += endpos - startpos; + + /* ready */ + *result = newlist; + return 0; + + failure: + xfree (newlist); + return rc; + + invalid_sexp: + xfree (newlist); + return gpg_error (GPG_ERR_INV_SEXP); +} + + + +/* Unprotect the key encoded in canonical format. We assume a valid + S-Exp here. */ +int +agent_unprotect (const unsigned char *protectedkey, const char *passphrase, + unsigned char **result, size_t *resultlen) +{ + int rc; + const unsigned char *s; + size_t n; + int infidx, i; + unsigned char sha1hash[20], sha1hash2[20]; + const unsigned char *s2ksalt; + unsigned long s2kcount; + const unsigned char *iv; + const unsigned char *prot_begin; + unsigned char *cleartext; + unsigned char *final; + + s = protectedkey; + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (!smatch (&s, n, "protected-private-key")) + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + if (*s != '(') + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + + for (infidx=0; protect_info[infidx].algo + && !smatch (&s, n, protect_info[infidx].algo); infidx++) + ; + if (!protect_info[infidx].algo) + return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + + /* now find the list with the protected information. Here is an + example for such a list: + (protected openpgp-s2k3-sha1-aes-cbc + ((sha1 ) ) + ) + */ + for (;;) + { + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + prot_begin = s; + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (smatch (&s, n, "protected")) + break; + s += n; + i = 1; + rc = sskip (&s, &i); + if (rc) + return rc; + } + /* found */ + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (!smatch (&s, n, "openpgp-s2k3-sha1-" PROT_CIPHER_STRING "-cbc")) + return gpg_error (GPG_ERR_UNSUPPORTED_PROTECTION); + if (*s != '(' || s[1] != '(') + return gpg_error (GPG_ERR_INV_SEXP); + s += 2; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (!smatch (&s, n, "sha1")) + return gpg_error (GPG_ERR_UNSUPPORTED_PROTECTION); + n = snext (&s); + if (n != 8) + return gpg_error (GPG_ERR_CORRUPTED_PROTECTION); + s2ksalt = s; + s += n; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_CORRUPTED_PROTECTION); + /* We expect a list close as next, so we can simply use strtoul() + here. We might want to check that we only have digits - but this + is nothing we should worry about */ + if (s[n] != ')' ) + return gpg_error (GPG_ERR_INV_SEXP); + s2kcount = strtoul (s, NULL, 10); + if (!s2kcount) + return gpg_error (GPG_ERR_CORRUPTED_PROTECTION); + s += n; + s++; /* skip list end */ + + n = snext (&s); + if (n != 16) /* Wrong blocksize for IV (we support ony aes-128) */ + return gpg_error (GPG_ERR_CORRUPTED_PROTECTION); + iv = s; + s += n; + if (*s != ')' ) + return gpg_error (GPG_ERR_INV_SEXP); + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + + rc = do_decryption (s, n, + passphrase, s2ksalt, s2kcount, + iv, 16, + &cleartext); + if (rc) + return rc; + + rc = merge_lists (protectedkey, prot_begin-protectedkey, cleartext, + sha1hash, &final); + xfree (cleartext); + if (rc) + return rc; + + rc = calculate_mic (final, sha1hash2); + if (!rc && memcmp (sha1hash, sha1hash2, 20)) + rc = gpg_error (GPG_ERR_CORRUPTED_PROTECTION); + if (rc) + { + xfree (final); + return rc; + } + + *result = final; + *resultlen = gcry_sexp_canon_len (final, 0, NULL, NULL); + return 0; +} + +/* Check the type of the private key, this is one of the constants: + PRIVATE_KEY_UNKNOWN if we can't figure out the type (this is the + value 0), PRIVATE_KEY_CLEAR for an unprotected private key. + PRIVATE_KEY_PROTECTED for an protected private key or + PRIVATE_KEY_SHADOWED for a sub key where the secret parts are stored + elsewhere. */ +int +agent_private_key_type (const unsigned char *privatekey) +{ + const unsigned char *s; + size_t n; + + s = privatekey; + if (*s != '(') + return PRIVATE_KEY_UNKNOWN; + s++; + n = snext (&s); + if (!n) + return PRIVATE_KEY_UNKNOWN; + if (smatch (&s, n, "protected-private-key")) + return PRIVATE_KEY_PROTECTED; + if (smatch (&s, n, "shadowed-private-key")) + return PRIVATE_KEY_SHADOWED; + if (smatch (&s, n, "private-key")) + return PRIVATE_KEY_CLEAR; + return PRIVATE_KEY_UNKNOWN; +} + + + +/* Transform a passphrase into a suitable key of length KEYLEN and + store this key in the caller provided buffer KEY. The caller must + provide an HASHALGO, a valid S2KMODE (see rfc-2440) and depending on + that mode an S2KSALT of 8 random bytes and an S2KCOUNT (a suitable + value is 96). + + Returns an error code on failure. */ +static int +hash_passphrase (const char *passphrase, int hashalgo, + int s2kmode, + const unsigned char *s2ksalt, + unsigned long s2kcount, + unsigned char *key, size_t keylen) +{ + int rc; + gcry_md_hd_t md; + int pass, i; + int used = 0; + int pwlen = strlen (passphrase); + + if ( (s2kmode != 0 && s2kmode != 1 && s2kmode != 3) + || !hashalgo || !keylen || !key || !passphrase) + return gpg_error (GPG_ERR_INV_VALUE); + if ((s2kmode == 1 ||s2kmode == 3) && !s2ksalt) + return gpg_error (GPG_ERR_INV_VALUE); + + rc = gcry_md_open (&md, hashalgo, GCRY_MD_FLAG_SECURE); + if (rc) + return rc; + + for (pass=0; used < keylen; pass++) + { + if (pass) + { + gcry_md_reset (md); + for (i=0; i < pass; i++) /* preset the hash context */ + gcry_md_putc (md, 0); + } + + if (s2kmode == 1 || s2kmode == 3) + { + int len2 = pwlen + 8; + unsigned long count = len2; + + if (s2kmode == 3) + { + count = (16ul + (s2kcount & 15)) << ((s2kcount >> 4) + 6); + if (count < len2) + count = len2; + } + + while (count > len2) + { + gcry_md_write (md, s2ksalt, 8); + gcry_md_write (md, passphrase, pwlen); + count -= len2; + } + if (count < 8) + gcry_md_write (md, s2ksalt, count); + else + { + gcry_md_write (md, s2ksalt, 8); + count -= 8; + gcry_md_write (md, passphrase, count); + } + } + else + gcry_md_write (md, passphrase, pwlen); + + gcry_md_final (md); + i = gcry_md_get_algo_dlen (hashalgo); + if (i > keylen - used) + i = keylen - used; + memcpy (key+used, gcry_md_read (md, hashalgo), i); + used += i; + } + gcry_md_close(md); + return 0; +} + + + +/* Create a shadow key from a public key. We use the shadow protocol + "ti-v1" and insert the S-expressionn SHADOW_INFO. The resulting + S-expression is returned in an allocated buffer RESULT will point + to. The input parameters are expected to be valid canonilized + S-expressions */ +int +agent_shadow_key (const unsigned char *pubkey, + const unsigned char *shadow_info, + unsigned char **result) +{ + const unsigned char *s; + const unsigned char *point; + size_t n; + int depth = 0; + unsigned char *p; + size_t pubkey_len = gcry_sexp_canon_len (pubkey, 0, NULL,NULL); + size_t shadow_info_len = gcry_sexp_canon_len (shadow_info, 0, NULL,NULL); + + if (!pubkey_len || !shadow_info_len) + return gpg_error (GPG_ERR_INV_VALUE); + s = pubkey; + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + depth++; + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (!smatch (&s, n, "public-key")) + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + if (*s != '(') + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + depth++; + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + s += n; /* skip over the algorithm name */ + + while (*s != ')') + { + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + depth++; + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + s += n; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + s +=n; /* skip value */ + if (*s != ')') + return gpg_error (GPG_ERR_INV_SEXP); + depth--; + s++; + } + point = s; /* insert right before the point */ + depth--; + s++; + assert (depth == 1); + + /* calculate required length by taking in account: the "shadowed-" + prefix, the "shadowed", "t1-v1" as well as some parenthesis */ + n = 12 + pubkey_len + 1 + 3+8 + 2+5 + shadow_info_len + 1; + *result = p = xtrymalloc (n); + if (!p) + return out_of_core (); + p = stpcpy (p, "(20:shadowed-private-key"); + /* (10:public-key ...)*/ + memcpy (p, pubkey+14, point - (pubkey+14)); + p += point - (pubkey+14); + p = stpcpy (p, "(8:shadowed5:t1-v1"); + memcpy (p, shadow_info, shadow_info_len); + p += shadow_info_len; + *p++ = ')'; + memcpy (p, point, pubkey_len - (point - pubkey)); + p += pubkey_len - (point - pubkey); + + return 0; +} + +/* Parse a canonical encoded shadowed key and return a pointer to the + inner list with the shadow_info */ +int +agent_get_shadow_info (const unsigned char *shadowkey, + unsigned char const **shadow_info) +{ + const unsigned char *s; + size_t n; + int depth = 0; + + s = shadowkey; + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + depth++; + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (!smatch (&s, n, "shadowed-private-key")) + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + if (*s != '(') + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + depth++; + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + s += n; /* skip over the algorithm name */ + + for (;;) + { + if (*s == ')') + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + depth++; + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (smatch (&s, n, "shadowed")) + break; + s += n; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + s +=n; /* skip value */ + if (*s != ')') + return gpg_error (GPG_ERR_INV_SEXP); + depth--; + s++; + } + /* found the shadowed list, s points to the protocol */ + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (smatch (&s, n, "t1-v1")) + { + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + *shadow_info = s; + } + else + return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL); + return 0; +} + diff --git a/agent/simple-pwquery.c b/agent/simple-pwquery.c new file mode 100644 index 000000000..e870122cb --- /dev/null +++ b/agent/simple-pwquery.c @@ -0,0 +1,486 @@ +/* simple-pwquery.c - A simple password query client for gpg-agent + * Copyright (C) 2002 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 + */ + +/* This module is intended as a standalone client implementation to + gpg-agent's GET_PASSPHRASE command. In particular it does not use + the Assuan library and can only cope with an already running + gpg-agent. Some stuff is configurable in the header file. */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LOCALE_H +#include +#endif + +#define SIMPLE_PWQUERY_IMPLEMENTATION 1 +#include "simple-pwquery.h" + +#if defined(SPWQ_USE_LOGGING) && !defined(HAVE_JNLIB_LOGGING) +# undef SPWQ_USE_LOGGING +#endif + +#ifndef _ +#define _(a) (a) +#endif + +#if !defined (hexdigitp) && !defined (xtoi_2) +#define digitp(p) (*(p) >= '0' && *(p) <= '9') +#define hexdigitp(a) (digitp (a) \ + || (*(a) >= 'A' && *(a) <= 'F') \ + || (*(a) >= 'a' && *(a) <= 'f')) +#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ + *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) +#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) +#endif + + +/* Write NBYTES of BUF to file descriptor FD. */ +static int +writen (int fd, const void *buf, size_t nbytes) +{ + size_t nleft = nbytes; + int nwritten; + + while (nleft > 0) + { + nwritten = write( fd, buf, nleft ); + if (nwritten < 0) + { + if (errno == EINTR) + nwritten = 0; + else { +#ifdef SPWQ_USE_LOGGING + log_error ("write failed: %s\n", strerror (errno)); +#endif + return SPWQ_IO_ERROR; + } + } + nleft -= nwritten; + buf = (const char*)buf + nwritten; + } + + return 0; +} + + +/* Read an entire line and return number of bytes read. */ +static int +readline (int fd, char *buf, size_t buflen) +{ + size_t nleft = buflen; + char *p; + int nread = 0; + + while (nleft > 0) + { + int n = read (fd, buf, nleft); + if (n < 0) + { + if (errno == EINTR) + continue; + return -(SPWQ_IO_ERROR); + } + else if (!n) + { + return -(SPWQ_PROTOCOL_ERROR); /* incomplete line */ + } + p = buf; + nleft -= n; + buf += n; + nread += n; + + for (; n && *p != '\n'; n--, p++) + ; + if (n) + { + break; /* at least one full line available - that's enough. + This function is just a simple implementation, so + it is okay to forget about pending bytes */ + } + } + + return nread; +} + + +/* Send an option to the agent */ +static int +agent_send_option (int fd, const char *name, const char *value) +{ + char buf[200]; + int nread; + char *line; + int i; + + line = spwq_malloc (7 + strlen (name) + 1 + strlen (value) + 2); + if (!line) + return SPWQ_OUT_OF_CORE; + strcpy (stpcpy (stpcpy (stpcpy ( + stpcpy (line, "OPTION "), name), "="), value), "\n"); + i = writen (fd, line, strlen (line)); + spwq_free (line); + if (i) + return i; + + /* get response */ + nread = readline (fd, buf, DIM(buf)-1); + if (nread < 0) + return -nread; + if (nread < 3) + return SPWQ_PROTOCOL_ERROR; + + if (buf[0] == 'O' && buf[1] == 'K' && (buf[2] == ' ' || buf[2] == '\n')) + return 0; /* okay */ + + return SPWQ_ERR_RESPONSE; +} + + +/* Send all available options to the agent. */ +static int +agent_send_all_options (int fd) +{ + char *dft_display = NULL; + char *dft_ttyname = NULL; + char *dft_ttytype = NULL; + int rc = 0; + + dft_display = getenv ("DISPLAY"); + if (dft_display) + { + if ((rc = agent_send_option (fd, "display", dft_display))) + return rc; + } + + dft_ttyname = getenv ("GPG_TTY"); + if ((!dft_ttyname || !*dft_ttyname) && ttyname (0)) + dft_ttyname = ttyname (0); + if (dft_ttyname && *dft_ttyname) + { + if ((rc=agent_send_option (fd, "ttyname", dft_ttyname))) + return rc; + } + + dft_ttytype = getenv ("TERM"); + if (dft_ttyname && dft_ttytype) + { + if ((rc = agent_send_option (fd, "ttytype", dft_ttytype))) + return rc; + } + +#if defined(HAVE_SETLOCALE) + { + char *old_lc = NULL; + char *dft_lc = NULL; + +#if defined(LC_CTYPE) + old_lc = setlocale (LC_CTYPE, NULL); + if (old_lc) + { + char *p = spwq_malloc (strlen (old_lc)+1); + if (!p) + return SPWQ_OUT_OF_CORE; + strcpy (p, old_lc); + old_lc = p; + } + dft_lc = setlocale (LC_CTYPE, ""); + if (dft_ttyname && dft_lc) + rc = agent_send_option (fd, "lc-ctype", dft_lc); + if (old_lc) + { + setlocale (LC_CTYPE, old_lc); + spwq_free (old_lc); + } + if (rc) + return rc; +#endif + +#if defined(LC_MESSAGES) + old_lc = setlocale (LC_MESSAGES, NULL); + if (old_lc) + { + char *p = spwq_malloc (strlen (old_lc)+1); + if (!p) + return SPWQ_OUT_OF_CORE; + strcpy (p, old_lc); + old_lc = p; + } + dft_lc = setlocale (LC_MESSAGES, ""); + if (dft_ttyname && dft_lc) + rc = agent_send_option (fd, "lc-messages", dft_lc); + if (old_lc) + { + setlocale (LC_MESSAGES, old_lc); + spwq_free (old_lc); + } + if (rc) + return rc; +#endif + } +#endif /*HAVE_SETLOCALE*/ + + return 0; +} + + + +/* Try to open a connection to the agent, send all options and return + the file descriptor for the connection. Return -1 in case of + error. */ +static int +agent_open (int *rfd) +{ + int rc; + int fd; + char *infostr, *p; + struct sockaddr_un client_addr; + size_t len; + int prot; + char line[200]; + int nread; + + *rfd = -1; + infostr = getenv ( "GPG_AGENT_INFO" ); + if ( !infostr ) + { +#ifdef SPWQ_USE_LOGGING + log_error (_("gpg-agent is not available in this session\n")); +#endif + return SPWQ_NO_AGENT; + } + + if ( !(p = strchr ( infostr, ':')) || p == infostr + || (p-infostr)+1 >= sizeof client_addr.sun_path ) + { +#ifdef SPWQ_USE_LOGGING + log_error ( _("malformed GPG_AGENT_INFO environment variable\n")); +#endif + return SPWQ_NO_AGENT; + } + *p++ = 0; + + while (*p && *p != ':') + p++; + prot = *p? atoi (p+1) : 0; + if ( prot != 1) + { +#ifdef SPWQ_USE_LOGGING + log_error (_("gpg-agent protocol version %d is not supported\n"),prot); +#endif + return SPWQ_PROTOCOL_ERROR; + } + + if( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1 ) + { +#ifdef SPWQ_USE_LOGGING + log_error ("can't create socket: %s\n", strerror(errno) ); +#endif + return SPWQ_SYS_ERROR; + } + + memset (&client_addr, 0, sizeof client_addr); + client_addr.sun_family = AF_UNIX; + strcpy (client_addr.sun_path, infostr); + len = (offsetof (struct sockaddr_un, sun_path) + + strlen(client_addr.sun_path) + 1); + + if (connect (fd, (struct sockaddr*)&client_addr, len ) == -1) + { +#ifdef SPWQ_USE_LOGGING + log_error ( _("can't connect to `%s': %s\n"), infostr, strerror (errno)); +#endif + close (fd ); + return SPWQ_IO_ERROR; + } + + nread = readline (fd, line, DIM(line)); + if (nread < 3 || !(line[0] == 'O' && line[1] == 'K' + && (line[2] == '\n' || line[2] == ' ')) ) + { +#ifdef SPWQ_USE_LOGGING + log_error ( _("communication problem with gpg-agent\n")); +#endif + close (fd ); + return SPWQ_PROTOCOL_ERROR; + } + + rc = agent_send_all_options (fd); + if (rc) + { +#ifdef SPWQ_USE_LOGGING + log_error (_("problem setting the gpg-agent options\n")); +#endif + close (fd); + return rc; + } + + *rfd = fd; + return 0; +} + + +/* Copy text to BUFFER and escape as required. Return a poiinter to + the end of the new buffer. NOte that BUFFER must be large enough + to keep the entire text; allocataing it 3 times the size of TEXT + is sufficient. */ +static char * +copy_and_escape (char *buffer, const char *text) +{ + int i; + char *p = buffer; + + for (i=0; text[i]; i++) + { + if (text[i] < ' ' || text[i] == '+') + { + sprintf (p, "%%%02X", text[i]); + p += 3; + } + else if (text[i] == ' ') + *p++ = '+'; + else + *p++ = text[i]; + } + return p; +} + + +/* Ask the gpg-agent for a passphrase and present the user with a + DESCRIPTION, a PROMPT and optiaonlly with a TRYAGAIN extra text. + If a CACHEID is not NULL it is used to locate the passphrase in in + the cache and store it under this ID. If ERRORCODE is not NULL it + should point a variable receiving an errorcode; thsi errocode might + be 0 if the user canceled the operation. The function returns NULL + to indicate an error. */ +char * +simple_pwquery (const char *cacheid, + const char *tryagain, + const char *prompt, + const char *description, + int *errorcode) +{ + int fd = -1; + int nread; + char *result = NULL; + char *pw = NULL; + char *p; + int rc, i; + + rc = agent_open (&fd); + if (rc) + goto leave; + + if (!cacheid) + cacheid = "X"; + if (!tryagain) + tryagain = "X"; + if (!prompt) + prompt = "X"; + if (!description) + description = "X"; + + { + char *line; + /* We allocate 3 times the needed space so that there is enough + space for escaping. */ + line = spwq_malloc (15 + + 3*strlen (cacheid) + 1 + + 3*strlen (tryagain) + 1 + + 3*strlen (prompt) + 1 + + 3*strlen (description) + 1 + + 2); + if (!line) + { + rc = SPWQ_OUT_OF_CORE; + goto leave; + } + strcpy (line, "GET_PASSPHRASE "); + p = line+15; + p = copy_and_escape (p, cacheid); + *p++ = ' '; + p = copy_and_escape (p, tryagain); + *p++ = ' '; + p = copy_and_escape (p, prompt); + *p++ = ' '; + p = copy_and_escape (p, description); + *p++ = '\n'; + rc = writen (fd, line, p - line); + spwq_free (line); + if (rc) + goto leave; + } + + /* get response */ + pw = spwq_secure_malloc (500); + nread = readline (fd, pw, 499); + if (nread < 0) + { + rc = -nread; + goto leave; + } + if (nread < 3) + { + rc = SPWQ_PROTOCOL_ERROR; + goto leave; + } + + if (pw[0] == 'O' && pw[1] == 'K' && pw[2] == ' ') + { /* we got a passphrase - convert it back from hex */ + size_t pwlen = 0; + + for (i=3; i < nread && hexdigitp (pw+i); i+=2) + pw[pwlen++] = xtoi_2 (pw+i); + pw[pwlen] = 0; /* make a C String */ + result = pw; + pw = NULL; + } + else if (nread > 7 && !memcmp (pw, "ERR 111", 7) + && (pw[7] == ' ' || pw[7] == '\n') ) + { +#ifdef SPWQ_USE_LOGGING + log_info (_("canceled by user\n") ); +#endif + *errorcode = 0; /* canceled */ + } + else + { +#ifdef SPWQ_USE_LOGGING + log_error (_("problem with the agent\n")); +#endif + rc = SPWQ_ERR_RESPONSE; + } + + leave: + if (errorcode) + *errorcode = rc; + if (fd != -1) + close (fd); + if (pw) + spwq_free (pw); + return result; +} diff --git a/common/ChangeLog b/common/ChangeLog new file mode 100644 index 000000000..4870a4a5d --- /dev/null +++ b/common/ChangeLog @@ -0,0 +1,219 @@ +2003-07-15 Werner Koch + + * simple-pwquery.c, simple-pwquery.h: New; moved from ../agent. + * Makefile.am (libsimple_pwquery_a_LIBADD): New. + +2003-06-25 Werner Koch + + * maperror.c (map_to_assuan_status): Directly map 0 to 0. + +2003-06-17 Werner Koch + + * gettime.c (scan_isodatestr,add_days_to_timestamp,strtimevalue) + (strtimestamp,asctimestamp): New. Code taken from gnupg 1.3.2 + mischelp.c. + + * yesno.c: New. Code taken from gnupg 1.3.2 mischelp.c + + * miscellaneous.c: New. + + * util.h: Include utf8conf.h + +2003-06-16 Werner Koch + + * gettime.c (make_timestamp): New. + + * ttyio.c: New. Taken from gnupg 1.2. + * ttyio.h: Move from ../include. + +2003-06-13 Werner Koch + + * util.h (seterr): Removed macro. + (xmalloc_secure,xcalloc_secure): New. + +2003-06-11 Werner Koch + + * iobuf.c (iobuf_writebyte,iobuf_write): Return error code from + iobuf_flush. + (iobuf_writestr): Ditto. + +2003-06-10 Werner Koch + + * iobuf.c, iobuf.h: New. Taken from current gnupg 1.3 CVS. Run + indent on it and adjusted error handling to libgpg-error style. + Replaced IOBUF by iobuf_t. Renamed malloc functions. + +2003-06-04 Werner Koch + + * errors.h: Removed all error codes. We keep the status codes for + now. + * Makefile.am: Do not create errors.c anymore; remove it from the + sources. + + * maperror.c: Don't include error.h. Change all error codes to + libgpg-error style. + (map_assuan_err): Changed to new Assuan error code convention. + (map_to_assuan_status): Likewise. + (map_gcry_err,map_kbx_err): Not needed. For now dummy functions. + + * membuf.c, membuf.h: New. Code taken from ../sm/call-agent.h. + * Makefile.am: Added above. + +2003-04-29 Werner Koch + + * util.h (fopencokokie): Removed prototype and struct. + + * fopencookie.c: Removed. + + * maperror.c: Use system assuan.h + +2002-10-31 Neal H. Walfield + + * isascii.c: New file. + * putc_unlocked.c: Likewise. + +2002-10-28 Neal H. Walfield + + * signal.c (caught_fatal_sig): Remove superfluous zero + initializer. + (caught_sigusr1): Likewise. + +2002-09-04 Neal H. Walfield + + * vasprintf.c (vasprintf) [va_copy]: Use va_copy. + [!va_copy && __va_copy]: Use __va_copy. + [!va_copy && !__va_copy]: Only now fall back to using memcpy. + +2002-08-21 Werner Koch + + * errors.h: Added STATUS_IMPORT_PROBLEM. + +2002-08-20 Werner Koch + + * vasprintf.c: Hack to handle NULL for %s. + +2002-08-09 Werner Koch + + * signal.c: New. Taken from GnuPG 1.1.91. + +2002-07-23 Werner Koch + + * util.h (_IO_cookie_io_functions_t): Fixed typo. Noted by + Richard Lefebvre. + +2002-07-22 Werner Koch + + * fseeko.c, ftello.c: New. + +2002-06-28 Werner Koch + + * maperror.c (map_to_assuan_status): Map more errorcodes to Bad + Certificate. + +2002-06-26 Werner Koch + + * maperror.c (map_to_assuan_status): Map EOF to No_Data_Available. + +2002-06-10 Werner Koch + + * errors.h (gnupg_error_token): Add new prototype. + (STATUS_ERROR): New. + + * mkerrtok: New. + * Makefile.am: Use it to create the new error token function. + +2002-06-04 Werner Koch + + * maperror.c (map_to_assuan_status): Map Bad_CA_Certificate. + +2002-05-23 Werner Koch + + * no-pth.c, Makefile.am: Removed. + +2002-05-22 Werner Koch + + * mkdtemp.c: Replaced byte by unsigned char because it is no longer + defined in gcrypt.h. + +2002-05-21 Werner Koch + + * maperror.c (map_gcry_err): Add libgcrypt's new S-expression errors. + (map_ksba_err): Add a few mappings. + +2002-05-14 Werner Koch + + * gettime.c: New. + +2002-05-03 Werner Koch + + * errors.h: Added STARUS_EXPSIG and STATUS_EXPKEYSIG. + +2002-04-15 Werner Koch + + * cryptmiss.c: New. + +2002-02-14 Werner Koch + + * maperror.c: Add more assuan<->gnupg mappings. + +2002-02-12 Werner Koch + + * fopencookie.c: Dummy function. + + * vasprintf.c: New. Taken from binutils-2.9.1 and dropped all non + ANSI-C stuff. Merged with asprintf version. + + * no-pth.c: New. + +2002-01-23 Werner Koch + + * mkdtemp.c: Copied from gnupg-1.0.6c and changed to use libgcrypt. + +2002-01-19 Werner Koch + + * sysutils.c: New. This is the misc.c file from gnupg 1.0.6 with + the OpenPGP stuff removed. + * sysutils.h: New. + +2002-01-15 Werner Koch + + * maperror.c: Add mapping for Not_Trusted. + +2002-01-11 Werner Koch + + * maperror.c (map_assuan_err): Codes for CRL + +2002-01-08 Werner Koch + + * util.h (spacep): New. + +2002-01-02 Werner Koch + + * maperror.c (map_to_assuan_status): New. Merged from ../agent + and ../sm. + +2001-12-20 Werner Koch + + * maperror.c (map_gcry_err): Add some mappings. + +2001-12-18 Werner Koch + + * Makefile.am (AM_CPPFLAGS): Include flags for gcrypt and ksba + +2001-12-14 Werner Koch + + * util.h (digitp, hexdigitp): New ctype like macros. + (atoi_1,atoi_2,atoi_4,xtoi_1,xtoi_2): New. + + + Copyright 2001, 2002 Free Software Foundation, Inc. + + This file is free software; as a special exception the author gives + unlimited permission to copy and/or distribute it, with or without + modifications, as long as this notice is preserved. + + This file is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY, to the extent permitted by law; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + diff --git a/common/Makefile.am b/common/Makefile.am new file mode 100644 index 000000000..2b99a19eb --- /dev/null +++ b/common/Makefile.am @@ -0,0 +1,58 @@ +# Makefile for common gnupg modules +# Copyright (C) 2001, 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 + +## Process this file with automake to produce Makefile.in + +EXTRA_DIST = mkerrors mkerrtok +#INCLUDES = + +noinst_LIBRARIES = libcommon.a libsimple-pwquery.a + +AM_CPPFLAGS = $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) + +libcommon_a_SOURCES = \ + util.h i18n.h \ + errors.h \ + maperror.c \ + sysutils.c sysutils.h \ + cryptmiss.c \ + gettime.c \ + yesno.c \ + miscellaneous.c \ + membuf.c membuf.h \ + iobuf.c iobuf.h \ + ttyio.c ttyio.h \ + signal.c + + +libcommon_a_LIBADD = @LIBOBJS@ + +libsimple_pwquery_a_SOURCES = \ + simple-pwquery.c simple-pwquery.h + +libsimple_pwquery_a_LIBADD = @LIBOBJS@ + + + + + + + + + diff --git a/common/README b/common/README new file mode 100644 index 000000000..a90224bab --- /dev/null +++ b/common/README @@ -0,0 +1,11 @@ +Stuff used by several modules of GnuPG. + +These directories use it: + +gpg +sm +agent + +These directories don't use it: + +kbx \ No newline at end of file diff --git a/common/errors.h b/common/errors.h new file mode 100644 index 000000000..a5643f08a --- /dev/null +++ b/common/errors.h @@ -0,0 +1,110 @@ +/* errors.h - Globally used error codes + * Copyright (C) 2001 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 GNUPG_COMMON_ERRORS_H +#define GNUPG_COMMON_ERRORS_H + +#include "util.h" + +/* Status codes - fixme: should go into another file */ +enum { + STATUS_ENTER, + STATUS_LEAVE, + STATUS_ABORT, + STATUS_GOODSIG, + STATUS_BADSIG, + STATUS_ERRSIG, + STATUS_BADARMOR, + STATUS_RSA_OR_IDEA, + STATUS_SIGEXPIRED, + STATUS_KEYREVOKED, + STATUS_TRUST_UNDEFINED, + STATUS_TRUST_NEVER, + STATUS_TRUST_MARGINAL, + STATUS_TRUST_FULLY, + STATUS_TRUST_ULTIMATE, + + STATUS_SHM_INFO, + STATUS_SHM_GET, + STATUS_SHM_GET_BOOL, + STATUS_SHM_GET_HIDDEN, + + STATUS_NEED_PASSPHRASE, + STATUS_VALIDSIG, + STATUS_SIG_ID, + STATUS_ENC_TO, + STATUS_NODATA, + STATUS_BAD_PASSPHRASE, + STATUS_NO_PUBKEY, + STATUS_NO_SECKEY, + STATUS_NEED_PASSPHRASE_SYM, + STATUS_DECRYPTION_FAILED, + STATUS_DECRYPTION_OKAY, + STATUS_MISSING_PASSPHRASE, + STATUS_GOOD_PASSPHRASE, + STATUS_GOODMDC, + STATUS_BADMDC, + STATUS_ERRMDC, + STATUS_IMPORTED, + STATUS_IMPORT_PROBLEM, + STATUS_IMPORT_RES, + STATUS_FILE_START, + STATUS_FILE_DONE, + STATUS_FILE_ERROR, + + STATUS_BEGIN_DECRYPTION, + STATUS_END_DECRYPTION, + STATUS_BEGIN_ENCRYPTION, + STATUS_END_ENCRYPTION, + + STATUS_DELETE_PROBLEM, + STATUS_GET_BOOL, + STATUS_GET_LINE, + STATUS_GET_HIDDEN, + STATUS_GOT_IT, + STATUS_PROGRESS, + STATUS_SIG_CREATED, + STATUS_SESSION_KEY, + STATUS_NOTATION_NAME, + STATUS_NOTATION_DATA, + STATUS_POLICY_URL, + STATUS_BEGIN_STREAM, + STATUS_END_STREAM, + STATUS_KEY_CREATED, + STATUS_USERID_HIN, + STATUS_UNEXPECTED, + STATUS_INV_RECP, + STATUS_NO_RECP, + STATUS_ALREADY_SIGNED, + + STATUS_EXPSIG, + STATUS_EXPKEYSIG, + + STATUS_TRUNCATED, + STATUS_ERROR +}; + + +/*-- errors.c (build by mkerror and mkerrtok) --*/ +const char *gnupg_strerror (int err); +const char *gnupg_error_token (int err); + + +#endif /*GNUPG_COMMON_ERRORS_H*/ diff --git a/common/gettime.c b/common/gettime.c new file mode 100644 index 000000000..a7914d348 --- /dev/null +++ b/common/gettime.c @@ -0,0 +1,250 @@ +/* gettime.c - Wrapper for time functions + * Copyright (C) 1998, 2002 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 +#ifdef HAVE_LANGINFO_H +#include +#endif + +#include "util.h" + +static unsigned long timewarp; +static enum { NORMAL = 0, FROZEN, FUTURE, PAST } timemode; + +/* Wrapper for the time(3). We use this here so we can fake the time + for tests */ +time_t +gnupg_get_time () +{ + time_t current = time (NULL); + if (timemode == NORMAL) + return current; + else if (timemode == FROZEN) + return timewarp; + else if (timemode == FUTURE) + return current + timewarp; + else + return current - timewarp; +} + +/* set the time to NEWTIME so that gnupg_get_time returns a time + starting with this one. With FREEZE set to 1 the returned time + will never change. Just for completeness, a value of (time_t)-1 + for NEWTIME gets you back to rality. Note that this is obviously + not thread-safe but this is not required. */ +void +gnupg_set_time (time_t newtime, int freeze) +{ + time_t current = time (NULL); + + if ( newtime == (time_t)-1 || current == newtime) + { + timemode = NORMAL; + timewarp = 0; + } + else if (freeze) + { + timemode = FROZEN; + timewarp = current; + } + else if (newtime > current) + { + timemode = FUTURE; + timewarp = newtime - current; + } + else + { + timemode = PAST; + timewarp = current - newtime; + } +} + +/* Returns true when we are in timewarp mode */ +int +gnupg_faked_time_p (void) +{ + return timemode; +} + + +/* This function is used by gpg because OpenPGP defines the timestamp + as an unsigned 32 bit value. */ +u32 +make_timestamp (void) +{ + time_t t = gnupg_get_time (); + + if (t == (time_t)-1) + log_fatal ("gnupg_get_time() failed\n"); + return (u32)t; +} + + + +/**************** + * Scan a date string and return a timestamp. + * The only supported format is "yyyy-mm-dd" + * Returns 0 for an invalid date. + */ +u32 +scan_isodatestr( const char *string ) +{ + int year, month, day; + struct tm tmbuf; + time_t stamp; + int i; + + if( strlen(string) != 10 || string[4] != '-' || string[7] != '-' ) + return 0; + for( i=0; i < 4; i++ ) + if( !digitp (string+i) ) + return 0; + if( !digitp (string+5) || !digitp(string+6) ) + return 0; + if( !digitp(string+8) || !digitp(string+9) ) + return 0; + year = atoi(string); + month = atoi(string+5); + day = atoi(string+8); + /* some basic checks */ + if( year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ) + return 0; + memset( &tmbuf, 0, sizeof tmbuf ); + tmbuf.tm_mday = day; + tmbuf.tm_mon = month-1; + tmbuf.tm_year = year - 1900; + tmbuf.tm_isdst = -1; + stamp = mktime( &tmbuf ); + if( stamp == (time_t)-1 ) + return 0; + return stamp; +} + + +u32 +add_days_to_timestamp( u32 stamp, u16 days ) +{ + return stamp + days*86400L; +} + + +/**************** + * Return a string with a time value in the form: x Y, n D, n H + */ + +const char * +strtimevalue( u32 value ) +{ + static char buffer[30]; + unsigned int years, days, hours, minutes; + + value /= 60; + minutes = value % 60; + value /= 60; + hours = value % 24; + value /= 24; + days = value % 365; + value /= 365; + years = value; + + sprintf(buffer,"%uy%ud%uh%um", years, days, hours, minutes ); + if( years ) + return buffer; + if( days ) + return strchr( buffer, 'y' ) + 1; + return strchr( buffer, 'd' ) + 1; +} + + +/**************** + * Note: this function returns GMT + */ +const char * +strtimestamp( u32 stamp ) +{ + static char buffer[11+5]; + struct tm *tp; + time_t atime = stamp; + + if (atime < 0) { + strcpy (buffer, "????" "-??" "-??"); + } + else { + tp = gmtime( &atime ); + sprintf(buffer,"%04d-%02d-%02d", + 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday ); + } + return buffer; +} + +/**************** + * Note: this function returns local time + */ +const char * +asctimestamp( u32 stamp ) +{ + static char buffer[50]; +#if defined (HAVE_STRFTIME) && defined (HAVE_NL_LANGINFO) + static char fmt[50]; +#endif + struct tm *tp; + time_t atime = stamp; + + if (atime < 0) { + strcpy (buffer, "????" "-??" "-??"); + return buffer; + } + + tp = localtime( &atime ); +#ifdef HAVE_STRFTIME +#if defined(HAVE_NL_LANGINFO) + mem2str( fmt, nl_langinfo(D_T_FMT), DIM(fmt)-3 ); + if( strstr( fmt, "%Z" ) == NULL ) + strcat( fmt, " %Z"); + strftime( buffer, DIM(buffer)-1, fmt, tp ); +#else + /* fixme: we should check whether the locale appends a " %Z" + * These locales from glibc don't put the " %Z": + * fi_FI hr_HR ja_JP lt_LT lv_LV POSIX ru_RU ru_SU sv_FI sv_SE zh_CN + */ + strftime( buffer, DIM(buffer)-1, "%c %Z", tp ); +#endif + buffer[DIM(buffer)-1] = 0; +#else + mem2str( buffer, asctime(tp), DIM(buffer) ); +#endif + return buffer; +} + + + + + + + + + + + + + + diff --git a/common/iobuf.c b/common/iobuf.c new file mode 100644 index 000000000..773e2993b --- /dev/null +++ b/common/iobuf.c @@ -0,0 +1,2415 @@ +/* iobuf.c - file handling + * Copyright (C) 1998, 1999, 2000, 2001, 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 +#include +#include +#include +#include +#ifdef HAVE_DOSISH_SYSTEM +#include +#endif +#ifdef __riscos__ +#include +#include +#endif /* __riscos__ */ + +#include "memory.h" +#include "util.h" +#include "iobuf.h" + +#undef FILE_FILTER_USES_STDIO + +#ifdef HAVE_DOSISH_SYSTEM +#define USE_SETMODE 1 +#endif + +#ifdef FILE_FILTER_USES_STDIO +#define my_fileno(a) fileno ((a)) +#define my_fopen_ro(a,b) fopen ((a),(b)) +#define my_fopen(a,b) fopen ((a),(b)) +typedef FILE *FILEP_OR_FD; +#define INVALID_FP NULL +#define FILEP_OR_FD_FOR_STDIN (stdin) +#define FILEP_OR_FD_FOR_STDOUT (stdout) +typedef struct +{ + FILE *fp; /* open file handle */ + int keep_open; + int no_cache; + int print_only_name; /* flags indicating that fname is not a real file */ + char fname[1]; /* name of the file */ +} +file_filter_ctx_t; +#else +#define my_fileno(a) (a) +#define my_fopen_ro(a,b) fd_cache_open ((a),(b)) +#define my_fopen(a,b) direct_open ((a),(b)) +#ifdef HAVE_DOSISH_SYSTEM +typedef HANDLE FILEP_OR_FD; +#define INVALID_FP ((HANDLE)-1) +#define FILEP_OR_FD_FOR_STDIN (GetStdHandle (STD_INPUT_HANDLE)) +#define FILEP_OR_FD_FOR_STDOUT (GetStdHandle (STD_OUTPUT_HANDLE)) +#undef USE_SETMODE +#else +typedef int FILEP_OR_FD; +#define INVALID_FP (-1) +#define FILEP_OR_FD_FOR_STDIN (0) +#define FILEP_OR_FD_FOR_STDOUT (1) +#endif +typedef struct +{ + FILEP_OR_FD fp; /* open file handle */ + int keep_open; + int no_cache; + int eof_seen; + int print_only_name; /* flags indicating that fname is not a real file */ + char fname[1]; /* name of the file */ +} +file_filter_ctx_t; + +struct close_cache_s +{ + struct close_cache_s *next; + FILEP_OR_FD fp; + char fname[1]; +}; +typedef struct close_cache_s *CLOSE_CACHE; +static CLOSE_CACHE close_cache; +#endif + +#ifdef __MINGW32__ +typedef struct +{ + int sock; + int keep_open; + int no_cache; + int eof_seen; + int print_only_name; /* flags indicating that fname is not a real file */ + char fname[1]; /* name of the file */ +} +sock_filter_ctx_t; +#endif /*__MINGW32__*/ + +/* The first partial length header block must be of size 512 + * to make it easier (and efficienter) we use a min. block size of 512 + * for all chunks (but the last one) */ +#define OP_MIN_PARTIAL_CHUNK 512 +#define OP_MIN_PARTIAL_CHUNK_2POW 9 + +typedef struct +{ + int use; + size_t size; + size_t count; + int partial; /* 1 = partial header, 2 in last partial packet */ + char *buffer; /* used for partial header */ + size_t buflen; /* used size of buffer */ + int first_c; /* of partial header (which is > 0) */ + int eof; +} +block_filter_ctx_t; + +static int special_names_enabled; + +static int underflow (iobuf_t a); +static int translate_file_handle (int fd, int for_write); + +#ifndef FILE_FILTER_USES_STDIO + +/* + * Invalidate (i.e. close) a cached iobuf + */ +static void +fd_cache_invalidate (const char *fname) +{ + CLOSE_CACHE cc; + + assert (fname); + if (DBG_IOBUF) + log_debug ("fd_cache_invalidate (%s)\n", fname); + + for (cc = close_cache; cc; cc = cc->next) + { + if (cc->fp != INVALID_FP && !strcmp (cc->fname, fname)) + { + if (DBG_IOBUF) + log_debug (" did (%s)\n", cc->fname); +#ifdef HAVE_DOSISH_SYSTEM + CloseHandle (cc->fp); +#else + close (cc->fp); +#endif + cc->fp = INVALID_FP; + } + } +} + + + +static FILEP_OR_FD +direct_open (const char *fname, const char *mode) +{ +#ifdef HAVE_DOSISH_SYSTEM + unsigned long da, cd, sm; + HANDLE hfile; + + /* Note, that we do not handle all mode combinations */ + + /* According to the ReactOS source it seems that open() of the + * standard MSW32 crt does open the file in share mode which is + * something new for MS applications ;-) + */ + if (strchr (mode, '+')) + { + fd_cache_invalidate (fname); + da = GENERIC_READ | GENERIC_WRITE; + cd = OPEN_EXISTING; + sm = FILE_SHARE_READ | FILE_SHARE_WRITE; + } + else if (strchr (mode, 'w')) + { + fd_cache_invalidate (fname); + da = GENERIC_WRITE; + cd = CREATE_ALWAYS; + sm = FILE_SHARE_WRITE; + } + else + { + da = GENERIC_READ; + cd = OPEN_EXISTING; + sm = FILE_SHARE_READ; + } + + hfile = CreateFile (fname, da, sm, NULL, cd, FILE_ATTRIBUTE_NORMAL, NULL); + return hfile; +#else + int oflag; + int cflag = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + + /* Note, that we do not handle all mode combinations */ + if (strchr (mode, '+')) + { + fd_cache_invalidate (fname); + oflag = O_RDWR; + } + else if (strchr (mode, 'w')) + { + fd_cache_invalidate (fname); + oflag = O_WRONLY | O_CREAT | O_TRUNC; + } + else + { + oflag = O_RDONLY; + } +#ifdef O_BINARY + if (strchr (mode, 'b')) + oflag |= O_BINARY; +#endif +#ifndef __riscos__ + return open (fname, oflag, cflag); +#else + { + struct stat buf; + int rc = stat (fname, &buf); + + /* Don't allow iobufs on directories */ + if (!rc && S_ISDIR (buf.st_mode) && !S_ISREG (buf.st_mode)) + return __set_errno (EISDIR); + else + return open (fname, oflag, cflag); + } +#endif +#endif +} + + +/* + * Instead of closing an FD we keep it open and cache it for later reuse + * Note that this caching strategy only works if the process does not chdir. + */ +static void +fd_cache_close (const char *fname, FILEP_OR_FD fp) +{ + CLOSE_CACHE cc; + + assert (fp); + if (!fname || !*fname) + { +#ifdef HAVE_DOSISH_SYSTEM + CloseHandle (fp); +#else + close (fp); +#endif + if (DBG_IOBUF) + log_debug ("fd_cache_close (%p) real\n", (void *) fp); + return; + } + /* try to reuse a slot */ + for (cc = close_cache; cc; cc = cc->next) + { + if (cc->fp == INVALID_FP && !strcmp (cc->fname, fname)) + { + cc->fp = fp; + if (DBG_IOBUF) + log_debug ("fd_cache_close (%s) used existing slot\n", fname); + return; + } + } + /* add a new one */ + if (DBG_IOBUF) + log_debug ("fd_cache_close (%s) new slot created\n", fname); + cc = xcalloc (1, sizeof *cc + strlen (fname)); + strcpy (cc->fname, fname); + cc->fp = fp; + cc->next = close_cache; + close_cache = cc; +} + +/* + * Do an direct_open on FNAME but first try to reuse one from the fd_cache + */ +static FILEP_OR_FD +fd_cache_open (const char *fname, const char *mode) +{ + CLOSE_CACHE cc; + + assert (fname); + for (cc = close_cache; cc; cc = cc->next) + { + if (cc->fp != INVALID_FP && !strcmp (cc->fname, fname)) + { + FILEP_OR_FD fp = cc->fp; + cc->fp = INVALID_FP; + if (DBG_IOBUF) + log_debug ("fd_cache_open (%s) using cached fp\n", fname); +#ifdef HAVE_DOSISH_SYSTEM + if (SetFilePointer (fp, 0, NULL, FILE_BEGIN) == 0xffffffff) + { + log_error ("rewind file failed on handle %p: ec=%d\n", + fp, (int) GetLastError ()); + fp = INVALID_FP; + } +#else + if (lseek (fp, 0, SEEK_SET) == (off_t) - 1) + { + log_error ("can't rewind fd %d: %s\n", fp, strerror (errno)); + fp = INVALID_FP; + } +#endif + return fp; + } + } + if (DBG_IOBUF) + log_debug ("fd_cache_open (%s) not cached\n", fname); + return direct_open (fname, mode); +} + + +#endif /*FILE_FILTER_USES_STDIO */ + + +/**************** + * Read data from a file into buf which has an allocated length of *LEN. + * return the number of read bytes in *LEN. OPAQUE is the FILE * of + * the stream. A is not used. + * control may be: + * IOBUFCTRL_INIT: called just before the function is linked into the + * list of function. This can be used to prepare internal + * data structures of the function. + * IOBUFCTRL_FREE: called just before the function is removed from the + * list of functions and can be used to release internal + * data structures or close a file etc. + * IOBUFCTRL_UNDERFLOW: called by iobuf_underflow to fill the buffer + * with new stuff. *RET_LEN is the available size of the + * buffer, and should be set to the number of bytes + * which were put into the buffer. The function + * returns 0 to indicate success, -1 on EOF and + * GPG_ERR_xxxxx for other errors. + * + * IOBUFCTRL_FLUSH: called by iobuf_flush() to write out the collected stuff. + * *RET_LAN is the number of bytes in BUF. + * + * IOBUFCTRL_CANCEL: send to all filters on behalf of iobuf_cancel. The + * filter may take appropriate action on this message. + */ +static int +file_filter (void *opaque, int control, iobuf_t chain, byte * buf, + size_t * ret_len) +{ + file_filter_ctx_t *a = opaque; + FILEP_OR_FD f = a->fp; + size_t size = *ret_len; + size_t nbytes = 0; + int rc = 0; + +#ifdef FILE_FILTER_USES_STDIO + if (control == IOBUFCTRL_UNDERFLOW) + { + assert (size); /* need a buffer */ + if (feof (f)) + { /* On terminals you could easiely read as many EOFs as you call */ + rc = -1; /* fread() or fgetc() repeatly. Every call will block until you press */ + *ret_len = 0; /* CTRL-D. So we catch this case before we call fread() again. */ + } + else + { + clearerr (f); + nbytes = fread (buf, 1, size, f); + if (feof (f) && !nbytes) + { + rc = -1; /* okay: we can return EOF now. */ + } + else if (ferror (f) && errno != EPIPE) + { + rc = gpg_error_from_errno (errno); + log_error ("%s: read error: %s\n", a->fname, strerror (errno)); + } + *ret_len = nbytes; + } + } + else if (control == IOBUFCTRL_FLUSH) + { + if (size) + { + clearerr (f); + nbytes = fwrite (buf, 1, size, f); + if (ferror (f)) + { + rc = gpg_error_from_errno (errno); + log_error ("%s: write error: %s\n", a->fname, strerror (errno)); + } + } + *ret_len = nbytes; + } + else if (control == IOBUFCTRL_INIT) + { + a->keep_open = a->no_cache = 0; + } + else if (control == IOBUFCTRL_DESC) + { + *(char **) buf = "file_filter"; + } + else if (control == IOBUFCTRL_FREE) + { + if (f != stdin && f != stdout) + { + if (DBG_IOBUF) + log_debug ("%s: close fd %d\n", a->fname, fileno (f)); + if (!a->keep_open) + fclose (f); + } + f = NULL; + xfree (a); /* we can free our context now */ + } +#else /* !stdio implementation */ + + if (control == IOBUFCTRL_UNDERFLOW) + { + assert (size); /* need a buffer */ + if (a->eof_seen) + { + rc = -1; + *ret_len = 0; + } + else + { +#ifdef HAVE_DOSISH_SYSTEM + unsigned long nread; + + nbytes = 0; + if (!ReadFile (f, buf, size, &nread, NULL)) + { + int ec = (int) GetLastError (); + if (ec != ERROR_BROKEN_PIPE) + { + rc = gpg_error_from_errno (ec); + log_error ("%s: read error: ec=%d\n", a->fname, ec); + } + } + else if (!nread) + { + a->eof_seen = 1; + rc = -1; + } + else + { + nbytes = nread; + } + +#else + + int n; + + nbytes = 0; + do + { + n = read (f, buf, size); + } + while (n == -1 && errno == EINTR); + if (n == -1) + { /* error */ + if (errno != EPIPE) + { + rc = gpg_error_from_errno (errno); + log_error ("%s: read error: %s\n", + a->fname, strerror (errno)); + } + } + else if (!n) + { /* eof */ + a->eof_seen = 1; + rc = -1; + } + else + { + nbytes = n; + } +#endif + *ret_len = nbytes; + } + } + else if (control == IOBUFCTRL_FLUSH) + { + if (size) + { +#ifdef HAVE_DOSISH_SYSTEM + byte *p = buf; + unsigned long n; + + nbytes = size; + do + { + if (size && !WriteFile (f, p, nbytes, &n, NULL)) + { + int ec = (int) GetLastError (); + rc = gpg_error_from_errno (ec); + log_error ("%s: write error: ec=%d\n", a->fname, ec); + break; + } + p += n; + nbytes -= n; + } + while (nbytes); + nbytes = p - buf; +#else + byte *p = buf; + int n; + + nbytes = size; + do + { + do + { + n = write (f, p, nbytes); + } + while (n == -1 && errno == EINTR); + if (n > 0) + { + p += n; + nbytes -= n; + } + } + while (n != -1 && nbytes); + if (n == -1) + { + rc = gpg_error_from_errno (errno); + log_error ("%s: write error: %s\n", a->fname, strerror (errno)); + } + nbytes = p - buf; +#endif + } + *ret_len = nbytes; + } + else if (control == IOBUFCTRL_INIT) + { + a->eof_seen = 0; + a->keep_open = 0; + a->no_cache = 0; + } + else if (control == IOBUFCTRL_DESC) + { + *(char **) buf = "file_filter(fd)"; + } + else if (control == IOBUFCTRL_FREE) + { +#ifdef HAVE_DOSISH_SYSTEM + if (f != FILEP_OR_FD_FOR_STDIN && f != FILEP_OR_FD_FOR_STDOUT) + { + if (DBG_IOBUF) + log_debug ("%s: close handle %p\n", a->fname, f); + if (!a->keep_open) + fd_cache_close (a->no_cache ? NULL : a->fname, f); + } +#else + if ((int) f != 0 && (int) f != 1) + { + if (DBG_IOBUF) + log_debug ("%s: close fd %d\n", a->fname, f); + if (!a->keep_open) + fd_cache_close (a->no_cache ? NULL : a->fname, f); + } + f = INVALID_FP; +#endif + xfree (a); /* we can free our context now */ + } +#endif /* !stdio implementation */ + return rc; +} + +#ifdef __MINGW32__ +/* Becuase sockets are an special object under Lose32 we have to + * use a special filter */ +static int +sock_filter (void *opaque, int control, iobuf_t chain, byte * buf, + size_t * ret_len) +{ + sock_filter_ctx_t *a = opaque; + size_t size = *ret_len; + size_t nbytes = 0; + int rc = 0; + + if (control == IOBUFCTRL_UNDERFLOW) + { + assert (size); /* need a buffer */ + if (a->eof_seen) + { + rc = -1; + *ret_len = 0; + } + else + { + int nread; + + nread = recv (a->sock, buf, size, 0); + if (nread == SOCKET_ERROR) + { + int ec = (int) WSAGetLastError (); + rc = gpg_error_from_errno (ec); + log_error ("socket read error: ec=%d\n", ec); + } + else if (!nread) + { + a->eof_seen = 1; + rc = -1; + } + else + { + nbytes = nread; + } + *ret_len = nbytes; + } + } + else if (control == IOBUFCTRL_FLUSH) + { + if (size) + { + byte *p = buf; + int n; + + nbytes = size; + do + { + n = send (a->sock, p, nbytes, 0); + if (n == SOCKET_ERROR) + { + int ec = (int) WSAGetLastError (); + rc = gpg_error_from_errno (ec); + log_error ("socket write error: ec=%d\n", ec); + break; + } + p += n; + nbytes -= n; + } + while (nbytes); + nbytes = p - buf; + } + *ret_len = nbytes; + } + else if (control == IOBUFCTRL_INIT) + { + a->eof_seen = 0; + a->keep_open = 0; + a->no_cache = 0; + } + else if (control == IOBUFCTRL_DESC) + { + *(char **) buf = "sock_filter"; + } + else if (control == IOBUFCTRL_FREE) + { + if (!a->keep_open) + closesocket (a->sock); + xfree (a); /* we can free our context now */ + } + return rc; +} +#endif /*__MINGW32__*/ + +/**************** + * This is used to implement the block write mode. + * Block reading is done on a byte by byte basis in readbyte(), + * without a filter + */ +static int +block_filter (void *opaque, int control, iobuf_t chain, byte * buf, + size_t * ret_len) +{ + block_filter_ctx_t *a = opaque; + size_t size = *ret_len; + int c, needed, rc = 0; + char *p; + + if (control == IOBUFCTRL_UNDERFLOW) + { + size_t n = 0; + + p = buf; + assert (size); /* need a buffer */ + if (a->eof) /* don't read any further */ + rc = -1; + while (!rc && size) + { + if (!a->size) + { /* get the length bytes */ + if (a->partial == 2) + { + a->eof = 1; + if (!n) + rc = -1; + break; + } + else if (a->partial) + { + /* These OpenPGP introduced huffman like encoded length + * bytes are really a mess :-( */ + if (a->first_c) + { + c = a->first_c; + a->first_c = 0; + } + else if ((c = iobuf_get (chain)) == -1) + { + log_error ("block_filter: 1st length byte missing\n"); + rc = GPG_ERR_BAD_DATA; + break; + } + if (c < 192) + { + a->size = c; + a->partial = 2; + if (!a->size) + { + a->eof = 1; + if (!n) + rc = -1; + break; + } + } + else if (c < 224) + { + a->size = (c - 192) * 256; + if ((c = iobuf_get (chain)) == -1) + { + log_error + ("block_filter: 2nd length byte missing\n"); + rc = GPG_ERR_BAD_DATA; + break; + } + a->size += c + 192; + a->partial = 2; + if (!a->size) + { + a->eof = 1; + if (!n) + rc = -1; + break; + } + } + else if (c == 255) + { + a->size = iobuf_get (chain) << 24; + a->size |= iobuf_get (chain) << 16; + a->size |= iobuf_get (chain) << 8; + if ((c = iobuf_get (chain)) == -1) + { + log_error ("block_filter: invalid 4 byte length\n"); + rc = GPG_ERR_BAD_DATA; + break; + } + a->size |= c; + } + else + { /* next partial body length */ + a->size = 1 << (c & 0x1f); + } + /* log_debug("partial: ctx=%p c=%02x size=%u\n", a, c, a->size); */ + } + else + { /* the gnupg partial length scheme - much better :-) */ + c = iobuf_get (chain); + a->size = c << 8; + c = iobuf_get (chain); + a->size |= c; + if (c == -1) + { + log_error ("block_filter: error reading length info\n"); + rc = GPG_ERR_BAD_DATA; + } + if (!a->size) + { + a->eof = 1; + if (!n) + rc = -1; + break; + } + } + } + + while (!rc && size && a->size) + { + needed = size < a->size ? size : a->size; + c = iobuf_read (chain, p, needed); + if (c < needed) + { + if (c == -1) + c = 0; + log_error + ("block_filter %p: read error (size=%lu,a->size=%lu)\n", + a, (ulong) size + c, (ulong) a->size + c); + rc = GPG_ERR_BAD_DATA; + } + else + { + size -= c; + a->size -= c; + p += c; + n += c; + } + } + } + *ret_len = n; + } + else if (control == IOBUFCTRL_FLUSH) + { + if (a->partial) + { /* the complicated openpgp scheme */ + size_t blen, n, nbytes = size + a->buflen; + + assert (a->buflen <= OP_MIN_PARTIAL_CHUNK); + if (nbytes < OP_MIN_PARTIAL_CHUNK) + { + /* not enough to write a partial block out; so we store it */ + if (!a->buffer) + a->buffer = xmalloc (OP_MIN_PARTIAL_CHUNK); + memcpy (a->buffer + a->buflen, buf, size); + a->buflen += size; + } + else + { /* okay, we can write out something */ + /* do this in a loop to use the most efficient block lengths */ + p = buf; + do + { + /* find the best matching block length - this is limited + * by the size of the internal buffering */ + for (blen = OP_MIN_PARTIAL_CHUNK * 2, + c = OP_MIN_PARTIAL_CHUNK_2POW + 1; blen <= nbytes; + blen *= 2, c++) + ; + blen /= 2; + c--; + /* write the partial length header */ + assert (c <= 0x1f); /*;-) */ + c |= 0xe0; + iobuf_put (chain, c); + if ((n = a->buflen)) + { /* write stuff from the buffer */ + assert (n == OP_MIN_PARTIAL_CHUNK); + if (iobuf_write (chain, a->buffer, n)) + rc = gpg_error_from_errno (errno); + a->buflen = 0; + nbytes -= n; + } + if ((n = nbytes) > blen) + n = blen; + if (n && iobuf_write (chain, p, n)) + rc = gpg_error_from_errno (errno); + p += n; + nbytes -= n; + } + while (!rc && nbytes >= OP_MIN_PARTIAL_CHUNK); + /* store the rest in the buffer */ + if (!rc && nbytes) + { + assert (!a->buflen); + assert (nbytes < OP_MIN_PARTIAL_CHUNK); + if (!a->buffer) + a->buffer = xmalloc (OP_MIN_PARTIAL_CHUNK); + memcpy (a->buffer, p, nbytes); + a->buflen = nbytes; + } + } + } + else + { /* the gnupg scheme (which is not openpgp compliant) */ + size_t avail, n; + + for (p = buf; !rc && size;) + { + n = size; + avail = a->size - a->count; + if (!avail) + { + if (n > a->size) + { + iobuf_put (chain, (a->size >> 8) & 0xff); + iobuf_put (chain, a->size & 0xff); + avail = a->size; + a->count = 0; + } + else + { + iobuf_put (chain, (n >> 8) & 0xff); + iobuf_put (chain, n & 0xff); + avail = n; + a->count = a->size - n; + } + } + if (n > avail) + n = avail; + if (iobuf_write (chain, p, n)) + rc = gpg_error_from_errno (errno); + a->count += n; + p += n; + size -= n; + } + } + } + else if (control == IOBUFCTRL_INIT) + { + if (DBG_IOBUF) + log_debug ("init block_filter %p\n", a); + if (a->partial) + a->count = 0; + else if (a->use == 1) + a->count = a->size = 0; + else + a->count = a->size; /* force first length bytes */ + a->eof = 0; + a->buffer = NULL; + a->buflen = 0; + } + else if (control == IOBUFCTRL_DESC) + { + *(char **) buf = "block_filter"; + } + else if (control == IOBUFCTRL_FREE) + { + if (a->use == 2) + { /* write the end markers */ + if (a->partial) + { + u32 len; + /* write out the remaining bytes without a partial header + * the length of this header may be 0 - but if it is + * the first block we are not allowed to use a partial header + * and frankly we can't do so, because this length must be + * a power of 2. This is _really_ complicated because we + * have to check the possible length of a packet prior + * to it's creation: a chain of filters becomes complicated + * and we need a lot of code to handle compressed packets etc. + * :-((((((( + */ + /* construct header */ + len = a->buflen; + /*log_debug("partial: remaining length=%u\n", len ); */ + if (len < 192) + rc = iobuf_put (chain, len); + else if (len < 8384) + { + if (!(rc = iobuf_put (chain, ((len - 192) / 256) + 192))) + rc = iobuf_put (chain, ((len - 192) % 256)); + } + else + { /* use a 4 byte header */ + if (!(rc = iobuf_put (chain, 0xff))) + if (!(rc = iobuf_put (chain, (len >> 24) & 0xff))) + if (!(rc = iobuf_put (chain, (len >> 16) & 0xff))) + if (!(rc = iobuf_put (chain, (len >> 8) & 0xff))) + rc = iobuf_put (chain, len & 0xff); + } + if (!rc && len) + rc = iobuf_write (chain, a->buffer, len); + if (rc) + { + log_error ("block_filter: write error: %s\n", + strerror (errno)); + rc = gpg_error_from_errno (errno); + } + xfree (a->buffer); + a->buffer = NULL; + a->buflen = 0; + } + else + { + iobuf_writebyte (chain, 0); + iobuf_writebyte (chain, 0); + } + } + else if (a->size) + { + log_error ("block_filter: pending bytes!\n"); + } + if (DBG_IOBUF) + log_debug ("free block_filter %p\n", a); + xfree (a); /* we can free our context now */ + } + + return rc; +} + + +static void +print_chain (iobuf_t a) +{ + if (!DBG_IOBUF) + return; + for (; a; a = a->chain) + { + size_t dummy_len = 0; + const char *desc = "[none]"; + + if (a->filter) + a->filter (a->filter_ov, IOBUFCTRL_DESC, NULL, + (byte *) & desc, &dummy_len); + + log_debug ("iobuf chain: %d.%d `%s' filter_eof=%d start=%d len=%d\n", + a->no, a->subno, desc, a->filter_eof, + (int) a->d.start, (int) a->d.len); + } +} + +int +iobuf_print_chain (iobuf_t a) +{ + print_chain (a); + return 0; +} + +/**************** + * Allocate a new io buffer, with no function assigned. + * Use is the desired usage: 1 for input, 2 for output, 3 for temp buffer + * BUFSIZE is a suggested buffer size. + */ +iobuf_t +iobuf_alloc (int use, size_t bufsize) +{ + iobuf_t a; + static int number = 0; + + a = xcalloc (1, sizeof *a); + a->use = use; + a->d.buf = xmalloc (bufsize); + a->d.size = bufsize; + a->no = ++number; + a->subno = 0; + a->opaque = NULL; + a->real_fname = NULL; + return a; +} + +int +iobuf_close (iobuf_t a) +{ + iobuf_t a2; + size_t dummy_len = 0; + int rc = 0; + + if (a && a->directfp) + { + fclose (a->directfp); + xfree (a->real_fname); + if (DBG_IOBUF) + log_debug ("iobuf_close -> %p\n", a->directfp); + return 0; + } + + for (; a && !rc; a = a2) + { + a2 = a->chain; + if (a->use == 2 && (rc = iobuf_flush (a))) + log_error ("iobuf_flush failed on close: %s\n", gpg_strerror (rc)); + + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: close `%s'\n", a->no, a->subno, a->desc); + if (a->filter && (rc = a->filter (a->filter_ov, IOBUFCTRL_FREE, + a->chain, NULL, &dummy_len))) + log_error ("IOBUFCTRL_FREE failed on close: %s\n", gpg_strerror (rc)); + xfree (a->real_fname); + if (a->d.buf) + { + memset (a->d.buf, 0, a->d.size); /* erase the buffer */ + xfree (a->d.buf); + } + xfree (a); + } + return rc; +} + +int +iobuf_cancel (iobuf_t a) +{ + const char *s; + iobuf_t a2; + int rc; +#if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) + char *remove_name = NULL; +#endif + + if (a && a->use == 2) + { + s = iobuf_get_real_fname (a); + if (s && *s) + { +#if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) + remove_name = m_strdup (s); +#else + remove (s); +#endif + } + } + + /* send a cancel message to all filters */ + for (a2 = a; a2; a2 = a2->chain) + { + size_t dummy; + if (a2->filter) + a2->filter (a2->filter_ov, IOBUFCTRL_CANCEL, a2->chain, NULL, &dummy); + } + + rc = iobuf_close (a); +#if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) + if (remove_name) + { + /* Argg, MSDOS does not allow to remove open files. So + * we have to do it here */ + remove (remove_name); + xfree (remove_name); + } +#endif + return rc; +} + + +/**************** + * create a temporary iobuf, which can be used to collect stuff + * in an iobuf and later be written by iobuf_write_temp() to another + * iobuf. + */ +iobuf_t +iobuf_temp () +{ + iobuf_t a; + + a = iobuf_alloc (3, 8192); + + return a; +} + +iobuf_t +iobuf_temp_with_content (const char *buffer, size_t length) +{ + iobuf_t a; + + a = iobuf_alloc (3, length); + memcpy (a->d.buf, buffer, length); + a->d.len = length; + + return a; +} + +void +iobuf_enable_special_filenames (int yes) +{ + special_names_enabled = yes; +} + +/* + * see whether the filename has the for "-&nnnn", where n is a + * non-zero number. + * Returns this number or -1 if it is not the case. + */ +static int +check_special_filename (const char *fname) +{ + if (special_names_enabled && fname && *fname == '-' && fname[1] == '&') + { + int i; + + fname += 2; + for (i = 0; isdigit (fname[i]); i++) + ; + if (!fname[i]) + return atoi (fname); + } + return -1; +} + +/**************** + * Create a head iobuf for reading from a file + * returns: NULL if an error occures and sets errno + */ +iobuf_t +iobuf_open (const char *fname) +{ + iobuf_t a; + FILEP_OR_FD fp; + file_filter_ctx_t *fcx; + size_t len; + int print_only = 0; + int fd; + + if (!fname || (*fname == '-' && !fname[1])) + { + fp = FILEP_OR_FD_FOR_STDIN; +#ifdef USE_SETMODE + setmode (my_fileno (fp), O_BINARY); +#endif + fname = "[stdin]"; + print_only = 1; + } + else if ((fd = check_special_filename (fname)) != -1) + return iobuf_fdopen (translate_file_handle (fd, 0), "rb"); + else if ((fp = my_fopen_ro (fname, "rb")) == INVALID_FP) + return NULL; + a = iobuf_alloc (1, 8192); + fcx = xmalloc (sizeof *fcx + strlen (fname)); + fcx->fp = fp; + fcx->print_only_name = print_only; + strcpy (fcx->fname, fname); + if (!print_only) + a->real_fname = xstrdup (fname); + a->filter = file_filter; + a->filter_ov = fcx; + file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len); + file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len); + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: open `%s' fd=%d\n", + a->no, a->subno, fname, (int) my_fileno (fcx->fp)); + + return a; +} + +/**************** + * Create a head iobuf for reading from a file + * returns: NULL if an error occures and sets errno + */ +iobuf_t +iobuf_fdopen (int fd, const char *mode) +{ + iobuf_t a; + FILEP_OR_FD fp; + file_filter_ctx_t *fcx; + size_t len; + +#ifdef FILE_FILTER_USES_STDIO + if (!(fp = fdopen (fd, mode))) + return NULL; +#else + fp = (FILEP_OR_FD) fd; +#endif + a = iobuf_alloc (strchr (mode, 'w') ? 2 : 1, 8192); + fcx = xmalloc (sizeof *fcx + 20); + fcx->fp = fp; + fcx->print_only_name = 1; + sprintf (fcx->fname, "[fd %d]", fd); + a->filter = file_filter; + a->filter_ov = fcx; + file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len); + file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len); + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: fdopen `%s'\n", a->no, a->subno, fcx->fname); + iobuf_ioctl (a, 3, 1, NULL); /* disable fd caching */ + return a; +} + + +iobuf_t +iobuf_sockopen (int fd, const char *mode) +{ + iobuf_t a; +#ifdef __MINGW32__ + sock_filter_ctx_t *scx; + size_t len; + + a = iobuf_alloc (strchr (mode, 'w') ? 2 : 1, 8192); + scx = m_alloc (sizeof *scx + 25); + scx->sock = fd; + scx->print_only_name = 1; + sprintf (scx->fname, "[sock %d]", fd); + a->filter = sock_filter; + a->filter_ov = scx; + sock_filter (scx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len); + sock_filter (scx, IOBUFCTRL_INIT, NULL, NULL, &len); + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: sockopen `%s'\n", a->no, a->subno, scx->fname); + iobuf_ioctl (a, 3, 1, NULL); /* disable fd caching */ +#else + a = iobuf_fdopen (fd, mode); +#endif + return a; +} + +/**************** + * create an iobuf for writing to a file; the file will be created. + */ +iobuf_t +iobuf_create (const char *fname) +{ + iobuf_t a; + FILEP_OR_FD fp; + file_filter_ctx_t *fcx; + size_t len; + int print_only = 0; + int fd; + + if (!fname || (*fname == '-' && !fname[1])) + { + fp = FILEP_OR_FD_FOR_STDOUT; +#ifdef USE_SETMODE + setmode (my_fileno (fp), O_BINARY); +#endif + fname = "[stdout]"; + print_only = 1; + } + else if ((fd = check_special_filename (fname)) != -1) + return iobuf_fdopen (translate_file_handle (fd, 1), "wb"); + else if ((fp = my_fopen (fname, "wb")) == INVALID_FP) + return NULL; + a = iobuf_alloc (2, 8192); + fcx = xmalloc (sizeof *fcx + strlen (fname)); + fcx->fp = fp; + fcx->print_only_name = print_only; + strcpy (fcx->fname, fname); + if (!print_only) + a->real_fname = xstrdup (fname); + a->filter = file_filter; + a->filter_ov = fcx; + file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len); + file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len); + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: create `%s'\n", a->no, a->subno, a->desc); + + return a; +} + +/**************** + * append to an iobuf; if the file does not exist, create it. + * cannot be used for stdout. + * Note: This is not used. + */ +#if 0 /* not used */ +iobuf_t +iobuf_append (const char *fname) +{ + iobuf_t a; + FILE *fp; + file_filter_ctx_t *fcx; + size_t len; + + if (!fname) + return NULL; + else if (!(fp = my_fopen (fname, "ab"))) + return NULL; + a = iobuf_alloc (2, 8192); + fcx = m_alloc (sizeof *fcx + strlen (fname)); + fcx->fp = fp; + strcpy (fcx->fname, fname); + a->real_fname = m_strdup (fname); + a->filter = file_filter; + a->filter_ov = fcx; + file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len); + file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len); + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: append `%s'\n", a->no, a->subno, a->desc); + + return a; +} +#endif + +iobuf_t +iobuf_openrw (const char *fname) +{ + iobuf_t a; + FILEP_OR_FD fp; + file_filter_ctx_t *fcx; + size_t len; + + if (!fname) + return NULL; + else if ((fp = my_fopen (fname, "r+b")) == INVALID_FP) + return NULL; + a = iobuf_alloc (2, 8192); + fcx = xmalloc (sizeof *fcx + strlen (fname)); + fcx->fp = fp; + strcpy (fcx->fname, fname); + a->real_fname = xstrdup (fname); + a->filter = file_filter; + a->filter_ov = fcx; + file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len); + file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len); + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: openrw `%s'\n", a->no, a->subno, a->desc); + + return a; +} + + +int +iobuf_ioctl (iobuf_t a, int cmd, int intval, void *ptrval) +{ + if (cmd == 1) + { /* keep system filepointer/descriptor open */ + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: ioctl `%s' keep=%d\n", + a ? a->no : -1, a ? a->subno : -1, a ? a->desc : "?", + intval); + for (; a; a = a->chain) + if (!a->chain && a->filter == file_filter) + { + file_filter_ctx_t *b = a->filter_ov; + b->keep_open = intval; + return 0; + } +#ifdef __MINGW32__ + else if (!a->chain && a->filter == sock_filter) + { + sock_filter_ctx_t *b = a->filter_ov; + b->keep_open = intval; + return 0; + } +#endif + } + else if (cmd == 2) + { /* invalidate cache */ + if (DBG_IOBUF) + log_debug ("iobuf-*.*: ioctl `%s' invalidate\n", + ptrval ? (char *) ptrval : "?"); + if (!a && !intval && ptrval) + { +#ifndef FILE_FILTER_USES_STDIO + fd_cache_invalidate (ptrval); +#endif + return 0; + } + } + else if (cmd == 3) + { /* disallow/allow caching */ + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: ioctl `%s' no_cache=%d\n", + a ? a->no : -1, a ? a->subno : -1, a ? a->desc : "?", + intval); + for (; a; a = a->chain) + if (!a->chain && a->filter == file_filter) + { + file_filter_ctx_t *b = a->filter_ov; + b->no_cache = intval; + return 0; + } +#ifdef __MINGW32__ + else if (!a->chain && a->filter == sock_filter) + { + sock_filter_ctx_t *b = a->filter_ov; + b->no_cache = intval; + return 0; + } +#endif + } + + return -1; +} + + +/**************** + * Register an i/o filter. + */ +int +iobuf_push_filter (iobuf_t a, + int (*f) (void *opaque, int control, + iobuf_t chain, byte * buf, size_t * len), + void *ov) +{ + return iobuf_push_filter2 (a, f, ov, 0); +} + +int +iobuf_push_filter2 (iobuf_t a, + int (*f) (void *opaque, int control, + iobuf_t chain, byte * buf, size_t * len), + void *ov, int rel_ov) +{ + iobuf_t b; + size_t dummy_len = 0; + int rc = 0; + + if (a->directfp) + BUG (); + + if (a->use == 2 && (rc = iobuf_flush (a))) + return rc; + /* make a copy of the current stream, so that + * A is the new stream and B the original one. + * The contents of the buffers are transferred to the + * new stream. + */ + b = xmalloc (sizeof *b); + memcpy (b, a, sizeof *b); + /* fixme: it is stupid to keep a copy of the name at every level + * but we need the name somewhere because the name known by file_filter + * may have been released when we need the name of the file */ + b->real_fname = a->real_fname ? xstrdup (a->real_fname) : NULL; + /* remove the filter stuff from the new stream */ + a->filter = NULL; + a->filter_ov = NULL; + a->filter_ov_owner = 0; + a->filter_eof = 0; + if (a->use == 3) + a->use = 2; /* make a write stream from a temp stream */ + + if (a->use == 2) + { /* allocate a fresh buffer for the + original stream */ + b->d.buf = xmalloc (a->d.size); + b->d.len = 0; + b->d.start = 0; + } + else + { /* allocate a fresh buffer for the new + stream */ + a->d.buf = xmalloc (a->d.size); + a->d.len = 0; + a->d.start = 0; + } + /* disable nlimit for the new stream */ + a->ntotal = b->ntotal + b->nbytes; + a->nlimit = a->nbytes = 0; + a->nofast &= ~1; + /* make a link from the new stream to the original stream */ + a->chain = b; + a->opaque = b->opaque; + + /* setup the function on the new stream */ + a->filter = f; + a->filter_ov = ov; + a->filter_ov_owner = rel_ov; + + a->subno = b->subno + 1; + f (ov, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &dummy_len); + + if (DBG_IOBUF) + { + log_debug ("iobuf-%d.%d: push `%s'\n", a->no, a->subno, a->desc); + print_chain (a); + } + + /* now we can initialize the new function if we have one */ + if (a->filter && (rc = a->filter (a->filter_ov, IOBUFCTRL_INIT, a->chain, + NULL, &dummy_len))) + log_error ("IOBUFCTRL_INIT failed: %s\n", gpg_strerror (rc)); + return rc; +} + +/**************** + * Remove an i/o filter. + */ +int +pop_filter (iobuf_t a, int (*f) (void *opaque, int control, + iobuf_t chain, byte * buf, size_t * len), + void *ov) +{ + iobuf_t b; + size_t dummy_len = 0; + int rc = 0; + + if (a->directfp) + BUG (); + + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: pop `%s'\n", a->no, a->subno, a->desc); + if (!a->filter) + { /* this is simple */ + b = a->chain; + assert (b); + xfree (a->d.buf); + xfree (a->real_fname); + memcpy (a, b, sizeof *a); + xfree (b); + return 0; + } + for (b = a; b; b = b->chain) + if (b->filter == f && (!ov || b->filter_ov == ov)) + break; + if (!b) + log_bug ("pop_filter(): filter function not found\n"); + + /* flush this stream if it is an output stream */ + if (a->use == 2 && (rc = iobuf_flush (b))) + { + log_error ("iobuf_flush failed in pop_filter: %s\n", gpg_strerror (rc)); + return rc; + } + /* and tell the filter to free it self */ + if (b->filter && (rc = b->filter (b->filter_ov, IOBUFCTRL_FREE, b->chain, + NULL, &dummy_len))) + { + log_error ("IOBUFCTRL_FREE failed: %s\n", gpg_strerror (rc)); + return rc; + } + if (b->filter_ov && b->filter_ov_owner) + { + xfree (b->filter_ov); + b->filter_ov = NULL; + } + + + /* and see how to remove it */ + if (a == b && !b->chain) + log_bug ("can't remove the last filter from the chain\n"); + else if (a == b) + { /* remove the first iobuf from the chain */ + /* everything from b is copied to a. This is save because + * a flush has been done on the to be removed entry + */ + b = a->chain; + xfree (a->d.buf); + xfree (a->real_fname); + memcpy (a, b, sizeof *a); + xfree (b); + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: popped filter\n", a->no, a->subno); + } + else if (!b->chain) + { /* remove the last iobuf from the chain */ + log_bug ("Ohh jeee, trying to remove a head filter\n"); + } + else + { /* remove an intermediate iobuf from the chain */ + log_bug ("Ohh jeee, trying to remove an intermediate filter\n"); + } + + return rc; +} + + +/**************** + * read underflow: read more bytes into the buffer and return + * the first byte or -1 on EOF. + */ +static int +underflow (iobuf_t a) +{ + size_t len; + int rc; + + assert (a->d.start == a->d.len); + if (a->use == 3) + return -1; /* EOF because a temp buffer can't do an underflow */ + + if (a->filter_eof) + { + if (a->chain) + { + iobuf_t b = a->chain; + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: pop `%s' in underflow\n", + a->no, a->subno, a->desc); + xfree (a->d.buf); + xfree (a->real_fname); + memcpy (a, b, sizeof *a); + xfree (b); + print_chain (a); + } + else + a->filter_eof = 0; /* for the top level filter */ + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: underflow: eof (due to filter eof)\n", + a->no, a->subno); + return -1; /* return one(!) EOF */ + } + if (a->error) + { + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: error\n", a->no, a->subno); + return -1; + } + + if (a->directfp) + { + FILE *fp = a->directfp; + + len = fread (a->d.buf, 1, a->d.size, fp); + if (len < a->d.size) + { + if (ferror (fp)) + a->error = gpg_error_from_errno (errno); + } + a->d.len = len; + a->d.start = 0; + return len ? a->d.buf[a->d.start++] : -1; + } + + + if (a->filter) + { + len = a->d.size; + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: underflow: req=%lu\n", + a->no, a->subno, (ulong) len); + rc = a->filter (a->filter_ov, IOBUFCTRL_UNDERFLOW, a->chain, + a->d.buf, &len); + if (DBG_IOBUF) + { + log_debug ("iobuf-%d.%d: underflow: got=%lu rc=%d\n", + a->no, a->subno, (ulong) len, rc); +/* if( a->no == 1 ) */ +/* log_hexdump (" data:", a->d.buf, len); */ + } + if (a->use == 1 && rc == -1) + { /* EOF: we can remove the filter */ + size_t dummy_len = 0; + + /* and tell the filter to free itself */ + if ((rc = a->filter (a->filter_ov, IOBUFCTRL_FREE, a->chain, + NULL, &dummy_len))) + log_error ("IOBUFCTRL_FREE failed: %s\n", gpg_strerror (rc)); + if (a->filter_ov && a->filter_ov_owner) + { + xfree (a->filter_ov); + a->filter_ov = NULL; + } + a->filter = NULL; + a->desc = NULL; + a->filter_ov = NULL; + a->filter_eof = 1; + if (!len && a->chain) + { + iobuf_t b = a->chain; + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: pop `%s' in underflow (!len)\n", + a->no, a->subno, a->desc); + xfree (a->d.buf); + xfree (a->real_fname); + memcpy (a, b, sizeof *a); + xfree (b); + print_chain (a); + } + } + else if (rc) + a->error = rc; + + if (!len) + { + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: underflow: eof\n", a->no, a->subno); + return -1; + } + a->d.len = len; + a->d.start = 0; + return a->d.buf[a->d.start++]; + } + else + { + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: underflow: eof (no filter)\n", + a->no, a->subno); + return -1; /* no filter; return EOF */ + } +} + + +int +iobuf_flush (iobuf_t a) +{ + size_t len; + int rc; + + if (a->directfp) + return 0; + + if (a->use == 3) + { /* increase the temp buffer */ + char *newbuf; + size_t newsize = a->d.size + 8192; + + if (DBG_IOBUF) + log_debug ("increasing temp iobuf from %lu to %lu\n", + (ulong) a->d.size, (ulong) newsize); + newbuf = xmalloc (newsize); + memcpy (newbuf, a->d.buf, a->d.len); + xfree (a->d.buf); + a->d.buf = newbuf; + a->d.size = newsize; + return 0; + } + else if (a->use != 2) + log_bug ("flush on non-output iobuf\n"); + else if (!a->filter) + log_bug ("iobuf_flush: no filter\n"); + len = a->d.len; + rc = a->filter (a->filter_ov, IOBUFCTRL_FLUSH, a->chain, a->d.buf, &len); + if (!rc && len != a->d.len) + { + log_info ("iobuf_flush did not write all!\n"); + rc = GPG_ERR_INTERNAL; + } + else if (rc) + a->error = rc; + a->d.len = 0; + + return rc; +} + + +/**************** + * Read a byte from the iobuf; returns -1 on EOF + */ +int +iobuf_readbyte (iobuf_t a) +{ + int c; + + /* nlimit does not work together with unget */ + /* nbytes is also not valid! */ + if (a->unget.buf) + { + if (a->unget.start < a->unget.len) + return a->unget.buf[a->unget.start++]; + xfree (a->unget.buf); + a->unget.buf = NULL; + a->nofast &= ~2; + } + + if (a->nlimit && a->nbytes >= a->nlimit) + return -1; /* forced EOF */ + + if (a->d.start < a->d.len) + { + c = a->d.buf[a->d.start++]; + } + else if ((c = underflow (a)) == -1) + return -1; /* EOF */ + + a->nbytes++; + return c; +} + + +int +iobuf_read (iobuf_t a, byte * buf, unsigned buflen) +{ + int c, n; + + if (a->unget.buf || a->nlimit) + { + /* handle special cases */ + for (n = 0; n < buflen; n++) + { + if ((c = iobuf_readbyte (a)) == -1) + { + if (!n) + return -1; /* eof */ + break; + } + else if (buf) + *buf = c; + if (buf) + buf++; + } + return n; + } + + n = 0; + do + { + if (n < buflen && a->d.start < a->d.len) + { + unsigned size = a->d.len - a->d.start; + if (size > buflen - n) + size = buflen - n; + if (buf) + memcpy (buf, a->d.buf + a->d.start, size); + n += size; + a->d.start += size; + if (buf) + buf += size; + } + if (n < buflen) + { + if ((c = underflow (a)) == -1) + { + a->nbytes += n; + return n ? n : -1 /*EOF*/; + } + if (buf) + *buf++ = c; + n++; + } + } + while (n < buflen); + a->nbytes += n; + return n; +} + + +/**************** + * Have a look at the iobuf. + * NOTE: This only works in special cases. + */ +int +iobuf_peek (iobuf_t a, byte * buf, unsigned buflen) +{ + int n = 0; + + if (a->filter_eof) + return -1; + + if (!(a->d.start < a->d.len)) + { + if (underflow (a) == -1) + return -1; + /* and unget this character */ + assert (a->d.start == 1); + a->d.start = 0; + } + + for (n = 0; n < buflen && (a->d.start + n) < a->d.len; n++, buf++) + *buf = a->d.buf[n]; + return n; +} + + + + +int +iobuf_writebyte (iobuf_t a, unsigned c) +{ + int rc; + + if (a->directfp) + BUG (); + + if (a->d.len == a->d.size) + if ((rc=iobuf_flush (a))) + return rc; + + assert (a->d.len < a->d.size); + a->d.buf[a->d.len++] = c; + return 0; +} + + +int +iobuf_write (iobuf_t a, byte * buf, unsigned buflen) +{ + int rc; + + if (a->directfp) + BUG (); + + do + { + if (buflen && a->d.len < a->d.size) + { + unsigned size = a->d.size - a->d.len; + if (size > buflen) + size = buflen; + memcpy (a->d.buf + a->d.len, buf, size); + buflen -= size; + buf += size; + a->d.len += size; + } + if (buflen) + { + rc = iobuf_flush (a); + if (rc) + return rc; + } + } + while (buflen); + return 0; +} + + +int +iobuf_writestr (iobuf_t a, const char *buf) +{ + int rc; + + for (; *buf; buf++) + if ((rc=iobuf_writebyte (a, *buf))) + return rc; + return 0; +} + + + +/**************** + * copy the contents of TEMP to A. + */ +int +iobuf_write_temp (iobuf_t a, iobuf_t temp) +{ + while (temp->chain) + pop_filter (temp, temp->filter, NULL); + return iobuf_write (a, temp->d.buf, temp->d.len); +} + +/**************** + * copy the contents of the temp io stream to BUFFER. + */ +size_t +iobuf_temp_to_buffer (iobuf_t a, byte * buffer, size_t buflen) +{ + size_t n = a->d.len; + + if (n > buflen) + n = buflen; + memcpy (buffer, a->d.buf, n); + return n; +} + + +/**************** + * Call this function to terminate processing of the temp stream + * without closing it. This removes all filters from the stream + * makes sure that iobuf_get_temp_{buffer,length}() returns correct + * values. + */ +void +iobuf_flush_temp (iobuf_t temp) +{ + while (temp->chain) + pop_filter (temp, temp->filter, NULL); +} + + +/**************** + * Set a limit on how many bytes may be read from the input stream A. + * Setting the limit to 0 disables this feature. + */ +void +iobuf_set_limit (iobuf_t a, off_t nlimit) +{ + if (nlimit) + a->nofast |= 1; + else + a->nofast &= ~1; + a->nlimit = nlimit; + a->ntotal += a->nbytes; + a->nbytes = 0; +} + + + +/**************** + * Return the length of an open file + */ +off_t +iobuf_get_filelength (iobuf_t a) +{ + struct stat st; + + if (a->directfp) + { + FILE *fp = a->directfp; + + if (!fstat (fileno (fp), &st)) + return st.st_size; + log_error ("fstat() failed: %s\n", strerror (errno)); + return 0; + } + + /* Hmmm: file_filter may have already been removed */ + for (; a; a = a->chain) + if (!a->chain && a->filter == file_filter) + { + file_filter_ctx_t *b = a->filter_ov; + FILEP_OR_FD fp = b->fp; + +#if defined(HAVE_DOSISH_SYSTEM) && !defined(FILE_FILTER_USES_STDIO) + ulong size; + + if ((size = GetFileSize (fp, NULL)) != 0xffffffff) + return size; + log_error ("GetFileSize for handle %p failed: ec=%d\n", + fp, (int) GetLastError ()); +#else + if (!fstat (my_fileno (fp), &st)) + return st.st_size; + log_error ("fstat() failed: %s\n", strerror (errno)); +#endif + break; + } + + return 0; +} + +/**************** + * Tell the file position, where the next read will take place + */ +off_t +iobuf_tell (iobuf_t a) +{ + return a->ntotal + a->nbytes; +} + + +#if !defined(HAVE_FSEEKO) && !defined(fseeko) + +#ifdef HAVE_LIMITS_H +# include +#endif +#ifndef LONG_MAX +# define LONG_MAX ((long) ((unsigned long) -1 >> 1)) +#endif +#ifndef LONG_MIN +# define LONG_MIN (-1 - LONG_MAX) +#endif + +/**************** + * A substitute for fseeko, for hosts that don't have it. + */ +static int +fseeko (FILE * stream, off_t newpos, int whence) +{ + while (newpos != (long) newpos) + { + long pos = newpos < 0 ? LONG_MIN : LONG_MAX; + if (fseek (stream, pos, whence) != 0) + return -1; + newpos -= pos; + whence = SEEK_CUR; + } + return fseek (stream, (long) newpos, whence); +} +#endif + +/**************** + * This is a very limited implementation. It simply discards all internal + * buffering and removes all filters but the first one. + */ +int +iobuf_seek (iobuf_t a, off_t newpos) +{ + file_filter_ctx_t *b = NULL; + + if (a->directfp) + { + FILE *fp = a->directfp; + if (fseeko (fp, newpos, SEEK_SET)) + { + log_error ("can't seek: %s\n", strerror (errno)); + return -1; + } + clearerr (fp); + } + else + { + for (; a; a = a->chain) + { + if (!a->chain && a->filter == file_filter) + { + b = a->filter_ov; + break; + } + } + if (!a) + return -1; +#ifdef FILE_FILTER_USES_STDIO + if (fseeko (b->fp, newpos, SEEK_SET)) + { + log_error ("can't fseek: %s\n", strerror (errno)); + return -1; + } +#else +#ifdef HAVE_DOSISH_SYSTEM + if (SetFilePointer (b->fp, newpos, NULL, FILE_BEGIN) == 0xffffffff) + { + log_error ("SetFilePointer failed on handle %p: ec=%d\n", + b->fp, (int) GetLastError ()); + return -1; + } +#else + if (lseek (b->fp, newpos, SEEK_SET) == (off_t) - 1) + { + log_error ("can't lseek: %s\n", strerror (errno)); + return -1; + } +#endif +#endif + } + a->d.len = 0; /* discard buffer */ + a->d.start = 0; + a->nbytes = 0; + a->nlimit = 0; + a->nofast &= ~1; + a->ntotal = newpos; + a->error = 0; + /* remove filters, but the last */ + if (a->chain) + log_debug ("pop_filter called in iobuf_seek - please report\n"); + while (a->chain) + pop_filter (a, a->filter, NULL); + + return 0; +} + + + + + + +/**************** + * Retrieve the real filename + */ +const char * +iobuf_get_real_fname (iobuf_t a) +{ + if (a->real_fname) + return a->real_fname; + + /* the old solution */ + for (; a; a = a->chain) + if (!a->chain && a->filter == file_filter) + { + file_filter_ctx_t *b = a->filter_ov; + return b->print_only_name ? NULL : b->fname; + } + + return NULL; +} + + +/**************** + * Retrieve the filename + */ +const char * +iobuf_get_fname (iobuf_t a) +{ + for (; a; a = a->chain) + if (!a->chain && a->filter == file_filter) + { + file_filter_ctx_t *b = a->filter_ov; + return b->fname; + } + + return NULL; +} + +/**************** + * Start the block write mode, see rfc1991.new for details. + * A value of 0 for N stops this mode (flushes and writes + * the end marker) + */ +void +iobuf_set_block_mode (iobuf_t a, size_t n) +{ + block_filter_ctx_t *ctx = xcalloc (1, sizeof *ctx); + + assert (a->use == 1 || a->use == 2); + ctx->use = a->use; + if (!n) + { + if (a->use == 1) + log_debug ("pop_filter called in set_block_mode - please report\n"); + pop_filter (a, block_filter, NULL); + } + else + { + ctx->size = n; /* only needed for use 2 */ + iobuf_push_filter (a, block_filter, ctx); + } +} + +/**************** + * enable partial block mode as described in the OpenPGP draft. + * LEN is the first length byte on read, but ignored on writes. + */ +void +iobuf_set_partial_block_mode (iobuf_t a, size_t len) +{ + block_filter_ctx_t *ctx = xcalloc (1, sizeof *ctx); + + assert (a->use == 1 || a->use == 2); + ctx->use = a->use; + if (!len) + { + if (a->use == 1) + log_debug ("pop_filter called in set_partial_block_mode" + " - please report\n"); + pop_filter (a, block_filter, NULL); + } + else + { + ctx->partial = 1; + ctx->size = 0; + ctx->first_c = len; + iobuf_push_filter (a, block_filter, ctx); + } +} + + +/**************** + * Checks whether the stream is in block mode + * Note: This does not work if other filters are pushed on the stream. + */ +int +iobuf_in_block_mode (iobuf_t a) +{ + if (a && a->filter == block_filter) + return 1; /* yes */ + return 0; /* no */ +} + + +/**************** + * Same as fgets() but if the buffer is too short a larger one will + * be allocated up to some limit *max_length. + * A line is considered a byte stream ending in a LF. + * Returns the length of the line. EOF is indicated by a line of + * length zero. The last LF may be missing due to an EOF. + * is max_length is zero on return, the line has been truncated. + * + * Note: The buffer is allocated with enough space to append a CR,LF,EOL + */ +unsigned int +iobuf_read_line (iobuf_t a, byte ** addr_of_buffer, + unsigned *length_of_buffer, unsigned *max_length) +{ + int c; + char *buffer = *addr_of_buffer; + unsigned length = *length_of_buffer; + unsigned nbytes = 0; + unsigned maxlen = *max_length; + char *p; + + if (!buffer) + { /* must allocate a new buffer */ + length = 256; + buffer = xmalloc (length); + *addr_of_buffer = buffer; + *length_of_buffer = length; + } + + length -= 3; /* reserve 3 bytes (cr,lf,eol) */ + p = buffer; + while ((c = iobuf_get (a)) != -1) + { + if (nbytes == length) + { /* increase the buffer */ + if (length > maxlen) + { /* this is out limit */ + /* skip the rest of the line */ + while (c != '\n' && (c = iobuf_get (a)) != -1) + ; + *p++ = '\n'; /* always append a LF (we have reserved space) */ + nbytes++; + *max_length = 0; /* indicate truncation */ + break; + } + length += 3; /* correct for the reserved byte */ + length += length < 1024 ? 256 : 1024; + buffer = xrealloc (buffer, length); + *addr_of_buffer = buffer; + *length_of_buffer = length; + length -= 3; /* and reserve again */ + p = buffer + nbytes; + } + *p++ = c; + nbytes++; + if (c == '\n') + break; + } + *p = 0; /* make sure the line is a string */ + + return nbytes; +} + +/* This is the non iobuf specific function */ +int +iobuf_translate_file_handle (int fd, int for_write) +{ +#ifdef __MINGW32__ + { + int x; + + if (fd <= 2) + return fd; /* do not do this for error, stdin, stdout, stderr */ + + x = _open_osfhandle (fd, for_write ? 1 : 0); + if (x == -1) + log_error ("failed to translate osfhandle %p\n", (void *) fd); + else + { + /*log_info ("_open_osfhandle %p yields %d%s\n", + (void*)fd, x, for_write? " for writing":"" ); */ + fd = x; + } + } +#endif + return fd; +} + +static int +translate_file_handle (int fd, int for_write) +{ +#ifdef __MINGW32__ +#ifdef FILE_FILTER_USES_STDIO + fd = iobuf_translate_file_handle (fd, for_write); +#else + { + int x; + + if (fd == 0) + x = (int) GetStdHandle (STD_INPUT_HANDLE); + else if (fd == 1) + x = (int) GetStdHandle (STD_OUTPUT_HANDLE); + else if (fd == 2) + x = (int) GetStdHandle (STD_ERROR_HANDLE); + else + x = fd; + + if (x == -1) + log_debug ("GetStdHandle(%d) failed: ec=%d\n", + fd, (int) GetLastError ()); + + fd = x; + } +#endif +#endif + return fd; +} diff --git a/common/iobuf.h b/common/iobuf.h new file mode 100644 index 000000000..0af94e22d --- /dev/null +++ b/common/iobuf.h @@ -0,0 +1,170 @@ +/* iobuf.h - I/O buffer + * Copyright (C) 1998, 1999, 2000, 2001, 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 GNUPG_COMMON_IOBUF_H +#define GNUPG_COMMON_IOBUF_H + +#include "../include/types.h" /* fixme: should be moved elsewhere. */ + + +#define DBG_IOBUF iobuf_debug_mode + + +#define IOBUFCTRL_INIT 1 +#define IOBUFCTRL_FREE 2 +#define IOBUFCTRL_UNDERFLOW 3 +#define IOBUFCTRL_FLUSH 4 +#define IOBUFCTRL_DESC 5 +#define IOBUFCTRL_CANCEL 6 +#define IOBUFCTRL_USER 16 + +typedef struct iobuf_struct *iobuf_t; + +/* fixme: we should hide most of this stuff */ +struct iobuf_struct +{ + int use; /* 1 input , 2 output, 3 temp */ + off_t nlimit; + off_t nbytes; /* used together with nlimit */ + off_t ntotal; /* total bytes read (position of stream) */ + int nofast; /* used by the iobuf_get() */ + void *directfp; + struct + { + size_t size; /* allocated size */ + size_t start; /* number of invalid bytes at the begin of the buffer */ + size_t len; /* currently filled to this size */ + byte *buf; + } + d; + int filter_eof; + int error; + int (*filter) (void *opaque, int control, + iobuf_t chain, byte * buf, size_t * len); + void *filter_ov; /* value for opaque */ + int filter_ov_owner; + char *real_fname; + iobuf_t chain; /* next iobuf used for i/o if any + (passed to filter) */ + int no, subno; + const char *desc; + void *opaque; /* can be used to hold any information + this value is copied to all + instances */ + struct + { + size_t size; /* allocated size */ + size_t start; /* number of invalid bytes at the + begin of the buffer */ + size_t len; /* currently filled to this size */ + byte *buf; + } + unget; +}; + +#ifndef EXTERN_UNLESS_MAIN_MODULE +#if defined (__riscos__) && !defined (INCLUDED_BY_MAIN_MODULE) +#define EXTERN_UNLESS_MAIN_MODULE extern +#else +#define EXTERN_UNLESS_MAIN_MODULE +#endif +#endif +EXTERN_UNLESS_MAIN_MODULE int iobuf_debug_mode; + +void iobuf_enable_special_filenames (int yes); +iobuf_t iobuf_alloc (int use, size_t bufsize); +iobuf_t iobuf_temp (void); +iobuf_t iobuf_temp_with_content (const char *buffer, size_t length); +iobuf_t iobuf_open (const char *fname); +iobuf_t iobuf_fdopen (int fd, const char *mode); +iobuf_t iobuf_sockopen (int fd, const char *mode); +iobuf_t iobuf_create (const char *fname); +iobuf_t iobuf_append (const char *fname); +iobuf_t iobuf_openrw (const char *fname); +int iobuf_ioctl (iobuf_t a, int cmd, int intval, void *ptrval); +int iobuf_close (iobuf_t iobuf); +int iobuf_cancel (iobuf_t iobuf); + +int iobuf_push_filter (iobuf_t a, int (*f) (void *opaque, int control, + iobuf_t chain, byte * buf, + size_t * len), void *ov); +int iobuf_push_filter2 (iobuf_t a, + int (*f) (void *opaque, int control, iobuf_t chain, + byte * buf, size_t * len), void *ov, + int rel_ov); +int iobuf_flush (iobuf_t a); +void iobuf_clear_eof (iobuf_t a); +#define iobuf_set_error(a) do { (a)->error = 1; } while(0) +#define iobuf_error(a) ((a)->error) + +void iobuf_set_limit (iobuf_t a, off_t nlimit); + +off_t iobuf_tell (iobuf_t a); +int iobuf_seek (iobuf_t a, off_t newpos); + +int iobuf_readbyte (iobuf_t a); +int iobuf_read (iobuf_t a, byte * buf, unsigned buflen); +unsigned iobuf_read_line (iobuf_t a, byte ** addr_of_buffer, + unsigned *length_of_buffer, unsigned *max_length); +int iobuf_peek (iobuf_t a, byte * buf, unsigned buflen); +int iobuf_writebyte (iobuf_t a, unsigned c); +int iobuf_write (iobuf_t a, byte * buf, unsigned buflen); +int iobuf_writestr (iobuf_t a, const char *buf); + +void iobuf_flush_temp (iobuf_t temp); +int iobuf_write_temp (iobuf_t a, iobuf_t temp); +size_t iobuf_temp_to_buffer (iobuf_t a, byte * buffer, size_t buflen); +void iobuf_unget_and_close_temp (iobuf_t a, iobuf_t temp); + +off_t iobuf_get_filelength (iobuf_t a); +#define IOBUF_FILELENGTH_LIMIT 0xffffffff +const char *iobuf_get_real_fname (iobuf_t a); +const char *iobuf_get_fname (iobuf_t a); + +void iobuf_set_block_mode (iobuf_t a, size_t n); +void iobuf_set_partial_block_mode (iobuf_t a, size_t len); +int iobuf_in_block_mode (iobuf_t a); + +int iobuf_translate_file_handle (int fd, int for_write); + + +/* get a byte form the iobuf; must check for eof prior to this function + * this function returns values in the range 0 .. 255 or -1 to indicate EOF + * iobuf_get_noeof() does not return -1 to indicate EOF, but masks the + * returned value to be in the range 0 ..255. + */ +#define iobuf_get(a) \ + ( ((a)->nofast || (a)->d.start >= (a)->d.len )? \ + iobuf_readbyte((a)) : ( (a)->nbytes++, (a)->d.buf[(a)->d.start++] ) ) +#define iobuf_get_noeof(a) (iobuf_get((a))&0xff) + +/* write a byte to the iobuf and return true on write error + * This macro does only write the low order byte + */ +#define iobuf_put(a,c) iobuf_writebyte(a,c) + +#define iobuf_where(a) "[don't know]" +#define iobuf_id(a) ((a)->no) + +#define iobuf_get_temp_buffer(a) ( (a)->d.buf ) +#define iobuf_get_temp_length(a) ( (a)->d.len ) +#define iobuf_is_temp(a) ( (a)->use == 3 ) + +#endif /*GNUPG_COMMON_IOBUF_H*/ diff --git a/common/maperror.c b/common/maperror.c new file mode 100644 index 000000000..13657bece --- /dev/null +++ b/common/maperror.c @@ -0,0 +1,157 @@ +/* maperror.c - Error mapping + * Copyright (C) 2001, 2002 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 + +#include +#include + +#include "util.h" +#include "errors.h" + +/* Note: we might want to wrap this in a macro to get our hands on + the line and file where the error occured */ +int +map_ksba_err (int err) +{ + switch (err) + { + case -1: + case 0: + break; + + case KSBA_Out_Of_Core: err = GPG_ERR_ENOMEM; break; + case KSBA_Invalid_Value: err = GPG_ERR_INV_VALUE; break; + case KSBA_Not_Implemented: err = GPG_ERR_NOT_IMPLEMENTED; break; + case KSBA_Conflict: err = GPG_ERR_CONFLICT; break; + case KSBA_Read_Error: err = GPG_ERR_EIO; break; + case KSBA_Write_Error: err = GPG_ERR_EIO; break; + case KSBA_No_Data: err = GPG_ERR_NO_DATA; break; + case KSBA_Bug: err = GPG_ERR_BUG; break; + case KSBA_Unsupported_Algorithm: err = GPG_ERR_UNSUPPORTED_ALGORITHM; break; + case KSBA_Invalid_Index: err = GPG_ERR_INV_INDEX; break; + case KSBA_Invalid_Sexp: err = GPG_ERR_INV_SEXP; break; + case KSBA_Unknown_Sexp: err = GPG_ERR_UNKNOWN_SEXP; break; + + default: + err = GPG_ERR_GENERAL; + break; + } + return err; +} + + +int +map_gcry_err (int err) +{ + return err; +} + +int +map_kbx_err (int err) +{ + return err; +} + +/* Map Assuan error code ERR to an GPG_ERR_ code. We need to + distinguish between genuine (and legacy) Assuan error codes and + application error codes shared with all GnuPG modules. The rule is + simple: All errors with a gpg_err_source of UNKNOWN are genuine + Assuan codes all others are passed verbatim through. */ +gpg_error_t +map_assuan_err (int err) +{ + gpg_err_code_t ec; + + if (gpg_err_source (err)) + return err; + + switch (err) + { + case -1: ec = GPG_ERR_EOF; break; + case 0: ec = 0; break; + + case ASSUAN_Canceled: ec = GPG_ERR_CANCELED; break; + case ASSUAN_Invalid_Index: ec = GPG_ERR_INV_INDEX; break; + + case ASSUAN_Not_Implemented: ec = GPG_ERR_NOT_IMPLEMENTED; break; + case ASSUAN_Server_Fault: ec = GPG_ERR_ASSUAN_SERVER_FAULT; break; + case ASSUAN_No_Public_Key: ec = GPG_ERR_NO_PUBKEY; break; + case ASSUAN_No_Secret_Key: ec = GPG_ERR_NO_SECKEY; break; + + case ASSUAN_Cert_Revoked: ec = GPG_ERR_CERT_REVOKED; break; + case ASSUAN_No_CRL_For_Cert: ec = GPG_ERR_NO_CRL_KNOWN; break; + case ASSUAN_CRL_Too_Old: ec = GPG_ERR_CRL_TOO_OLD; break; + + case ASSUAN_Not_Trusted: ec = GPG_ERR_NOT_TRUSTED; break; + + case ASSUAN_Card_Error: ec = GPG_ERR_CARD; break; + case ASSUAN_Invalid_Card: ec = GPG_ERR_INV_CARD; break; + case ASSUAN_No_PKCS15_App: ec = GPG_ERR_NO_PKCS15_APP; break; + case ASSUAN_Card_Not_Present: ec= GPG_ERR_CARD_NOT_PRESENT; break; + case ASSUAN_Not_Confirmed: ec = GPG_ERR_NOT_CONFIRMED; break; + case ASSUAN_Invalid_Id: ec = GPG_ERR_INV_ID; break; + + default: + ec = err < 100? GPG_ERR_ASSUAN_SERVER_FAULT : GPG_ERR_ASSUAN; + break; + } + return gpg_err_make (GPG_ERR_SOURCE_UNKNOWN, ec); +} + +/* Map GPG_xERR_xx error codes to Assuan status codes */ +int +map_to_assuan_status (int rc) +{ + gpg_err_code_t ec = gpg_err_code (rc); + gpg_err_source_t es = gpg_err_source (rc); + + if (!rc) + return 0; + if (!es) + { + es = GPG_ERR_SOURCE_USER_4; /* This should not happen, but we + need to make sure to pass a new + Assuan errorcode along. */ + log_debug ("map_to_assuan_status called with no error source\n"); + } + + if (ec == -1) + ec = GPG_ERR_NO_DATA; /* That used to be ASSUAN_No_Data_Available. */ + + return gpg_err_make (es, ec); +} + + + + + + + + + + + diff --git a/common/membuf.c b/common/membuf.c new file mode 100644 index 000000000..69e4ab908 --- /dev/null +++ b/common/membuf.c @@ -0,0 +1,89 @@ +/* membuf.c - A simple implementation of a dynamic buffer + * Copyright (C) 2001, 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 "membuf.h" + +#include "util.h" + + +/* A simple implementation of a dynamic buffer. Use init_membuf() to + create a buffer, put_membuf to append bytes and get_membuf to + release and return the buffer. Allocation errors are detected but + only returned at the final get_membuf(), this helps not to clutter + the code with out of core checks. */ + +void +init_membuf (membuf_t *mb, int initiallen) +{ + mb->len = 0; + mb->size = initiallen; + mb->out_of_core = 0; + mb->buf = xtrymalloc (initiallen); + if (!mb->buf) + mb->out_of_core = errno; +} + + +void +put_membuf (membuf_t *mb, const void *buf, size_t len) +{ + if (mb->out_of_core) + return; + + if (mb->len + len >= mb->size) + { + char *p; + + mb->size += len + 1024; + p = xtryrealloc (mb->buf, mb->size); + if (!p) + { + mb->out_of_core = errno; + return; + } + mb->buf = p; + } + memcpy (mb->buf + mb->len, buf, len); + mb->len += len; +} + + +void * +get_membuf (membuf_t *mb, size_t *len) +{ + char *p; + + if (mb->out_of_core) + { + xfree (mb->buf); + mb->buf = NULL; + return NULL; + } + + p = mb->buf; + *len = mb->len; + mb->buf = NULL; + mb->out_of_core = ENOMEM; /* hack to make sure it won't get reused. */ + return p; +} diff --git a/common/membuf.h b/common/membuf.h new file mode 100644 index 000000000..c199363cc --- /dev/null +++ b/common/membuf.h @@ -0,0 +1,41 @@ +/* membuf.h - A simple implementation of a dynamic buffer + * Copyright (C) 2001, 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 GNUPG_COMMON_MEMBUF_H +#define GNUPG_COMMON_MEMBUF_H + +/* The definition of the structure is private, we only need it here, + so it can be allocated on the stack. */ +struct private_membuf_s { + size_t len; + size_t size; + char *buf; + int out_of_core; +}; + +typedef struct private_membuf_s membuf_t; + + +void init_membuf (membuf_t *mb, int initiallen); +void put_membuf (membuf_t *mb, const void *buf, size_t len); +void *get_membuf (membuf_t *mb, size_t *len); + + +#endif /*GNUPG_COMMON_MEMBUF_H*/ diff --git a/common/miscellaneous.c b/common/miscellaneous.c new file mode 100644 index 000000000..bdb12c574 --- /dev/null +++ b/common/miscellaneous.c @@ -0,0 +1,126 @@ +/* miscellaneous.c - Stuff not fitting elsewhere + * 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 "util.h" +#include "iobuf.h" + +/* Decide whether the filename is stdout or a real filename and return + * an appropriate string. */ +const char * +print_fname_stdout (const char *s) +{ + if( !s || (*s == '-' && !s[1]) ) + return "[stdout]"; + return s; +} + + +/* Decide whether the filename is stdin or a real filename and return + * an appropriate string. */ +const char * +print_fname_stdin (const char *s) +{ + if( !s || (*s == '-' && !s[1]) ) + return "[stdin]"; + return s; +} + +void +print_string( FILE *fp, const byte *p, size_t n, int delim ) +{ + print_sanitized_buffer (fp, p, n, delim); +} + +void +print_utf8_string2 ( FILE *fp, const byte *p, size_t n, int delim ) +{ + print_sanitized_utf8_buffer (fp, p, n, delim); +} + +void +print_utf8_string( FILE *fp, const byte *p, size_t n ) +{ + print_utf8_string2 (fp, p, n, 0); +} + +char * +make_printable_string( const byte *p, size_t n, int delim ) +{ + return sanitize_buffer (p, n, delim); +} + + +/* + * Check if the file is compressed. + */ +int +is_file_compressed (const char *s, int *ret_rc) +{ + iobuf_t a; + byte buf[4]; + int i, rc = 0; + + struct magic_compress_s { + size_t len; + byte magic[4]; + } magic[] = { + { 3, { 0x42, 0x5a, 0x68, 0x00 } }, /* bzip2 */ + { 3, { 0x1f, 0x8b, 0x08, 0x00 } }, /* gzip */ + { 4, { 0x50, 0x4b, 0x03, 0x04 } }, /* (pk)zip */ + }; + + if ( !s || (*s == '-' && !s[1]) || !ret_rc ) + return 0; /* We can't check stdin or no file was given */ + + a = iobuf_open( s ); + if ( a == NULL ) { + *ret_rc = gpg_error_from_errno (errno); + return 0; + } + + if ( iobuf_get_filelength( a ) < 4 ) { + *ret_rc = 0; + goto leave; + } + + if ( iobuf_read( a, buf, 4 ) == -1 ) { + *ret_rc = a->error; + goto leave; + } + + for ( i = 0; i < DIM( magic ); i++ ) { + if ( !memcmp( buf, magic[i].magic, magic[i].len ) ) { + *ret_rc = 0; + rc = 1; + break; + } + } + +leave: + iobuf_close( a ); + return rc; +} + + + diff --git a/common/simple-pwquery.c b/common/simple-pwquery.c new file mode 100644 index 000000000..afdc4e2a4 --- /dev/null +++ b/common/simple-pwquery.c @@ -0,0 +1,486 @@ +/* simple-pwquery.c - A simple password query cleint for gpg-agent + * Copyright (C) 2002 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 + */ + +/* This module is intended as a standalone client implementation to + gpg-agent's GET_PASSPHRASE command. In particular it does not use + the Assuan library and can only cope with an already running + gpg-agent. Some stuff is configurable in the header file. */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LOCALE_H +#include +#endif + +#define SIMPLE_PWQUERY_IMPLEMENTATION 1 +#include "simple-pwquery.h" + +#if defined(SPWQ_USE_LOGGING) && !defined(HAVE_JNLIB_LOGGING) +# undef SPWQ_USE_LOGGING +#endif + +#ifndef _ +#define _(a) (a) +#endif + +#if !defined (hexdigitp) && !defined (xtoi_2) +#define digitp(p) (*(p) >= '0' && *(p) <= '9') +#define hexdigitp(a) (digitp (a) \ + || (*(a) >= 'A' && *(a) <= 'F') \ + || (*(a) >= 'a' && *(a) <= 'f')) +#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ + *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) +#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) +#endif + + +/* Write NBYTES of BUF to file descriptor FD. */ +static int +writen (int fd, const void *buf, size_t nbytes) +{ + size_t nleft = nbytes; + int nwritten; + + while (nleft > 0) + { + nwritten = write( fd, buf, nleft ); + if (nwritten < 0) + { + if (errno == EINTR) + nwritten = 0; + else { +#ifdef SPWQ_USE_LOGGING + log_error ("write failed: %s\n", strerror (errno)); +#endif + return SPWQ_IO_ERROR; + } + } + nleft -= nwritten; + buf = (const char*)buf + nwritten; + } + + return 0; +} + + +/* Read an entire line and return number of bytes read. */ +static int +readline (int fd, char *buf, size_t buflen) +{ + size_t nleft = buflen; + char *p; + int nread = 0; + + while (nleft > 0) + { + int n = read (fd, buf, nleft); + if (n < 0) + { + if (errno == EINTR) + continue; + return -(SPWQ_IO_ERROR); + } + else if (!n) + { + return -(SPWQ_PROTOCOL_ERROR); /* incomplete line */ + } + p = buf; + nleft -= n; + buf += n; + nread += n; + + for (; n && *p != '\n'; n--, p++) + ; + if (n) + { + break; /* at least one full line available - that's enough. + This function is just a simple implementation, so + it is okay to forget about pending bytes */ + } + } + + return nread; +} + + +/* Send an option to the agent */ +static int +agent_send_option (int fd, const char *name, const char *value) +{ + char buf[200]; + int nread; + char *line; + int i; + + line = spwq_malloc (7 + strlen (name) + 1 + strlen (value) + 2); + if (!line) + return SPWQ_OUT_OF_CORE; + strcpy (stpcpy (stpcpy (stpcpy ( + stpcpy (line, "OPTION "), name), "="), value), "\n"); + i = writen (fd, line, strlen (line)); + spwq_free (line); + if (i) + return i; + + /* get response */ + nread = readline (fd, buf, DIM(buf)-1); + if (nread < 0) + return -nread; + if (nread < 3) + return SPWQ_PROTOCOL_ERROR; + + if (buf[0] == 'O' && buf[1] == 'K' && (buf[2] == ' ' || buf[2] == '\n')) + return 0; /* okay */ + + return SPWQ_ERR_RESPONSE; +} + + +/* Send all available options to the agent. */ +static int +agent_send_all_options (int fd) +{ + char *dft_display = NULL; + char *dft_ttyname = NULL; + char *dft_ttytype = NULL; + int rc = 0; + + dft_display = getenv ("DISPLAY"); + if (dft_display) + { + if ((rc = agent_send_option (fd, "display", dft_display))) + return rc; + } + + dft_ttyname = getenv ("GPG_TTY"); + if ((!dft_ttyname || !*dft_ttyname) && ttyname (0)) + dft_ttyname = ttyname (0); + if (dft_ttyname && *dft_ttyname) + { + if ((rc=agent_send_option (fd, "ttyname", dft_ttyname))) + return rc; + } + + dft_ttytype = getenv ("TERM"); + if (dft_ttyname && dft_ttytype) + { + if ((rc = agent_send_option (fd, "ttytype", dft_ttytype))) + return rc; + } + +#if defined(HAVE_SETLOCALE) + { + char *old_lc = NULL; + char *dft_lc = NULL; + +#if defined(LC_CTYPE) + old_lc = setlocale (LC_CTYPE, NULL); + if (old_lc) + { + char *p = spwq_malloc (strlen (old_lc)+1); + if (!p) + return SPWQ_OUT_OF_CORE; + strcpy (p, old_lc); + old_lc = p; + } + dft_lc = setlocale (LC_CTYPE, ""); + if (dft_ttyname && dft_lc) + rc = agent_send_option (fd, "lc-ctype", dft_lc); + if (old_lc) + { + setlocale (LC_CTYPE, old_lc); + spwq_free (old_lc); + } + if (rc) + return rc; +#endif + +#if defined(LC_MESSAGES) + old_lc = setlocale (LC_MESSAGES, NULL); + if (old_lc) + { + char *p = spwq_malloc (strlen (old_lc)+1); + if (!p) + return SPWQ_OUT_OF_CORE; + strcpy (p, old_lc); + old_lc = p; + } + dft_lc = setlocale (LC_MESSAGES, ""); + if (dft_ttyname && dft_lc) + rc = agent_send_option (fd, "lc-messages", dft_lc); + if (old_lc) + { + setlocale (LC_MESSAGES, old_lc); + spwq_free (old_lc); + } + if (rc) + return rc; +#endif + } +#endif /*HAVE_SETLOCALE*/ + + return 0; +} + + + +/* Try to open a connection to the agent, send all options and return + the file descriptor for the connection. Return -1 in case of + error. */ +static int +agent_open (int *rfd) +{ + int rc; + int fd; + char *infostr, *p; + struct sockaddr_un client_addr; + size_t len; + int prot; + char line[200]; + int nread; + + *rfd = -1; + infostr = getenv ( "GPG_AGENT_INFO" ); + if ( !infostr ) + { +#ifdef SPWQ_USE_LOGGING + log_error (_("gpg-agent is not available in this session\n")); +#endif + return SPWQ_NO_AGENT; + } + + if ( !(p = strchr ( infostr, ':')) || p == infostr + || (p-infostr)+1 >= sizeof client_addr.sun_path ) + { +#ifdef SPWQ_USE_LOGGING + log_error ( _("malformed GPG_AGENT_INFO environment variable\n")); +#endif + return SPWQ_NO_AGENT; + } + *p++ = 0; + + while (*p && *p != ':') + p++; + prot = *p? atoi (p+1) : 0; + if ( prot != 1) + { +#ifdef SPWQ_USE_LOGGING + log_error (_("gpg-agent protocol version %d is not supported\n"),prot); +#endif + return SPWQ_PROTOCOL_ERROR; + } + + if( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1 ) + { +#ifdef SPWQ_USE_LOGGING + log_error ("can't create socket: %s\n", strerror(errno) ); +#endif + return SPWQ_SYS_ERROR; + } + + memset (&client_addr, 0, sizeof client_addr); + client_addr.sun_family = AF_UNIX; + strcpy (client_addr.sun_path, infostr); + len = (offsetof (struct sockaddr_un, sun_path) + + strlen(client_addr.sun_path) + 1); + + if (connect (fd, (struct sockaddr*)&client_addr, len ) == -1) + { +#ifdef SPWQ_USE_LOGGING + log_error ( _("can't connect to `%s': %s\n"), infostr, strerror (errno)); +#endif + close (fd ); + return SPWQ_IO_ERROR; + } + + nread = readline (fd, line, DIM(line)); + if (nread < 3 || !(line[0] == 'O' && line[1] == 'K' + && (line[2] == '\n' || line[2] == ' ')) ) + { +#ifdef SPWQ_USE_LOGGING + log_error ( _("communication problem with gpg-agent\n")); +#endif + close (fd ); + return SPWQ_PROTOCOL_ERROR; + } + + rc = agent_send_all_options (fd); + if (rc) + { +#ifdef SPWQ_USE_LOGGING + log_error (_("problem setting the gpg-agent options\n")); +#endif + close (fd); + return rc; + } + + *rfd = fd; + return 0; +} + + +/* Copy text to BUFFER and escape as required. Return a poiinter to + the end of the new buffer. NOte that BUFFER must be large enough + to keep the entire text; allocataing it 3 times the size of TEXT + is sufficient. */ +static char * +copy_and_escape (char *buffer, const char *text) +{ + int i; + char *p = buffer; + + for (i=0; text[i]; i++) + { + if (text[i] < ' ' || text[i] == '+') + { + sprintf (p, "%%%02X", text[i]); + p += 3; + } + else if (text[i] == ' ') + *p++ = '+'; + else + *p++ = text[i]; + } + return p; +} + + +/* Ask the gpg-agent for a passphrase and present the user with a + DESCRIPTION, a PROMPT and optiaonlly with a TRYAGAIN extra text. + If a CACHEID is not NULL it is used to locate the passphrase in in + the cache and store it under this ID. If ERRORCODE is not NULL it + should point a variable receiving an errorcode; thsi errocode might + be 0 if the user canceled the operation. The function returns NULL + to indicate an error. */ +char * +simple_pwquery (const char *cacheid, + const char *tryagain, + const char *prompt, + const char *description, + int *errorcode) +{ + int fd = -1; + int nread; + char *result = NULL; + char *pw = NULL; + char *p; + int rc, i; + + rc = agent_open (&fd); + if (rc) + goto leave; + + if (!cacheid) + cacheid = "X"; + if (!tryagain) + tryagain = "X"; + if (!prompt) + prompt = "X"; + if (!description) + description = "X"; + + { + char *line; + /* We allocate 3 times the needed space so that there is enough + space for escaping. */ + line = spwq_malloc (15 + + 3*strlen (cacheid) + 1 + + 3*strlen (tryagain) + 1 + + 3*strlen (prompt) + 1 + + 3*strlen (description) + 1 + + 2); + if (!line) + { + rc = SPWQ_OUT_OF_CORE; + goto leave; + } + strcpy (line, "GET_PASSPHRASE "); + p = line+15; + p = copy_and_escape (p, cacheid); + *p++ = ' '; + p = copy_and_escape (p, tryagain); + *p++ = ' '; + p = copy_and_escape (p, prompt); + *p++ = ' '; + p = copy_and_escape (p, description); + *p++ = '\n'; + rc = writen (fd, line, p - line); + spwq_free (line); + if (rc) + goto leave; + } + + /* get response */ + pw = spwq_secure_malloc (500); + nread = readline (fd, pw, 499); + if (nread < 0) + { + rc = -nread; + goto leave; + } + if (nread < 3) + { + rc = SPWQ_PROTOCOL_ERROR; + goto leave; + } + + if (pw[0] == 'O' && pw[1] == 'K' && pw[2] == ' ') + { /* we got a passphrase - convert it back from hex */ + size_t pwlen = 0; + + for (i=3; i < nread && hexdigitp (pw+i); i+=2) + pw[pwlen++] = xtoi_2 (pw+i); + pw[pwlen] = 0; /* make a C String */ + result = pw; + pw = NULL; + } + else if (nread > 7 && !memcmp (pw, "ERR 111", 7) + && (pw[7] == ' ' || pw[7] == '\n') ) + { +#ifdef SPWQ_USE_LOGGING + log_info (_("canceled by user\n") ); +#endif + *errorcode = 0; /* canceled */ + } + else + { +#ifdef SPWQ_USE_LOGGING + log_error (_("problem with the agent\n")); +#endif + rc = SPWQ_ERR_RESPONSE; + } + + leave: + if (errorcode) + *errorcode = rc; + if (fd != -1) + close (fd); + if (pw) + spwq_free (pw); + return result; +} diff --git a/common/simple-pwquery.h b/common/simple-pwquery.h new file mode 100644 index 000000000..5947c42b5 --- /dev/null +++ b/common/simple-pwquery.h @@ -0,0 +1,69 @@ +/* simple-pwquery.c - A simple password query cleint for gpg-agent + * Copyright (C) 2002 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 SIMPLE_PWQUERY_H +#define SIMPLE_PWQUERY_H + +#ifdef SIMPLE_PWQUERY_IMPLEMENTATION /* Begin configuration stuff. */ + +/* Include whatever files you need. */ +#include +#include "../jnlib/logging.h" + +/* Try to write error message using the standard log mechanism. The + current implementation requires that the HAVE_JNLIB_LOGGING is also + defined. */ +#define SPWQ_USE_LOGGING 1 + +/* Memory allocation functions used by the implementation. Note, that + the returned value is expected to be freed with + spwq_secure_free. */ +#define spwq_malloc(a) gcry_malloc (a) +#define spwq_free(a) gcry_free (a) +#define spwq_secure_malloc(a) gcry_malloc_secure (a) +#define spwq_secure_free(a) gcry_free (a) + + +#endif /*SIMPLE_PWQUERY_IMPLEMENTATION*/ /* End configuration stuff. */ + + +/* Ask the gpg-agent for a passphrase and present the user with a + DESCRIPTION, a PROMPT and optiaonlly with a TRYAGAIN extra text. + If a CACHEID is not NULL it is used to locate the passphrase in in + the cache and store it under this ID. If ERRORCODE is not NULL it + should point a variable receiving an errorcode; this errocode might + be 0 if the user canceled the operation. The function returns NULL + to indicate an error. */ +char *simple_pwquery (const char *cacheid, + const char *tryagain, + const char *prompt, + const char *description, + int *errorcode); + + +#define SPWQ_OUT_OF_CORE 1 +#define SPWQ_IO_ERROR 2 +#define SPWQ_PROTOCOL_ERROR 3 +#define SPWQ_ERR_RESPONSE 4 +#define SPWQ_NO_AGENT 5 +#define SPWQ_SYS_ERROR 6 +#define SPWQ_GENERAL_ERROR 7 + +#endif /*SIMPLE_PWQUERY_H*/ diff --git a/common/ttyio.c b/common/ttyio.c new file mode 100644 index 000000000..fd748009e --- /dev/null +++ b/common/ttyio.c @@ -0,0 +1,508 @@ +/* ttyio.c - tty i/O functions + * Copyright (C) 1998,1999,2000,2001,2002,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 +#ifdef HAVE_TCGETATTR +#include +#else +#ifdef HAVE_TERMIO_H +/* simulate termios with termio */ +#include +#define termios termio +#define tcsetattr ioctl +#define TCSAFLUSH TCSETAF +#define tcgetattr(A,B) ioctl(A,TCGETA,B) +#define HAVE_TCGETATTR +#endif +#endif +#ifdef __MINGW32__ /* use the odd Win32 functions */ +#include +#ifdef HAVE_TCGETATTR +#error mingw32 and termios +#endif +#endif +#include +#include +#include "util.h" +#include "memory.h" +#include "ttyio.h" + +#define CONTROL_D ('D' - 'A' + 1) + +#ifdef __MINGW32__ /* use the odd Win32 functions */ +static struct { + HANDLE in, out; +} con; +#define DEF_INPMODE (ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT \ + |ENABLE_PROCESSED_INPUT ) +#define HID_INPMODE (ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT ) +#define DEF_OUTMODE (ENABLE_WRAP_AT_EOL_OUTPUT|ENABLE_PROCESSED_OUTPUT) + +#else /* yeah, we have a real OS */ +static FILE *ttyfp = NULL; +#endif + +static int initialized; +static int last_prompt_len; +static int batchmode; +static int no_terminal; + +#ifdef HAVE_TCGETATTR + static struct termios termsave; + static int restore_termios; +#endif + + + +/* This is a wrapper around ttyname so that we can use it even when + the standard streams are redirected. It figures the name out the + first time and returns it in a statically allocated buffer. */ +const char * +tty_get_ttyname (void) +{ + static char *name; + + /* On a GNU system ctermid() always return /dev/tty, so this does + not make much sense - however if it is ever changed we do the + Right Thing now. */ +#ifdef HAVE_CTERMID + static int got_name; + + if (!got_name) + { + const char *s; + s = ctermid (NULL); + if (s) + name = strdup (s); + got_name = 1; + } +#endif + /* Assume the staandrd tty on memory error or when tehre is no + certmid. */ + return name? name : "/dev/tty"; +} + + + +#ifdef HAVE_TCGETATTR +static void +cleanup(void) +{ + if( restore_termios ) { + restore_termios = 0; /* do it prios in case it is interrupted again */ + if( tcsetattr(fileno(ttyfp), TCSAFLUSH, &termsave) ) + log_error("tcsetattr() failed: %s\n", strerror(errno) ); + } +} +#endif + +static void +init_ttyfp(void) +{ + if( initialized ) + return; + +#if defined(__MINGW32__) + { + SECURITY_ATTRIBUTES sa; + + memset(&sa, 0, sizeof(sa)); + sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + con.out = CreateFileA( "CONOUT$", GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, + &sa, OPEN_EXISTING, 0, 0 ); + if( con.out == INVALID_HANDLE_VALUE ) + log_fatal("open(CONOUT$) failed: rc=%d", (int)GetLastError() ); + memset(&sa, 0, sizeof(sa)); + sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + con.in = CreateFileA( "CONIN$", GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, + &sa, OPEN_EXISTING, 0, 0 ); + if( con.in == INVALID_HANDLE_VALUE ) + log_fatal("open(CONIN$) failed: rc=%d", (int)GetLastError() ); + } + SetConsoleMode(con.in, DEF_INPMODE ); + SetConsoleMode(con.out, DEF_OUTMODE ); + +#elif defined(__EMX__) + ttyfp = stdout; /* Fixme: replace by the real functions: see wklib */ +#else + ttyfp = batchmode? stderr : fopen (tty_get_ttyname (), "r+"); + if( !ttyfp ) { + log_error("cannot open `%s': %s\n", tty_get_ttyname (), + strerror(errno) ); + exit(2); + } +#endif +#ifdef HAVE_TCGETATTR + atexit( cleanup ); +#endif + initialized = 1; +} + + +int +tty_batchmode( int onoff ) +{ + int old = batchmode; + if( onoff != -1 ) + batchmode = onoff; + return old; +} + +int +tty_no_terminal(int onoff) +{ + int old = no_terminal; + no_terminal = onoff ? 1 : 0; + return old; +} + +void +tty_printf( const char *fmt, ... ) +{ + va_list arg_ptr; + + if (no_terminal) + return; + + if( !initialized ) + init_ttyfp(); + + va_start( arg_ptr, fmt ) ; +#ifdef __MINGW32__ + { + char *buf = NULL; + int n; + DWORD nwritten; + + n = vasprintf(&buf, fmt, arg_ptr); + if( !buf ) + log_bug("vasprintf() failed\n"); + + if( !WriteConsoleA( con.out, buf, n, &nwritten, NULL ) ) + log_fatal("WriteConsole failed: rc=%d", (int)GetLastError() ); + if( n != nwritten ) + log_fatal("WriteConsole failed: %d != %d\n", n, (int)nwritten ); + last_prompt_len += n; + xfree (buf); + } +#else + last_prompt_len += vfprintf(ttyfp,fmt,arg_ptr) ; + fflush(ttyfp); +#endif + va_end(arg_ptr); +} + + +/**************** + * Print a string, but filter all control characters out. + */ +void +tty_print_string( byte *p, size_t n ) +{ + if (no_terminal) + return; + + if( !initialized ) + init_ttyfp(); + +#ifdef __MINGW32__ + /* not so effective, change it if you want */ + for( ; n; n--, p++ ) + if( iscntrl( *p ) ) { + if( *p == '\n' ) + tty_printf("\\n"); + else if( !*p ) + tty_printf("\\0"); + else + tty_printf("\\x%02x", *p); + } + else + tty_printf("%c", *p); +#else + for( ; n; n--, p++ ) + if( iscntrl( *p ) ) { + putc('\\', ttyfp); + if( *p == '\n' ) + putc('n', ttyfp); + else if( !*p ) + putc('0', ttyfp); + else + fprintf(ttyfp, "x%02x", *p ); + } + else + putc(*p, ttyfp); +#endif +} + +void +tty_print_utf8_string2( byte *p, size_t n, size_t max_n ) +{ + size_t i; + char *buf; + + if (no_terminal) + return; + + /* we can handle plain ascii simpler, so check for it first */ + for(i=0; i < n; i++ ) { + if( p[i] & 0x80 ) + break; + } + if( i < n ) { + buf = utf8_to_native( p, n, 0 ); + if( max_n && (strlen( buf ) > max_n )) { + buf[max_n] = 0; + } + /*(utf8 conversion already does the control character quoting)*/ + tty_printf("%s", buf ); + xfree( buf ); + } + else { + if( max_n && (n > max_n) ) { + n = max_n; + } + tty_print_string( p, n ); + } +} + +void +tty_print_utf8_string( byte *p, size_t n ) +{ + tty_print_utf8_string2( p, n, 0 ); +} + + +static char * +do_get( const char *prompt, int hidden ) +{ + char *buf; +#ifndef __riscos__ + byte cbuf[1]; +#endif + int c, n, i; + + if( batchmode ) { + log_error("Sorry, we are in batchmode - can't get input\n"); + exit(2); + } + + if (no_terminal) { + log_error("Sorry, no terminal at all requested - can't get input\n"); + exit(2); + } + + if( !initialized ) + init_ttyfp(); + + last_prompt_len = 0; + tty_printf( "%s", prompt ); + buf = xmalloc((n=50)); + i = 0; + +#ifdef __MINGW32__ /* windoze version */ + if( hidden ) + SetConsoleMode(con.in, HID_INPMODE ); + + for(;;) { + DWORD nread; + + if( !ReadConsoleA( con.in, cbuf, 1, &nread, NULL ) ) + log_fatal("ReadConsole failed: rc=%d", (int)GetLastError() ); + if( !nread ) + continue; + if( *cbuf == '\n' ) + break; + + if( !hidden ) + last_prompt_len++; + c = *cbuf; + if( c == '\t' ) + c = ' '; + else if( c > 0xa0 ) + ; /* we don't allow 0xa0, as this is a protected blank which may + * confuse the user */ + else if( iscntrl(c) ) + continue; + if( !(i < n-1) ) { + n += 50; + buf = xrealloc (buf, n); + } + buf[i++] = c; + } + + if( hidden ) + SetConsoleMode(con.in, DEF_INPMODE ); + +#elif defined(__riscos__) + do { + c = riscos_getchar(); + if (c == 0xa || c == 0xd) { /* Return || Enter */ + c = (int) '\n'; + } else if (c == 0x8 || c == 0x7f) { /* Backspace || Delete */ + if (i>0) { + i--; + if (!hidden) { + last_prompt_len--; + fputc(8, ttyfp); + fputc(32, ttyfp); + fputc(8, ttyfp); + fflush(ttyfp); + } + } else { + fputc(7, ttyfp); + fflush(ttyfp); + } + continue; + } else if (c == (int) '\t') { /* Tab */ + c = ' '; + } else if (c > 0xa0) { + ; /* we don't allow 0xa0, as this is a protected blank which may + * confuse the user */ + } else if (iscntrl(c)) { + continue; + } + if(!(i < n-1)) { + n += 50; + buf = xrealloc (buf, n); + } + buf[i++] = c; + if (!hidden) { + last_prompt_len++; + fputc(c, ttyfp); + fflush(ttyfp); + } + } while (c != '\n'); + i = (i>0) ? i-1 : 0; +#else /* unix version */ + if( hidden ) { +#ifdef HAVE_TCGETATTR + struct termios term; + + if( tcgetattr(fileno(ttyfp), &termsave) ) + log_fatal("tcgetattr() failed: %s\n", strerror(errno) ); + restore_termios = 1; + term = termsave; + term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); + if( tcsetattr( fileno(ttyfp), TCSAFLUSH, &term ) ) + log_fatal("tcsetattr() failed: %s\n", strerror(errno) ); +#endif + } + + /* fixme: How can we avoid that the \n is echoed w/o disabling + * canonical mode - w/o this kill_prompt can't work */ + while( read(fileno(ttyfp), cbuf, 1) == 1 && *cbuf != '\n' ) { + if( !hidden ) + last_prompt_len++; + c = *cbuf; + if( c == CONTROL_D ) + log_info("control d found\n"); + if( c == '\t' ) + c = ' '; + else if( c > 0xa0 ) + ; /* we don't allow 0xa0, as this is a protected blank which may + * confuse the user */ + else if( iscntrl(c) ) + continue; + if( !(i < n-1) ) { + n += 50; + buf = xrealloc (buf, n ); + } + buf[i++] = c; + } + if( *cbuf != '\n' ) { + buf[0] = CONTROL_D; + i = 1; + } + + + if( hidden ) { +#ifdef HAVE_TCGETATTR + if( tcsetattr(fileno(ttyfp), TCSAFLUSH, &termsave) ) + log_error("tcsetattr() failed: %s\n", strerror(errno) ); + restore_termios = 0; +#endif + } +#endif /* end unix version */ + buf[i] = 0; + return buf; +} + + +char * +tty_get( const char *prompt ) +{ + return do_get( prompt, 0 ); +} + +char * +tty_get_hidden( const char *prompt ) +{ + return do_get( prompt, 1 ); +} + + +void +tty_kill_prompt() +{ + if ( no_terminal ) + return; + + if( !initialized ) + init_ttyfp(); + + if( batchmode ) + last_prompt_len = 0; + if( !last_prompt_len ) + return; +#ifdef __MINGW32__ + tty_printf("\r%*s\r", last_prompt_len, ""); +#else + { + int i; + putc('\r', ttyfp); + for(i=0; i < last_prompt_len; i ++ ) + putc(' ', ttyfp); + putc('\r', ttyfp); + fflush(ttyfp); + } +#endif + last_prompt_len = 0; +} + + +int +tty_get_answer_is_yes( const char *prompt ) +{ + int yes; + char *p = tty_get( prompt ); + tty_kill_prompt(); + yes = answer_is_yes(p); + xfree(p); + return yes; +} diff --git a/common/ttyio.h b/common/ttyio.h new file mode 100644 index 000000000..b3ca7dcaf --- /dev/null +++ b/common/ttyio.h @@ -0,0 +1,40 @@ +/* ttyio.h + * Copyright (C) 1998, 1999, 2000, 2001, 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 GNUPG_COMMON_TTYIO_H +#define GNUPG_COMMON_TTYIO_H + +const char *tty_get_ttyname (void); +int tty_batchmode (int onoff); +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 ) +void tty_printf (const char *fmt, ... ) __attribute__ ((format (printf,1,2))); +#else +void tty_printf (const char *fmt, ... ); +#endif +void tty_print_string (unsigned char *p, size_t n); +void tty_print_utf8_string (unsigned char *p, size_t n); +void tty_print_utf8_string2 (unsigned char *p, size_t n, size_t max_n); +char *tty_get (const char *prompt); +char *tty_get_hidden (const char *prompt); +void tty_kill_prompt (void); +int tty_get_answer_is_yes (const char *prompt); +int tty_no_terminal (int onoff); + + +#endif /*GNUPG_COMMON_TTYIO_H*/ diff --git a/common/util.h b/common/util.h new file mode 100644 index 000000000..045851481 --- /dev/null +++ b/common/util.h @@ -0,0 +1,120 @@ +/* util.h - Utility functions for Gnupg + * Copyright (C) 2001, 2002, 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 GNUPG_COMMON_UTIL_H +#define GNUPG_COMMON_UTIL_H + +#include /* We need this for the memory function protos. */ +#include /* We need time_t. */ +#include /* we need gpg-error_t. */ + +/* to pass hash functions to libksba we need to cast it */ +#define HASH_FNC ((void (*)(void *, const void*,size_t))gcry_md_write) + +/* get all the stuff from jnlib */ +#include "../jnlib/logging.h" +#include "../jnlib/argparse.h" +#include "../jnlib/stringhelp.h" +#include "../jnlib/mischelp.h" +#include "../jnlib/strlist.h" +#include "../jnlib/dotlock.h" +#include "../jnlib/utf8conv.h" + +/* handy malloc macros - use only them */ +#define xtrymalloc(a) gcry_malloc ((a)) +#define xtrycalloc(a,b) gcry_calloc ((a),(b)) +#define xtryrealloc(a,b) gcry_realloc ((a),(b)) +#define xtrystrdup(a) gcry_strdup ((a)) +#define xfree(a) gcry_free ((a)) + +#define xmalloc(a) gcry_xmalloc ((a)) +#define xmalloc_secure(a) gcry_xmalloc_secure ((a)) +#define xcalloc(a,b) gcry_xcalloc ((a),(b)) +#define xcalloc_secure(a,b) gcry_xcalloc_secure ((a),(b)) +#define xrealloc(a,b) gcry_xrealloc ((a),(b)) +#define xstrdup(a) gcry_xstrdup ((a)) + +/*-- maperror.c --*/ +int map_ksba_err (int err); +int map_gcry_err (int err); +int map_kbx_err (int err); +gpg_error_t map_assuan_err (int err); +int map_to_assuan_status (int rc); + +/*-- gettime.c --*/ +time_t gnupg_get_time (void); +void gnupg_set_time (time_t newtime, int freeze); +int gnupg_faked_time_p (void); +u32 make_timestamp (void); +u32 scan_isodatestr (const char *string); +u32 add_days_to_timestamp (u32 stamp, u16 days); +const char *strtimevalue (u32 stamp); +const char *strtimestamp (u32 stamp); /* GMT */ +const char *asctimestamp (u32 stamp); /* localized */ + +/*-- signal.c --*/ +void gnupg_init_signals (int mode, void (*fast_cleanup)(void)); +void gnupg_pause_on_sigusr (int which); +void gnupg_block_all_signals (void); +void gnupg_unblock_all_signals (void); + +/*-- yesno.c --*/ +int answer_is_yes (const char *s); +int answer_is_yes_no_default (const char *s, int def_answer); +int answer_is_yes_no_quit (const char *s); + + +/*-- miscellaneous.c --*/ +const char *print_fname_stdout (const char *s); +const char *print_fname_stdin (const char *s); +void print_string (FILE *fp, const byte *p, size_t n, int delim); +void print_utf8_string2 ( FILE *fp, const byte *p, size_t n, int delim); +void print_utf8_string (FILE *fp, const byte *p, size_t n); +char *make_printable_string (const byte *p, size_t n, int delim); + +int is_file_compressed (const char *s, int *ret_rc); + + +/*-- replacement functions from funcname.c --*/ +#if !HAVE_VASPRINTF +#include +int vasprintf (char **result, const char *format, va_list *args); +int asprintf (char **result, const char *format, ...); +#endif + + + +/*-- some macros to replace ctype ones and avoid locale problems --*/ +#define spacep(p) (*(p) == ' ' || *(p) == '\t') +#define digitp(p) (*(p) >= '0' && *(p) <= '9') +#define hexdigitp(a) (digitp (a) \ + || (*(a) >= 'A' && *(a) <= 'F') \ + || (*(a) >= 'a' && *(a) <= 'f')) +/* the atoi macros assume that the buffer has only valid digits */ +#define atoi_1(p) (*(p) - '0' ) +#define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1)) +#define atoi_4(p) ((atoi_2(p) * 100) + atoi_2((p)+2)) +#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ + *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) +#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) + + + +#endif /*GNUPG_COMMON_UTIL_H*/ diff --git a/common/yesno.c b/common/yesno.c new file mode 100644 index 000000000..2a96b4e5d --- /dev/null +++ b/common/yesno.c @@ -0,0 +1,96 @@ +/* yesno.c - Yes/No questions + * Copyright (C) 1998, 1999, 2000, 2001, 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 "i18n.h" +#include "util.h" + +int +answer_is_yes_no_default( const char *s, int def_answer ) +{ + const char *long_yes = _("yes"); + const char *short_yes = _("yY"); + const char *long_no = _("no"); + const char *short_no = _("nN"); + + /* Note: we have to use the local dependent strcasecmp here */ + if( !strcasecmp(s, long_yes ) ) + return 1; + if( *s && strchr( short_yes, *s ) && !s[1] ) + return 1; + /* test for no strings to catch ambiguities for the next test */ + if( !strcasecmp(s, long_no ) ) + return 0; + if( *s && strchr( short_no, *s ) && !s[1] ) + return 0; + /* test for the english version (for those who are used to type yes) */ + if( !ascii_strcasecmp(s, "yes" ) ) + return 1; + if( *s && strchr( "yY", *s ) && !s[1] ) + return 1; + return def_answer; +} + +int +answer_is_yes( const char *s ) +{ + return answer_is_yes_no_default(s,0); +} + +/**************** + * Return 1 for yes, -1 for quit, or 0 for no + */ +int +answer_is_yes_no_quit( const char *s ) +{ + const char *long_yes = _("yes"); + const char *long_no = _("no"); + const char *long_quit = _("quit"); + const char *short_yes = _("yY"); + const char *short_no = _("nN"); + const char *short_quit = _("qQ"); + + /* Note: We have to use the locale dependent strcasecmp */ + if( !strcasecmp(s, long_no ) ) + return 0; + if( !strcasecmp(s, long_yes ) ) + return 1; + if( !strcasecmp(s, long_quit ) ) + return -1; + if( *s && strchr( short_no, *s ) && !s[1] ) + return 0; + if( *s && strchr( short_yes, *s ) && !s[1] ) + return 1; + if( *s && strchr( short_quit, *s ) && !s[1] ) + return -1; + /* but not here */ + if( !ascii_strcasecmp(s, "yes" ) ) + return 1; + if( !ascii_strcasecmp(s, "quit" ) ) + return -1; + if( *s && strchr( "yY", *s ) && !s[1] ) + return 1; + if( *s && strchr( "qQ", *s ) && !s[1] ) + return -1; + return 0; +} diff --git a/kbx/Makefile.am b/kbx/Makefile.am new file mode 100644 index 000000000..4f0c40043 --- /dev/null +++ b/kbx/Makefile.am @@ -0,0 +1,52 @@ +# Keybox Makefile +# Copyright (C) 2001, 2002, 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 + +## Process this file with automake to produce Makefile.in + +localedir = $(datadir)/locale +INCLUDES = -I../intl -DLOCALEDIR=\"$(localedir)\" + +EXTRA_DIST = mkerrors +AM_CPPFLAGS = -I$(top_srcdir)/common -I$(top_srcdir)/intl \ + $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) + +noinst_LIBRARIES = libkeybox.a +bin_PROGRAMS = kbxutil + +common_sources = \ + keybox.h keybox-defs.h keybox-search-desc.h \ + keybox-util.c \ + keybox-init.c \ + keybox-blob.c \ + keybox-file.c \ + keybox-search.c \ + keybox-update.c \ + keybox-dump.c + + +libkeybox_a_SOURCES = $(common_sources) + +kbxutil_SOURCES = kbxutil.c $(common_sources) +kbxutil_LDADD = ../jnlib/libjnlib.a $(KSBA_LIBS) $(LIBGCRYPT_LIBS) \ + -lgpg-error @INTLLIBS@ + + + + + diff --git a/kbx/kbxutil.c b/kbx/kbxutil.c new file mode 100644 index 000000000..abca4faa9 --- /dev/null +++ b/kbx/kbxutil.c @@ -0,0 +1,339 @@ +/* kbxutil.c - The Keybox utility + * Copyright (C) 2000, 2001 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 + +#include "../jnlib/logging.h" +#include "../jnlib/argparse.h" +#include "../jnlib/stringhelp.h" +#include "../common/i18n.h" +#include "keybox-defs.h" + +#include + + +enum cmd_and_opt_values { + aNull = 0, + oArmor = 'a', + oDryRun = 'n', + oOutput = 'o', + oQuiet = 'q', + oVerbose = 'v', + + aNoSuchCmd = 500, /* force other values not to be a letter */ + aFindByFpr, + aFindByKid, + aFindByUid, + + oDebug, + oDebugAll, + + oNoArmor, + + + aTest +}; + + +static ARGPARSE_OPTS opts[] = { + { 300, NULL, 0, N_("@Commands:\n ") }, + + { aFindByFpr, "find-by-fpr", 0, "|FPR| find key using it's fingerprnt" }, + { aFindByKid, "find-by-kid", 0, "|KID| find key using it's keyid" }, + { aFindByUid, "find-by-uid", 0, "|NAME| find key by user name" }, + + { 301, NULL, 0, N_("@\nOptions:\n ") }, + + { oArmor, "armor", 0, N_("create ascii armored output")}, + { oArmor, "armour", 0, "@" }, + { oOutput, "output", 2, N_("use as output file")}, + { oVerbose, "verbose", 0, N_("verbose") }, + { oQuiet, "quiet", 0, N_("be somewhat more quiet") }, + { oDryRun, "dry-run", 0, N_("do not make any changes") }, + + { oDebug, "debug" ,4|16, N_("set debugging flags")}, + { oDebugAll, "debug-all" ,0, N_("enable full debugging")}, + + {0} /* end of list */ +}; + + +void myexit (int rc); + +int keybox_errors_seen = 0; + + +static const char * +my_strusage( int level ) +{ + const char *p; + switch( level ) { + case 11: p = "kbxutil (GnuPG)"; + break; + case 13: p = VERSION; break; + case 17: p = PRINTABLE_OS_NAME; break; + case 19: p = + _("Please report bugs to " PACKAGE_BUGREPORT ".\n"); + break; + case 1: + case 40: p = + _("Usage: kbxutil [options] [files] (-h for help)"); + break; + case 41: p = + _("Syntax: kbxutil [options] [files]\n" + "list, export, import Keybox data\n"); + break; + + + default: p = NULL; + } + return p; +} + + +static void +i18n_init(void) +{ + #ifdef USE_SIMPLE_GETTEXT + set_gettext_file( PACKAGE ); + #else + #ifdef ENABLE_NLS + #ifdef HAVE_LC_MESSAGES + setlocale( LC_TIME, "" ); + setlocale( LC_MESSAGES, "" ); + #else + setlocale( LC_ALL, "" ); + #endif + bindtextdomain( PACKAGE, LOCALEDIR ); + textdomain( PACKAGE ); + #endif + #endif +} + + +/* static void */ +/* wrong_args( const char *text ) */ +/* { */ +/* log_error("usage: kbxutil %s\n", text); */ +/* myexit ( 1 ); */ +/* } */ + + +#if 0 +static int +hextobyte( const byte *s ) +{ + int c; + + if( *s >= '0' && *s <= '9' ) + c = 16 * (*s - '0'); + else if( *s >= 'A' && *s <= 'F' ) + c = 16 * (10 + *s - 'A'); + else if( *s >= 'a' && *s <= 'f' ) + c = 16 * (10 + *s - 'a'); + else + return -1; + s++; + if( *s >= '0' && *s <= '9' ) + c += *s - '0'; + else if( *s >= 'A' && *s <= 'F' ) + c += 10 + *s - 'A'; + else if( *s >= 'a' && *s <= 'f' ) + c += 10 + *s - 'a'; + else + return -1; + return c; +} +#endif + +#if 0 +static char * +format_fingerprint ( const char *s ) +{ + int i, c; + byte fpr[20]; + + for (i=0; i < 20 && *s; ) { + if ( *s == ' ' || *s == '\t' ) { + s++; + continue; + } + c = hextobyte(s); + if (c == -1) { + return NULL; + } + fpr[i++] = c; + s += 2; + } + return gcry_xstrdup ( fpr ); +} +#endif + +#if 0 +static int +format_keyid ( const char *s, u32 *kid ) +{ + char helpbuf[9]; + switch ( strlen ( s ) ) { + case 8: + kid[0] = 0; + kid[1] = strtoul( s, NULL, 16 ); + return 10; + + case 16: + mem2str( helpbuf, s, 9 ); + kid[0] = strtoul( helpbuf, NULL, 16 ); + kid[1] = strtoul( s+8, NULL, 16 ); + return 11; + } + return 0; /* error */ +} +#endif + + +int +main( int argc, char **argv ) +{ + ARGPARSE_ARGS pargs; + enum cmd_and_opt_values cmd = 0; + + set_strusage( my_strusage ); + /*log_set_name("kbxutil"); fixme */ +#if 0 + /* check that the libraries are suitable. Do it here because + * the option parse may need services of the library */ + if ( !gcry_check_version ( "1.1.4" ) ) + { + log_fatal(_("libgcrypt is too old (need %s, have %s)\n"), + "1.1.4", gcry_check_version(NULL) ); + } +#endif + + /*create_dotlock(NULL); register locking cleanup */ + i18n_init(); + + /* We need to use the gcry malloc function because jnlib does use them */ + keybox_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free); + ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free ); + + + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags= 1; /* do not remove the args */ + while (arg_parse( &pargs, opts) ) + { + switch (pargs.r_opt) + { + case oVerbose: + /*opt.verbose++;*/ + /*gcry_control( GCRYCTL_SET_VERBOSITY, (int)opt.verbose );*/ + break; + case oDebug: + /*opt.debug |= pargs.r.ret_ulong; */ + break; + case oDebugAll: + /*opt.debug = ~0;*/ + break; + + case aFindByFpr: + case aFindByKid: + case aFindByUid: + cmd = pargs.r_opt; + break; + + default: + pargs.err = 2; + break; + } + } + if (log_get_errorcount(0) ) + myexit(2); + + if (!cmd) + { /* default is to list a KBX file */ + if (!argc) + _keybox_dump_file (NULL, stdout); + else + { + for (; argc; argc--, argv++) + _keybox_dump_file (*argv, stdout); + } + } +#if 0 + else if ( cmd == aFindByFpr ) { + char *fpr; + if ( argc != 2 ) + wrong_args ("kbxfile foingerprint"); + fpr = format_fingerprint ( argv[1] ); + if ( !fpr ) + log_error ("invalid formatted fingerprint\n"); + else { + kbxfile_search_by_fpr ( argv[0], fpr ); + gcry_free ( fpr ); + } + } + else if ( cmd == aFindByKid ) { + u32 kid[2]; + int mode; + + if ( argc != 2 ) + wrong_args ("kbxfile short-or-long-keyid"); + mode = format_keyid ( argv[1], kid ); + if ( !mode ) + log_error ("invalid formatted keyID\n"); + else { + kbxfile_search_by_kid ( argv[0], kid, mode ); + } + } + else if ( cmd == aFindByUid ) { + if ( argc != 2 ) + wrong_args ("kbxfile userID"); + kbxfile_search_by_uid ( argv[0], argv[1] ); + } +#endif + else + log_error ("unsupported action\n"); + + myexit(0); + return 8; /*NEVER REACHED*/ +} + + +void +myexit( int rc ) +{ + /* if( opt.debug & DBG_MEMSTAT_VALUE ) {*/ +/* gcry_control( GCRYCTL_DUMP_MEMORY_STATS ); */ +/* gcry_control( GCRYCTL_DUMP_RANDOM_STATS ); */ + /* }*/ +/* if( opt.debug ) */ +/* gcry_control( GCRYCTL_DUMP_SECMEM_STATS ); */ + rc = rc? rc : log_get_errorcount(0)? 2 : + keybox_errors_seen? 1 : 0; + exit(rc ); +} + + diff --git a/kbx/keybox-blob.c b/kbx/keybox-blob.c new file mode 100644 index 000000000..5ad1d2610 --- /dev/null +++ b/kbx/keybox-blob.c @@ -0,0 +1,1008 @@ +/* keybox-blob.c - KBX Blob handling + * Copyright (C) 2000, 2001, 2002, 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 + */ + + +/* The keybox data formats + +The KeyBox uses an augmented OpenPGP/X.509 key format. This makes +random access to a keyblock/Certificate easier and also gives the +opportunity to store additional information (e.g. the fingerprint) +along with the key. All integers are stored in network byte order, +offsets are counted from the beginning of the Blob. + +The first record of a plain KBX file has a special format: + + u32 length of the first record + byte Blob type (1) + byte version number (1) + byte reserved + byte reserved + u32 magic 'KBXf' + byte pgp_marginals used for validity calculation of this file + byte pgp_completes ditto. + byte pgp_cert_depth ditto. + +The OpenPGP and X.509 blob are verry similiar, things which are +X.509 specific are noted like [X.509: xxx] + + u32 length of this blob (including these 4 bytes) + byte Blob type (2) [X509: 3] + byte version number of this blob type (1) + u16 Blob flags + bit 0 = contains secret key material + bit 1 = ephemeral blob (e.g. used while quering external resources) + + u32 offset to the OpenPGP keyblock or X509 DER encoded certificate + u32 and its length + u16 number of keys (at least 1!) [X509: always 1] + u16 size of additional key information + n times: + b20 The keys fingerprint + (fingerprints are always 20 bytes, MD5 left padded with zeroes) + u32 offset to the n-th key's keyID (a keyID is always 8 byte) + or 0 if not known which is the case opnly for X509. + u16 special key flags + bit 0 = + u16 reserved + u16 size of serialnumber(may be zero) + n u16 (see above) bytes of serial number + u16 number of user IDs + u16 size of additional user ID information + n times: + u32 offset to the n-th user ID + u32 length of this user ID. + u16 special user ID flags. + bit 0 = + byte validity + byte reserved + [For X509, the first user ID is the ISsuer, the second the subject + and the others are subjectAltNames] + u16 number of signatures + u16 size of signature information (4) + u32 expiration time of signature with some special values: + 0x00000000 = not checked + 0x00000001 = missing key + 0x00000002 = bad signature + 0x10000000 = valid and expires at some date in 1978. + 0xffffffff = valid and does not expire + u8 assigned ownertrust [X509: no used] + u8 all_validity [X509: no used] + u16 reserved + u32 recheck_after + u32 Newest timestamp in the keyblock (useful for KS syncronsiation?) + u32 Blob created at + u32 size of reserved space (not including this field) + reserved space + + Here we might want to put other data + + Here comes the keyblock + + maybe we put a signature here later. + + b16 MD5 checksum (useful for KS syncronisation), we might also want to use + a mac here. + b4 resevered + +*/ + + +#include +#include +#include +#include +#include +#include + +#include "keybox-defs.h" +#include + +#ifdef KEYBOX_WITH_OPENPGP +/* include stuff to parse the packets */ +#endif +#ifdef KEYBOX_WITH_X509 +#include +#endif + + + +/* special values of the signature status */ +#define SF_NONE(a) ( !(a) ) +#define SF_NOKEY(a) ((a) & (1<<0)) +#define SF_BAD(a) ((a) & (1<<1)) +#define SF_VALID(a) ((a) & (1<<29)) + + +struct membuf { + size_t len; + size_t size; + char *buf; + int out_of_core; +}; + + +/* #if MAX_FINGERPRINT_LEN < 20 */ +/* #error fingerprints are 20 bytes */ +/* #endif */ + +struct keyboxblob_key { + char fpr[20]; + u32 off_kid; + ulong off_kid_addr; + u16 flags; +}; +struct keyboxblob_uid { + ulong off_addr; + char *name; /* used only with x509 */ + u32 len; + u16 flags; + byte validity; +}; + +struct keyid_list { + struct keyid_list *next; + int seqno; + byte kid[8]; +}; + +struct fixup_list { + struct fixup_list *next; + u32 off; + u32 val; +}; + + +struct keyboxblob { + byte *blob; + size_t bloblen; + off_t fileoffset; + + /* stuff used only by keybox_create_blob */ + unsigned char *serialbuf; + const unsigned char *serial; + size_t seriallen; + int nkeys; + struct keyboxblob_key *keys; + int nuids; + struct keyboxblob_uid *uids; + int nsigs; + u32 *sigs; + struct fixup_list *fixups; + int fixup_out_of_core; + + struct keyid_list *temp_kids; + struct membuf bufbuf; /* temporary store for the blob */ + struct membuf *buf; +}; + + + +/* A simple implemnation of a dynamic buffer. Use init_membuf() to + create a buffer, put_membuf to append bytes and get_membuf to + release and return the buffer. Allocation errors are detected but + only returned at the final get_membuf(), this helps not to clutter + the code with out of core checks. */ + +static void +init_membuf (struct membuf *mb, int initiallen) +{ + mb->len = 0; + mb->size = initiallen; + mb->out_of_core = 0; + mb->buf = xtrymalloc (initiallen); + if (!mb->buf) + mb->out_of_core = 1; +} + +static void +put_membuf (struct membuf *mb, const void *buf, size_t len) +{ + if (mb->out_of_core) + return; + + if (mb->len + len >= mb->size) + { + char *p; + + mb->size += len + 1024; + p = xtryrealloc (mb->buf, mb->size); + if (!p) + { + mb->out_of_core = 1; + return; + } + mb->buf = p; + } + memcpy (mb->buf + mb->len, buf, len); + mb->len += len; +} + +static void * +get_membuf (struct membuf *mb, size_t *len) +{ + char *p; + + if (mb->out_of_core) + { + xfree (mb->buf); + mb->buf = NULL; + return NULL; + } + + p = mb->buf; + *len = mb->len; + mb->buf = NULL; + mb->out_of_core = 1; /* don't allow a reuse */ + return p; +} + + +static void +put8 (struct membuf *mb, byte a ) +{ + put_membuf (mb, &a, 1); +} + +static void +put16 (struct membuf *mb, u16 a ) +{ + unsigned char tmp[2]; + tmp[0] = a>>8; + tmp[1] = a; + put_membuf (mb, tmp, 2); +} + +static void +put32 (struct membuf *mb, u32 a ) +{ + unsigned char tmp[4]; + tmp[0] = a>>24; + tmp[1] = a>>16; + tmp[2] = a>>8; + tmp[3] = a; + put_membuf (mb, tmp, 4); +} + + +/* Store a value in the fixup list */ +static void +add_fixup (KEYBOXBLOB blob, u32 off, u32 val) +{ + struct fixup_list *fl; + + if (blob->fixup_out_of_core) + return; + + fl = xtrycalloc(1, sizeof *fl); + if (!fl) + blob->fixup_out_of_core = 1; + else + { + fl->off = off; + fl->val = val; + fl->next = blob->fixups; + blob->fixups = fl; + } +} + + +/* + Some wrappers +*/ + +static u32 +make_timestamp (void) +{ + return time(NULL); +} + + + +#ifdef KEYBOX_WITH_OPENPGP +/* + OpenPGP specific stuff +*/ + + +/* + We must store the keyid at some place because we can't calculate the + offset yet. This is only used for v3 keyIDs. Function returns an + index value for later fixup or -1 for out of core. The value must be + a non-zero value */ +static int +pgp_temp_store_kid (KEYBOXBLOB blob, PKT_public_key *pk) +{ + struct keyid_list *k, *r; + + k = xtrymalloc (sizeof *k); + if (!k) + return -1; + k->kid[0] = pk->keyid[0] >> 24 ; + k->kid[1] = pk->keyid[0] >> 16 ; + k->kid[2] = pk->keyid[0] >> 8 ; + k->kid[3] = pk->keyid[0] ; + k->kid[4] = pk->keyid[0] >> 24 ; + k->kid[5] = pk->keyid[0] >> 16 ; + k->kid[6] = pk->keyid[0] >> 8 ; + k->kid[7] = pk->keyid[0] ; + k->seqno = 0; + k->next = blob->temp_kids; + blob->temp_kids = k; + for (r=k; r; r = r->next) + k->seqno++; + + return k->seqno; +} + +static int +pgp_create_key_part (KEYBOXBLOB blob, KBNODE keyblock) +{ + KBNODE node; + size_t fprlen; + int n; + + for (n=0, node = keyblock; node; node = node->next) + { + if ( node->pkt->pkttype == PKT_PUBLIC_KEY + || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) + { + PKT_public_key *pk = node->pkt->pkt.public_key; + char tmp[20]; + + fingerprint_from_pk (pk, tmp , &fprlen); + memcpy (blob->keys[n].fpr, tmp, 20); + if ( fprlen != 20 ) /*v3 fpr - shift right and fill with zeroes*/ + { + assert (fprlen == 16); + memmove (blob->keys[n].fpr+4, blob->keys[n].fpr, 16); + memset (blob->keys[n].fpr, 0, 4); + blob->keys[n].off_kid = pgp_temp_store_kid (blob, pk); + } + else + { + blob->keys[n].off_kid = 0; /* will be fixed up later */ + } + blob->keys[n].flags = 0; + n++; + } + else if ( node->pkt->pkttype == PKT_SECRET_KEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY ) + { + never_reached (); /* actually not yet implemented */ + } + } + assert (n == blob->nkeys); + return 0; +} + +static int +pgp_create_uid_part (KEYBOXBLOB blob, KBNODE keyblock) +{ + KBNODE node; + int n; + + for (n=0, node = keyblock; node; node = node->next) + { + if (node->pkt->pkttype == PKT_USER_ID) + { + PKT_user_id *u = node->pkt->pkt.user_id; + + blob->uids[n].len = u->len; + blob->uids[n].flags = 0; + blob->uids[n].validity = 0; + n++; + } + } + assert (n == blob->nuids); + return 0; +} + +static int +pgp_create_sig_part (KEYBOXBLOB blob, KBNODE keyblock) +{ + KBNODE node; + int n; + + for (n=0, node = keyblock; node; node = node->next) + { + if (node->pkt->pkttype == PKT_SIGNATURE) + { + PKT_signature *sig = node->pkt->pkt.signature; + + blob->sigs[n] = 0; /* FIXME: check the signature here */ + n++; + } + } + assert( n == blob->nsigs ); + return 0; +} + +static int +pgp_create_blob_keyblock (KEYBOXBLOB blob, KBNODE keyblock) +{ + struct membuf *a = blob->buf; + KBNODE node; + int rc; + int n; + u32 kbstart = a->len; + + add_fixup (blob, kbstart); + + for (n = 0, node = keyblock; node; node = node->next) + { + rc = build_packet ( a, node->pkt ); + if ( rc ) { + gpg_log_error ("build_packet(%d) for keyboxblob failed: %s\n", + node->pkt->pkttype, gpg_errstr(rc) ); + return GPGERR_WRITE_FILE; + } + if ( node->pkt->pkttype == PKT_USER_ID ) + { + PKT_user_id *u = node->pkt->pkt.user_id; + /* build_packet has set the offset of the name into u ; + * now we can do the fixup */ + add_fixup (blob, blob->uids[n].off_addr, u->stored_at); + n++; + } + } + assert (n == blob->nuids); + + add_fixup (blob, a->len - kbstart); + return 0; +} + +#endif /*KEYBOX_WITH_OPENPGP*/ + + +#ifdef KEYBOX_WITH_X509 +/* + X.509 specific stuff + */ + +/* Write the raw certificate out */ +static int +x509_create_blob_cert (KEYBOXBLOB blob, KsbaCert cert) +{ + struct membuf *a = blob->buf; + const unsigned char *image; + size_t length; + u32 kbstart = a->len; + + /* Store our offset for later fixup */ + add_fixup (blob, 8, kbstart); + + image = ksba_cert_get_image (cert, &length); + if (!image) + return gpg_error (GPG_ERR_GENERAL); + put_membuf (a, image, length); + + add_fixup (blob, 12, a->len - kbstart); + return 0; +} + +#endif /*KEYBOX_WITH_X509*/ + +/* Write a stored keyID out to the buffer */ +static void +write_stored_kid (KEYBOXBLOB blob, int seqno) +{ + struct keyid_list *r; + + for ( r = blob->temp_kids; r; r = r->next ) + { + if (r->seqno == seqno ) + { + put_membuf (blob->buf, r->kid, 8); + return; + } + } + never_reached (); +} + +/* Release a list of key IDs */ +static void +release_kid_list (struct keyid_list *kl) +{ + struct keyid_list *r, *r2; + + for ( r = kl; r; r = r2 ) + { + r2 = r->next; + xfree (r); + } +} + + + +static int +create_blob_header (KEYBOXBLOB blob, int blobtype, int as_ephemeral) +{ + struct membuf *a = blob->buf; + int i; + + put32 ( a, 0 ); /* blob length, needs fixup */ + put8 ( a, blobtype); + put8 ( a, 1 ); /* blob type version */ + put16 ( a, as_ephemeral? 2:0 ); /* blob flags */ + + put32 ( a, 0 ); /* offset to the raw data, needs fixup */ + put32 ( a, 0 ); /* length of the raw data, needs fixup */ + + put16 ( a, blob->nkeys ); + put16 ( a, 20 + 4 + 2 + 2 ); /* size of key info */ + for ( i=0; i < blob->nkeys; i++ ) + { + put_membuf (a, blob->keys[i].fpr, 20); + blob->keys[i].off_kid_addr = a->len; + put32 ( a, 0 ); /* offset to keyid, fixed up later */ + put16 ( a, blob->keys[i].flags ); + put16 ( a, 0 ); /* reserved */ + } + + put16 (a, blob->seriallen); /*fixme: check that it fits into 16 bits*/ + if (blob->serial) + put_membuf (a, blob->serial, blob->seriallen); + + put16 ( a, blob->nuids ); + put16 ( a, 4 + 4 + 2 + 1 + 1 ); /* size of uid info */ + for (i=0; i < blob->nuids; i++) + { + blob->uids[i].off_addr = a->len; + put32 ( a, 0 ); /* offset to userid, fixed up later */ + put32 ( a, blob->uids[i].len ); + put16 ( a, blob->uids[i].flags ); + put8 ( a, 0 ); /* validity */ + put8 ( a, 0 ); /* reserved */ + } + + put16 ( a, blob->nsigs ); + put16 ( a, 4 ); /* size of sig info */ + for (i=0; i < blob->nsigs; i++) + { + put32 ( a, blob->sigs[i]); + } + + put8 ( a, 0 ); /* assigned ownertrust */ + put8 ( a, 0 ); /* validity of all user IDs */ + put16 ( a, 0 ); /* reserved */ + put32 ( a, 0 ); /* time of next recheck */ + put32 ( a, 0 ); /* newest timestamp (none) */ + put32 ( a, make_timestamp() ); /* creation time */ + put32 ( a, 0 ); /* size of reserved space */ + /* reserved space (which is currently of size 0) */ + + /* space where we write keyIDs and and other stuff so that the + pointers can actually point to somewhere */ + if (blobtype == BLOBTYPE_PGP) + { + /* We need to store the keyids for all pgp v3 keys because those key + IDs are not part of the fingerprint. While we are doing that, we + fixup all the keyID offsets */ + for (i=0; i < blob->nkeys; i++ ) + { + if (blob->keys[i].off_kid) + { /* this is a v3 one */ + add_fixup (blob, blob->keys[i].off_kid_addr, a->len); + write_stored_kid (blob, blob->keys[i].off_kid); + } + else + { /* the better v4 key IDs - just store an offset 8 bytes back */ + add_fixup (blob, blob->keys[i].off_kid_addr, + blob->keys[i].off_kid_addr - 8); + } + } + } + + if (blobtype == BLOBTYPE_X509) + { + /* We don't want to point to ASN.1 encoded UserIDs (DNs) but to + the utf-8 string represenation of them */ + for (i=0; i < blob->nuids; i++ ) + { + if (blob->uids[i].name) + { /* this is a v3 one */ + add_fixup (blob, blob->uids[i].off_addr, a->len); + put_membuf (blob->buf, blob->uids[i].name, blob->uids[i].len); + } + } + } + + return 0; +} + + + +static int +create_blob_trailer (KEYBOXBLOB blob) +{ + return 0; +} + + +static int +create_blob_finish (KEYBOXBLOB blob) +{ + struct membuf *a = blob->buf; + byte *p; + char *pp; + int i; + size_t n; + + /* write a placeholder for the checksum */ + for (i = 0; i < 16; i++ ) + put32 (a, 0); /* Hmmm: why put32() ?? */ + + /* get the memory area */ + p = get_membuf (a, &n); + if (!p) + return gpg_error (GPG_ERR_ENOMEM); + assert (n >= 20); + + /* fixup the length */ + add_fixup (blob, 0, n); + + /* do the fixups */ + if (blob->fixup_out_of_core) + return gpg_error (GPG_ERR_ENOMEM); + + { + struct fixup_list *fl; + for (fl = blob->fixups; fl; fl = fl->next) + { + assert (fl->off+4 <= n); + p[fl->off+0] = fl->val >> 24; + p[fl->off+1] = fl->val >> 16; + p[fl->off+2] = fl->val >> 8; + p[fl->off+3] = fl->val; + } + } + + /* calculate and store the MD5 checksum */ + gcry_md_hash_buffer (GCRY_MD_MD5, p + n - 16, p, n - 16); + + pp = xtrymalloc (n); + if ( !pp ) + return gpg_error (gpg_err_code_from_errno (errno)); + memcpy (pp , p, n); + blob->blob = pp; + blob->bloblen = n; + + return 0; +} + + +#ifdef KEYBOX_WITH_OPENPGP + +int +_keybox_create_pgp_blob (KEYBOXBLOB *r_blob, KBNODE keyblock, int as_ephemeral) +{ + int rc = 0; + KBNODE node; + KEYBOXBLOB blob; + + *r_blob = NULL; + blob = xtrycalloc (1, sizeof *blob); + if (!blob) + return gpg_error (gpg_err_code_from_errno (errno)); + + /* fixme: Do some sanity checks on the keyblock */ + + /* count userids and keys so that we can allocate the arrays */ + for (node = keyblock; node; node = node->next) + { + switch (node->pkt->pkttype) + { + case PKT_PUBLIC_KEY: + case PKT_SECRET_KEY: + case PKT_PUBLIC_SUBKEY: + case PKT_SECRET_SUBKEY: blob->nkeys++; break; + case PKT_USER_ID: blob->nuids++; break; + case PKT_SIGNATURE: blob->nsigs++; break; + default: break; + } + } + + blob->keys = xtrycalloc (blob->nkeys, sizeof *blob->keys ); + blob->uids = xtrycalloc (blob->nuids, sizeof *blob->uids ); + blob->sigs = xtrycalloc (blob->nsigs, sizeof *blob->sigs ); + if (!blob->keys || !blob->uids || !blob->sigs) + { + rc = gpg_error (GPG_ERR_ENOMEM); + goto leave; + } + + rc = pgp_create_key_part ( blob, keyblock ); + if (rc) + goto leave; + rc = pgp_create_uid_part ( blob, keyblock ); + if (rc) + goto leave; + rc = pgp_create_sig_part ( blob, keyblock ); + if (rc) + goto leave; + + init_membuf (&blob->bufbuf, 1024); + blob->buf = &blob->bufbuf; + rc = create_blob_header (blob, BLOBTYPE_OPENPGP, as_ephemeral); + if (rc) + goto leave; + rc = pgp_create_blob_keyblock (blob, keyblock); + if (rc) + goto leave; + rc = create_blob_trailer (blob); + if (rc) + goto leave; + rc = create_blob_finish ( blob ); + if (rc) + goto leave; + + + leave: + release_kid_list (blob->temp_kids); + blob->temp_kids = NULL; + if (rc) + { + keybox_release_blob (blob); + *r_blob = NULL; + } + else + { + *r_blob = blob; + } + return rc; +} +#endif /*KEYBOX_WITH_OPENPGP*/ + +#ifdef KEYBOX_WITH_X509 + +/* return an allocated string with the email address extracted from a + DN */ +static char * +x509_email_kludge (const char *name) +{ + const unsigned char *p; + unsigned char *buf; + int n; + + if (strncmp (name, "1.2.840.113549.1.9.1=#", 22)) + return NULL; + /* This looks pretty much like an email address in the subject's DN + we use this to add an additional user ID entry. This way, + openSSL generated keys get a nicer and usable listing */ + name += 22; + for (n=0, p=name; hexdigitp (p) && hexdigitp (p+1); p +=2, n++) + ; + if (*p != '#' || !n) + return NULL; + buf = xtrymalloc (n+3); + if (!buf) + return NULL; /* oops, out of core */ + *buf = '<'; + for (n=1, p=name; *p != '#'; p +=2, n++) + buf[n] = xtoi_2 (p); + buf[n++] = '>'; + buf[n] = 0; + return buf; +} + + + +/* Note: We should move calculation of the digest into libksba and + remove that parameter */ +int +_keybox_create_x509_blob (KEYBOXBLOB *r_blob, KsbaCert cert, + unsigned char *sha1_digest, int as_ephemeral) +{ + int i, rc = 0; + KEYBOXBLOB blob; + unsigned char *p; + unsigned char **names = NULL; + size_t max_names; + + *r_blob = NULL; + blob = xtrycalloc (1, sizeof *blob); + if( !blob ) + return gpg_error (gpg_err_code_from_errno (errno)); + + p = ksba_cert_get_serial (cert); + if (p) + { + size_t n, len; + n = gcry_sexp_canon_len (p, 0, NULL, NULL); + if (n < 2) + { + xfree (p); + return gpg_error (GPG_ERR_GENERAL); + } + blob->serialbuf = p; + p++; n--; /* skip '(' */ + for (len=0; n && *p && *p != ':' && digitp (p); n--, p++) + len = len*10 + atoi_1 (p); + if (*p != ':') + { + xfree (blob->serialbuf); + blob->serialbuf = NULL; + return gpg_error (GPG_ERR_GENERAL); + } + p++; + blob->serial = p; + blob->seriallen = len; + } + + blob->nkeys = 1; + + /* create list of names */ + blob->nuids = 0; + max_names = 100; + names = xtrymalloc (max_names * sizeof *names); + if (!names) + { + rc = gpg_error (gpg_err_code_from_errno (errno)); + goto leave; + } + p = ksba_cert_get_issuer (cert, 0); + if (!p) + { + rc = gpg_error (GPG_ERR_MISSING_VALUE); + goto leave; + } + names[blob->nuids++] = p; + for (i=0; (p = ksba_cert_get_subject (cert, i)); i++) + { + + if (blob->nuids >= max_names) + { + unsigned char **tmp; + + max_names += 100; + tmp = xtryrealloc (names, max_names * sizeof *names); + if (!tmp) + { + rc = gpg_error (gpg_err_code_from_errno (errno)); + goto leave; + } + } + names[blob->nuids++] = p; + if (!i && (p=x509_email_kludge (p))) + names[blob->nuids++] = p; /* due to !i we don't need to check bounds*/ + } + + /* space for signature information */ + blob->nsigs = 1; + + blob->keys = xtrycalloc (blob->nkeys, sizeof *blob->keys ); + blob->uids = xtrycalloc (blob->nuids, sizeof *blob->uids ); + blob->sigs = xtrycalloc (blob->nsigs, sizeof *blob->sigs ); + if (!blob->keys || !blob->uids || !blob->sigs) + { + rc = gpg_error (GPG_ERR_ENOMEM); + goto leave; + } + + memcpy (blob->keys[0].fpr, sha1_digest, 20); + blob->keys[0].off_kid = 0; /* We don't have keyids */ + blob->keys[0].flags = 0; + + /* issuer and subject names */ + for (i=0; i < blob->nuids; i++) + { + blob->uids[i].name = names[i]; + blob->uids[i].len = strlen(names[i]); + names[i] = NULL; + blob->uids[i].flags = 0; + blob->uids[i].validity = 0; + } + xfree (names); + names = NULL; + + /* signatures */ + blob->sigs[0] = 0; /* not yet checked */ + + /* Create a temporary buffer for further processing */ + init_membuf (&blob->bufbuf, 1024); + blob->buf = &blob->bufbuf; + /* write out what we already have */ + rc = create_blob_header (blob, BLOBTYPE_X509, as_ephemeral); + if (rc) + goto leave; + rc = x509_create_blob_cert (blob, cert); + if (rc) + goto leave; + rc = create_blob_trailer (blob); + if (rc) + goto leave; + rc = create_blob_finish ( blob ); + if (rc) + goto leave; + + + leave: + release_kid_list (blob->temp_kids); + blob->temp_kids = NULL; + if (blob && names) + { + for (i=0; i < blob->nuids; i++) + xfree (names[i]); + } + xfree (names); + if (rc) + { + _keybox_release_blob (blob); + *r_blob = NULL; + } + else + { + *r_blob = blob; + } + return rc; +} +#endif /*KEYBOX_WITH_X509*/ + + + +int +_keybox_new_blob (KEYBOXBLOB *r_blob, char *image, size_t imagelen, off_t off) +{ + KEYBOXBLOB blob; + + *r_blob = NULL; + blob = xtrycalloc (1, sizeof *blob); + if (!blob) + return gpg_error (gpg_err_code_from_errno (errno)); + + blob->blob = image; + blob->bloblen = imagelen; + blob->fileoffset = off; + *r_blob = blob; + return 0; +} + +void +_keybox_release_blob (KEYBOXBLOB blob) +{ + int i; + if (!blob) + return; + /* hmmm: release membuf here?*/ + xfree (blob->keys ); + xfree (blob->serialbuf); + for (i=0; i < blob->nuids; i++) + xfree (blob->uids[i].name); + xfree (blob->uids ); + xfree (blob->sigs ); + xfree (blob->blob ); + xfree (blob ); +} + + + +const char * +_keybox_get_blob_image ( KEYBOXBLOB blob, size_t *n ) +{ + *n = blob->bloblen; + return blob->blob; +} + +off_t +_keybox_get_blob_fileoffset (KEYBOXBLOB blob) +{ + return blob->fileoffset; +} + diff --git a/scd/ChangeLog b/scd/ChangeLog new file mode 100644 index 000000000..ad4b0518c --- /dev/null +++ b/scd/ChangeLog @@ -0,0 +1,242 @@ +2003-07-31 Werner Koch + + * Makefile.am (scdaemon_LDADD): Added INTLLIBS. + +2003-07-28 Werner Koch + + * app-openpgp.c (do_setattr): Change implementation. Allow all + useful DOs. + +2003-07-27 Werner Koch + + Adjusted for gcry_mpi_print and gcry_mpi_scan API change. + +2003-07-24 Werner Koch + + * app-openpgp.c (do_learn_status): Print more status information. + (app_select_openpgp): Store the card version. + (store_fpr): Add argument card_version and fix DOs for old cards. + (app_openpgp_storekey): Likewise. + +2003-07-23 Werner Koch + + * command.c (cmd_pkauth): New. + (cmd_setdata): Check whether data was given at all to avoid + passing 0 to malloc. + + * app.c (app_auth): New. + * app-openpgp.c (do_auth): New. + +2003-07-22 Werner Koch + + * command.c (cmd_passwd): New. + * app.c (app_change_pin): New. + * app-openpgp.c (do_change_pin): New. + * iso7816.c (iso7816_reset_retry_counter): Implemented. + + * sc-investigate.c (main): New option --gen-random. + * iso7816.c (iso7816_get_challenge): Don't create APDUs with a + length larger than 255. + +2003-07-17 Werner Koch + + * command.c (cmd_random): New command RANDOM. + + * iso7816.c (map_sw): New. Use it in this file to return + meaningful error messages. Changed all public fucntions to return + a gpg_error_t. + (iso7816_change_reference_data): New. + * apdu.c (apdu_open_reader): Use faked status words for soem + system errors. + +2003-07-16 Werner Koch + + * apdu.c (apdu_send_simple): Use apdu_send_le so that we can + specify not to send Le as it should be. + +2003-07-15 Werner Koch + + * Makefile.am: Add sc-copykeys program. + * sc-copykeys.c: New. + * app-openpgp.c (app_openpgp_storekey): New. + (app_openpgp_cardinfo): New. + (count_bits): New. + (store_fpr): And use it here to get the actual length in bit. + +2003-07-03 Werner Koch + + * app-openpgp.c (do_setattr): Add setting of the URL. + (app_select_openpgp): Dump card data only in very verbose mode. + (do_decipher): New. + +2003-07-02 Werner Koch + + * app-openpgp.c (get_sig_counter): New. + (do_sign): Print the signature counter and enable the PIN callback. + (do_genkey): Implement the PIN callback. + +2003-07-01 Werner Koch + + * app-openpgp.c (store_fpr): Fixed fingerprint calculation. + +2003-06-26 Werner Koch + + * app-openpgp.c (find_tlv): Fixed length header parsing. + + * app.c (app_genkey): New. + * command.c (cmd_genkey): New. + +2003-06-25 Werner Koch + + * command.c (percent_plus_unescape): New. + (cmd_setattr): New. + +2003-06-24 Werner Koch + + * command.c (send_status_info): New. + + * app-openpgp.c (app_select_openpgp): Replace SLOT arg by APP arg + and setup the function pointers in APP on success. Changed callers. + * app.c: New. + * app-common.h: New. + * scdaemon.h (APP): New type to handle applications. + (server_control_s): Add an APP context field. + + * command.c (cmd_serialno): Handle applications. + (cmd_pksign): Ditto. + (cmd_pkdecrypt): Ditto. + (reset_notify): Ditto. + (cmd_learn): For now return error for application contexts. + (cmd_readcert): Ditto. + (cmd_readkey): Ditto. + +2003-06-04 Werner Koch + + * card.c (map_sc_err): Renamed gpg_make_err to gpg_err_make. + + Renamed error codes from INVALID to INV and removed _ERROR suffixes. + +2003-06-03 Werner Koch + + Changed all error codes in all files to the new libgpg-error scheme. + + * scdaemon.h: Include gpg-error.h and errno.h + * card.c (map_sc_err): Use unknown for the error source. + * Makefile.am: Link with libgpg-error + +2003-05-14 Werner Koch + + * atr.c, atr.h: New. + * sc-investigate.c: Dump the ATR in a human readable format. + +2003-05-08 Werner Koch + + * scdaemon.h (DBG_CARD_IO_VALUE): New. + + * sc-investigate.c: New. + * scdaemon.c (main): Removed --print-atr option. + + * iso7816.c, iso7816.h, app-openpgp.c: New. + +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. + +2002-08-21 Werner Koch + + * scdaemon.c (main): New option --daemon so that the program is + not accidently started in the background. + +2002-08-16 Werner Koch + + * scdaemon.c: Include i18n.h. + + * card-common.h (struct p15_private_s): Forward declaration. Add + it to card_ctx_s. + * card.c (card_close): Make sure private data is released. + (card_enum_certs): New. + * card-p15.c (p15_release_private_data): New. + (init_private_data): New to work around an OpenSC weirdness. + (p15_enum_keypairs): Do an OpenSC get_objects only once. + (p15_enum_certs): New. + (card_p15_bind): Bind new function. + * command.c (cmd_learn): Return information about the certificates. + +2002-08-09 Werner Koch + + * card.c (card_get_serial_and_stamp): Use the tokeinfo serial + number as a fallback. Add a special prefix for serial numbers. + +2002-07-30 Werner Koch + + Changes to cope with OpenSC 0.7.0: + + * card.c: Removed the check for the packed opensc version. + Changed include file names of opensc. + (map_sc_err): Adjusted error codes for new opensc version. + * card-p15.c: Changed include filename of opensc. + * card-dinsig.c: Ditto. + + * card-p15.c (p15_decipher): Add flags argument to OpenSC call. + +2002-07-24 Werner Koch + + * card.c (find_simple_tlv, find_iccsn): New. + (card_get_serial_and_stamp): Improved serial number parser. + +2002-06-27 Werner Koch + + * scdaemon.c (main): Use GNUPG_DEFAULT_HOMEDIR constant. + +2002-06-15 Werner Koch + + * card-dinsig.c: Documented some stuff from the DIN norm. + +2002-04-15 Werner Koch + + * command.c (cmd_pksign, cmd_pkdecrypt): Use a copy of the key ID. + +2002-04-12 Werner Koch + + * scdaemon.c: New option --debug-sc N. + * card.c (card_open): set it here. + + * card-p15.c (p15_prepare_key): Factored out common code from ... + (p15_sign, p15_decipher): here and made the decryption work the + regular way. + +2002-04-10 Werner Koch + + * card.c (card_open): Return immediately when no reader is available. + +2002-03-27 Werner Koch + + * card.c (card_open, card_close): Adjusted for changes in OpenSC. + +2002-03-10 Werner Koch + + * card-p15.c, card-dinsig.c, card-common.h: New. + * card.c: Factored most code out to the new modules, so that we + can better support different types of card applications. + +2002-01-26 Werner Koch + + * scdaemon.c scdaemon.h, command.c: New. Based on the code from + the gpg-agent. + + Copyright 2002 Free Software Foundation, Inc. + + This file is free software; as a special exception the author gives + unlimited permission to copy and/or distribute it, with or without + modifications, as long as this notice is preserved. + + This file is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY, to the extent permitted by law; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/scd/Makefile.am b/scd/Makefile.am new file mode 100644 index 000000000..0771beb60 --- /dev/null +++ b/scd/Makefile.am @@ -0,0 +1,72 @@ +# Copyright (C) 2002, 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 + +## Process this file with automake to produce Makefile.in + +localedir = $(datadir)/locale +INCLUDES = -I../intl -DLOCALEDIR=\"$(localedir)\" + +bin_PROGRAMS = scdaemon sc-investigate sc-copykeys + +AM_CPPFLAGS = -I$(top_srcdir)/common $(OPENSC_CFLAGS) $(LIBGCRYPT_CFLAGS) \ + $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) + +scdaemon_SOURCES = \ + scdaemon.c scdaemon.h \ + command.c card.c \ + card-common.h \ + card-p15.c card-dinsig.c \ + apdu.c apdu.h \ + iso7816.c iso7816.h \ + app.c app-common.h \ + app-openpgp.c + +scdaemon_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \ + $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) \ + -lgpg-error @INTLLIBS@ -ldl + +sc_investigate_SOURCES = \ + sc-investigate.c scdaemon.h \ + apdu.c apdu.h \ + iso7816.c iso7816.h \ + app.c app-common.h \ + app-openpgp.c \ + atr.c atr.h + +sc_investigate_LDADD = \ + ../jnlib/libjnlib.a ../common/libcommon.a \ + $(LIBGCRYPT_LIBS) @INTLLIBS@ -lgpg-error -ldl + + +sc_copykeys_SOURCES = \ + sc-copykeys.c scdaemon.h \ + apdu.c apdu.h \ + iso7816.c iso7816.h \ + app.c app-common.h \ + app-openpgp.c \ + atr.c atr.h + +sc_copykeys_LDADD = \ + ../jnlib/libjnlib.a ../common/libcommon.a \ + ../common/libsimple-pwquery.a \ + $(LIBGCRYPT_LIBS) -lgpg-error @INTLLIBS@ -ldl + + + + + diff --git a/scd/apdu.c b/scd/apdu.c new file mode 100644 index 000000000..6fec584b9 --- /dev/null +++ b/scd/apdu.c @@ -0,0 +1,558 @@ +/* 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 + +#include "scdaemon.h" +#include "apdu.h" + +#define HAVE_CTAPI 1 + +#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). */ + + + +/* 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; + + if (count) + sleep (1); /* FIXME: we should use a more reliable timer. */ + + /* 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; + } + + } + + 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 have 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; +} + + +/* 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; +} + + +#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); +} + + +unsigned char * +apdu_get_atr (int slot, size_t *atrlen) +{ + char *buf; + + if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) + return NULL; + + buf = xtrymalloc (reader_table[slot].atrlen); + if (!buf) + return NULL; + memcpy (buf, reader_table[slot].atr, reader_table[slot].atrlen); + *atrlen = reader_table[slot].atrlen; + return buf; +} + + +static const char * +error_string (int slot, int rc) +{ +#ifdef HAVE_CTAPI + return ct_error_string (rc); +#elif defined(HAVE_PCSC) + return "?"; +#else + return "?"; +#endif +} + + +/* Dispatcher for the actual send_apdu fucntion. */ +static int +send_apdu (int slot, unsigned char *apdu, size_t apdulen, + unsigned char *buffer, size_t *buflen) +{ +#ifdef HAVE_CTAPI + return ct_send_apdu (slot, apdu, apdulen, buffer, buflen); +#elif defined(HAVE_PCSC) + return SW_HOST_NO_DRIVER; +#else + return SW_HOST_NO_DRIVER; +#endif +} + +/* Send an APDU to the card in SLOT. The APDU is created from all + given parameters: CLASS, INS, P0, P1, LC, DATA, LE. A value of -1 + for LC won't sent this field and the data field; in this case DATA + must also be passed as NULL. The return value is the status word + or -1 for an invalid SLOT or other non card related error. If + RETBUF is not NULL, it will receive an allocated buffer with the + returned data. The length of that data will be put into + *RETBUFLEN. The caller is reponsible for releasing the buffer even + in case of errors. */ +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) +{ + unsigned char result[256+10]; /* 10 extra in case of bugs in the driver. */ + size_t resultlen = 256; + unsigned char apdu[5+256+1]; + size_t apdulen; + int rc, sw; + + if (DBG_CARD_IO) + log_debug ("send apdu: c=%02X i=%02X p0=%02X p1=%02X lc=%d le=%d\n", + class, ins, p0, p1, lc, le); + + if (lc != -1 && (lc > 255 || lc < 0)) + return SW_WRONG_LENGTH; + if (le != -1 && (le > 256 || le < 1)) + return SW_WRONG_LENGTH; + if ((!data && lc != -1) || (data && lc == -1)) + return SW_HOST_INV_VALUE; + + apdulen = 0; + apdu[apdulen++] = class; + apdu[apdulen++] = ins; + apdu[apdulen++] = p0; + apdu[apdulen++] = p1; + if (lc != -1) + { + apdu[apdulen++] = lc; + memcpy (apdu+apdulen, data, lc); + apdulen += lc; + } + if (le != -1) + apdu[apdulen++] = le; /* Truncation is okay becuase 0 means 256. */ + assert (sizeof (apdu) >= apdulen); + /* As safeguard don't pass any garbage from the stack to the driver. */ + memset (apdu+apdulen, 0, sizeof (apdu) - apdulen); + rc = send_apdu (slot, apdu, apdulen, result, &resultlen); + if (rc || resultlen < 2) + { + log_error ("apdu_send_simple(%d) failed: %s\n", + slot, error_string (slot, rc)); + return 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 (sw == SW_SUCCESS) + { + if (retbuf) + { + *retbuf = xtrymalloc (resultlen? resultlen : 1); + if (!*retbuf) + return SW_HOST_OUT_OF_CORE; + *retbuflen = resultlen; + memcpy (*retbuf, result, resultlen); + } + } + else if ((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); + if (!*retbuf) + return SW_HOST_OUT_OF_CORE; + assert (resultlen < bufsize); + memcpy (p, result, resultlen); + p += resultlen; + } + + do + { + int len = (sw & 0x00ff); + + log_debug ("apdu_send_simple(%d): %d more bytes available\n", + slot, len); + apdulen = 0; + apdu[apdulen++] = class; + apdu[apdulen++] = 0xC0; + apdu[apdulen++] = 0; + apdu[apdulen++] = 0; + apdu[apdulen++] = 64; /* that is 256 bytes for Le */ + memset (apdu+apdulen, 0, sizeof (apdu) - apdulen); + rc = send_apdu (slot, apdu, apdulen, result, &resultlen); + if (rc || resultlen < 2) + { + log_error ("apdu_send_simple(%d) for get response failed: %s\n", + slot, error_string (slot, rc)); + return 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) + { + if (retbuf) + { + if (p - *retbuf + resultlen > bufsize) + { + bufsize += resultlen > 4096? resultlen: 4096; + tmp = xtryrealloc (*retbuf, bufsize); + if (!tmp) + return SW_HOST_OUT_OF_CORE; + p = tmp + (p - *retbuf); + *retbuf = tmp; + } + memcpy (p, result, resultlen); + p += resultlen; + } + } + else + log_info ("apdu_send_simple(%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); + if (tmp) + *retbuf = tmp; + } + } + if (DBG_CARD_IO && retbuf && sw == SW_SUCCESS) + log_printhex (" dump: ", *retbuf, *retbuflen); + + return sw; +} + +/* Send an APDU to the card in SLOT. The APDU is created from all + given parameters: CLASS, INS, P0, P1, LC, DATA. A value of -1 for + LC won't sent this field and the data field; in this case DATA must + also be passed as NULL. The return value is the status word or -1 + for an invalid SLOT or other non card related error. If RETBUF is + not NULL, it will receive an allocated buffer with the returned + data. The length of that data will be put into *RETBUFLEN. The + caller is reponsible for releasing the buffer even in case of + errors. */ +int +apdu_send (int slot, int class, int ins, int p0, int p1, + int lc, const char *data, unsigned char **retbuf, size_t *retbuflen) +{ + return apdu_send_le (slot, class, ins, p0, p1, lc, data, 256, + retbuf, retbuflen); +} + +/* Send an APDU to the card in SLOT. The APDU is created from all + given parameters: CLASS, INS, P0, P1, LC, DATA. A value of -1 for + LC won't sent this field and the data field; in this case DATA must + also be passed as NULL. The return value is the status word or -1 + for an invalid SLOT or other non card related error. No data will be + returned. */ +int +apdu_send_simple (int slot, int class, int ins, int p0, int p1, + int lc, const char *data) +{ + return apdu_send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL); +} + + + + diff --git a/scd/apdu.h b/scd/apdu.h new file mode 100644 index 000000000..44166a3fe --- /dev/null +++ b/scd/apdu.h @@ -0,0 +1,73 @@ +/* apdu.h - 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 + +/* ISO 7816 values for the statusword are defined here because they + should not be visible to the users of the actual ISO command + API. */ +enum { + SW_MORE_DATA = 0x6100, /* Note: that the low byte must be + masked of.*/ + SW_EEPROM_FAILURE = 0x6581, + SW_WRONG_LENGTH = 0x6700, + SW_CHV_WRONG = 0x6982, + SW_CHV_BLOCKED = 0x6983, + SW_USE_CONDITIONS = 0x6985, + SW_NOT_SUPPORTED = 0x6a81, + SW_BAD_PARAMETER = 0x6a80, /* (in the data field) */ + SW_REF_NOT_FOUND = 0x6a88, + SW_BAD_P0_P1 = 0x6b00, + SW_INS_NOT_SUP = 0x6d00, + SW_CLA_NOT_SUP = 0x6e00, + SW_SUCCESS = 0x9000, + + /* The follwoing statuswords are no real ones but used to map host + OS errors into status words. A status word is 16 bit so that + those values can't be issued by a card. */ + SW_HOST_OUT_OF_CORE = 0x10001, /* No way yet to differentiate + between errnos on a failed malloc. */ + SW_HOST_INV_VALUE = 0x10002, + SW_HOST_INCOMPLETE_CARD_RESPONSE = 0x10003, +}; + + + +/* Note , that apdu_open_reader returns no status word but -1 on error. */ +int apdu_open_reader (int port); +unsigned char *apdu_get_atr (int slot, size_t *atrlen); + + +/* The apdu send functions do return status words. */ +int apdu_send_simple (int slot, int class, int ins, int p0, int p1, + int lc, const char *data); +int apdu_send (int slot, int class, int ins, int p0, int p1, + int lc, const char *data, + unsigned char **retbuf, size_t *retbuflen); +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); + + +#endif /*APDU_H*/ + + + diff --git a/scd/app-common.h b/scd/app-common.h new file mode 100644 index 000000000..282f82715 --- /dev/null +++ b/scd/app-common.h @@ -0,0 +1,128 @@ +/* app-common.h - Common declarations for all card applications + * 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 GNUPG_SCD_APP_COMMON_H +#define GNUPG_SCD_APP_COMMON_H + +struct app_ctx_s { + int initialized; /* The application has been initialied and the + function pointers may be used. Note that for + unsupported operations the particular + function pointer is set to NULL */ + int slot; /* Used reader. */ + unsigned char *serialno; /* Serialnumber in raw form, allocated. */ + size_t serialnolen; /* Length in octets of serialnumber. */ + unsigned int card_version; + int did_chv1; + int did_chv2; + int did_chv3; + struct { + int (*learn_status) (APP app, CTRL ctrl); + int (*setattr) (APP app, const char *name, + int (*pincb)(void*, const char *, char **), + void *pincb_arg, + const unsigned char *value, size_t valuelen); + int (*sign) (APP app, + const char *keyidstr, int hashalgo, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen ); + int (*auth) (APP app, const char *keyidstr, + int (*pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen); + int (*decipher) (APP app, const char *keyidstr, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen); + int (*genkey) (APP app, CTRL ctrl, + const char *keynostr, unsigned int flags, + int (*pincb)(void*, const char *, char **), + void *pincb_arg); + int (*change_pin) (APP app, CTRL ctrl, + const char *chvnostr, int reset_mode, + int (*pincb)(void*, const char *, char **), + void *pincb_arg); + } fnc; + + +}; + +/*-- app.c --*/ +APP select_application (void); +int app_get_serial_and_stamp (APP app, char **serial, time_t *stamp); +int app_write_learn_status (APP app, CTRL ctrl); +int app_setattr (APP app, const char *name, + int (*pincb)(void*, const char *, char **), + void *pincb_arg, + const unsigned char *value, size_t valuelen); +int app_sign (APP app, const char *keyidstr, int hashalgo, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen ); +int app_auth (APP app, const char *keyidstr, + int (*pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen); +int app_decipher (APP app, const char *keyidstr, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen ); +int app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, + int (*pincb)(void*, const char *, char **), + void *pincb_arg); +int app_get_challenge (APP app, size_t nbytes, unsigned char *buffer); +int app_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode, + int (*pincb)(void*, const char *, char **), + void *pincb_arg); + + +/*-- app-openpgp.c --*/ +int app_select_openpgp (APP app, unsigned char **sn, size_t *snlen); + +int app_openpgp_cardinfo (APP app, + char **serialno, + char **disp_name, + char **pubkey_url, + unsigned char **fpr1, + unsigned char **fpr2, + unsigned char **fpr3); +int app_openpgp_storekey (APP app, int keyno, + unsigned char *template, size_t template_len, + time_t created_at, + const unsigned char *m, size_t mlen, + const unsigned char *e, size_t elen, + int (*pincb)(void*, const char *, char **), + void *pincb_arg); +int app_openpgp_readkey (APP app, int keyno, + unsigned char **m, size_t *mlen, + unsigned char **e, size_t *elen); + + +#endif /*GNUPG_SCD_APP_COMMON_H*/ + + + diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c new file mode 100644 index 000000000..09a19699d --- /dev/null +++ b/scd/app-openpgp.c @@ -0,0 +1,1482 @@ +/* app-openpgp.c - The OpenPGP card application. + * 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 "app-common.h" +#include "iso7816.h" + + + +static struct { + int tag; + int constructed; + int get_from; /* Constructed DO with this DO or 0 for direct access. */ + int binary; + char *desc; +} data_objects[] = { + { 0x005E, 0, 0, 1, "Login Data" }, + { 0x5F50, 0, 0, 0, "URL" }, + { 0x0065, 1, 0, 1, "Cardholder Related Data"}, + { 0x005B, 0, 0x65, 0, "Name" }, + { 0x5F2D, 0, 0x65, 0, "Language preferences" }, + { 0x5F35, 0, 0x65, 0, "Sex" }, + { 0x006E, 1, 0, 1, "Application Related Data" }, + { 0x004F, 0, 0x6E, 1, "AID" }, + { 0x0073, 1, 0, 1, "Discretionary Data Objects" }, + { 0x0047, 0, 0x6E, 1, "Card Capabilities" }, + { 0x00C0, 0, 0x6E, 1, "Extended Card Capabilities" }, + { 0x00C1, 0, 0x6E, 1, "Algorithm Attributes Signature" }, + { 0x00C2, 0, 0x6E, 1, "Algorithm Attributes Decryption" }, + { 0x00C3, 0, 0x6E, 1, "Algorithm Attributes Authentication" }, + { 0x00C4, 0, 0x6E, 1, "CHV Status Bytes" }, + { 0x00C5, 0, 0x6E, 1, "Fingerprints" }, + { 0x00C6, 0, 0x6E, 1, "CA Fingerprints" }, + { 0x007A, 1, 0, 1, "Security Support Template" }, + { 0x0093, 0, 0x7A, 1, "Digital Signature Counter" }, + { 0 } +}; + + +static unsigned long get_sig_counter (APP app); + + +/* Locate a TLV encoded data object in BUFFER of LENGTH and + return a pointer to value as well as its length in NBYTES. Return + NULL if it was not found. Note, that the function does not check + whether the value fits into the provided buffer. + + FIXME: Move this to an extra file, it is mostly duplicated from card.c. +*/ +static const unsigned char * +find_tlv (const unsigned char *buffer, size_t length, + int tag, size_t *nbytes, int nestlevel) +{ + const unsigned char *s = buffer; + size_t n = length; + size_t len; + int this_tag; + int composite; + + for (;;) + { + buffer = s; + if (n < 2) + return NULL; /* buffer definitely too short for tag and length. */ + if (!*s || *s == 0xff) + { /* Skip optional filler between TLV objects. */ + s++; + n--; + continue; + } + composite = !!(*s & 0x20); + if ((*s & 0x1f) == 0x1f) + { /* more tag bytes to follow */ + s++; + n--; + if (n < 2) + return NULL; /* buffer definitely too short for tag and length. */ + if ((*s & 0x1f) == 0x1f) + return NULL; /* We support only up to 2 bytes. */ + this_tag = (s[-1] << 8) | (s[0] & 0x7f); + } + else + this_tag = s[0]; + len = s[1]; + s += 2; n -= 2; + if (len < 0x80) + ; + else if (len == 0x81) + { /* One byte length follows. */ + if (!n) + return NULL; /* we expected 1 more bytes with the length. */ + len = s[0]; + s++; n--; + } + else if (len == 0x82) + { /* Two byte length follows. */ + if (n < 2) + return NULL; /* we expected 2 more bytes with the length. */ + len = (s[0] << 8) | s[1]; + s += 2; n -= 2; + } + else + return NULL; /* APDU limit is 65535, thus it does not make + sense to assume longer length fields. */ + + if (composite && nestlevel < 100) + { /* Dive into this composite DO after checking for too deep + nesting. */ + const unsigned char *tmp_s; + size_t tmp_len; + + tmp_s = find_tlv (s, len, tag, &tmp_len, nestlevel+1); + if (tmp_s) + { + *nbytes = tmp_len; + return tmp_s; + } + } + + if (this_tag == tag) + { + *nbytes = len; + return s; + } + if (len > n) + return NULL; /* buffer too short to skip to the next tag. */ + s += len; n -= len; + } +} + + +/* Get the DO identified by TAG from the card in SLOT and return a + buffer with its content in RESULT and NBYTES. The return value is + NULL if not found or a pointer which must be used to release the + buffer holding value. */ +static void * +get_one_do (int slot, int tag, unsigned char **result, size_t *nbytes) +{ + int rc, i; + unsigned char *buffer; + size_t buflen; + unsigned char *value; + size_t valuelen; + + *result = NULL; + *nbytes = 0; + for (i=0; data_objects[i].tag && data_objects[i].tag != tag; i++) + ; + + value = NULL; + rc = -1; + if (data_objects[i].tag && data_objects[i].get_from) + { + rc = iso7816_get_data (slot, data_objects[i].get_from, + &buffer, &buflen); + if (!rc) + { + const unsigned char *s; + + s = find_tlv (buffer, buflen, tag, &valuelen, 0); + if (!s) + value = NULL; /* not found */ + else if (valuelen > buflen - (s - buffer)) + { + log_error ("warning: constructed DO too short\n"); + value = NULL; + xfree (buffer); buffer = NULL; + } + else + value = buffer + (s - buffer); + } + } + + if (!value) /* Not in a constructed DO, try simple. */ + { + rc = iso7816_get_data (slot, tag, &buffer, &buflen); + if (!rc) + { + value = buffer; + valuelen = buflen; + } + } + + if (!rc) + { + *nbytes = valuelen; + *result = value; + return buffer; + } + return NULL; +} + +#if 0 /* not used */ +static void +dump_one_do (int slot, int tag) +{ + int rc, i; + unsigned char *buffer; + size_t buflen; + const char *desc; + int binary; + const unsigned char *value; + size_t valuelen; + + for (i=0; data_objects[i].tag && data_objects[i].tag != tag; i++) + ; + desc = data_objects[i].tag? data_objects[i].desc : "?"; + binary = data_objects[i].tag? data_objects[i].binary : 1; + + value = NULL; + rc = -1; + if (data_objects[i].tag && data_objects[i].get_from) + { + rc = iso7816_get_data (slot, data_objects[i].get_from, + &buffer, &buflen); + if (!rc) + { + value = find_tlv (buffer, buflen, tag, &valuelen, 0); + if (!value) + ; /* not found */ + else if (valuelen > buflen - (value - buffer)) + { + log_error ("warning: constructed DO too short\n"); + value = NULL; + xfree (buffer); buffer = NULL; + } + } + } + + if (!value) /* Not in a constructed DO, try simple. */ + { + rc = iso7816_get_data (slot, tag, &buffer, &buflen); + if (!rc) + { + value = buffer; + valuelen = buflen; + } + } + if (rc == 0x6a88) + log_info ("DO `%s' not available\n", desc); + else if (rc) + log_info ("DO `%s' not available (rc=%04X)\n", desc, rc); + else + { + if (binary) + { + log_info ("DO `%s': ", desc); + log_printhex ("", value, valuelen); + } + else + log_info ("DO `%s': `%.*s'\n", + desc, (int)valuelen, value); /* FIXME: sanitize */ + xfree (buffer); + } +} +#endif /*not used*/ + + +static void +dump_all_do (int slot) +{ + int rc, i, j; + unsigned char *buffer; + size_t buflen; + + for (i=0; data_objects[i].tag; i++) + { + if (data_objects[i].get_from) + continue; + + rc = iso7816_get_data (slot, data_objects[i].tag, &buffer, &buflen); + if (rc == 0x6a88) + ; + else if (rc) + log_info ("DO `%s' not available (rc=%04X)\n", + data_objects[i].desc, rc); + else + { + if (data_objects[i].binary) + { + log_info ("DO `%s': ", data_objects[i].desc); + log_printhex ("", buffer, buflen); + } + else + log_info ("DO `%s': `%.*s'\n", + data_objects[i].desc, + (int)buflen, buffer); /* FIXME: sanitize */ + } + + if (data_objects[i].constructed) + { + for (j=0; data_objects[j].tag; j++) + { + const unsigned char *value; + size_t valuelen; + + if (j==i || data_objects[i].tag != data_objects[j].get_from) + continue; + value = find_tlv (buffer, buflen, + data_objects[j].tag, &valuelen, 0); + if (!value) + ; /* not found */ + else if (valuelen > buflen - (value - buffer)) + log_error ("warning: constructed DO too short\n"); + else + { + if (data_objects[j].binary) + { + log_info ("DO `%s': ", data_objects[j].desc); + log_printhex ("", value, valuelen); + } + else + log_info ("DO `%s': `%.*s'\n", + data_objects[j].desc, + (int)valuelen, value); /* FIXME: sanitize */ + } + } + } + xfree (buffer); buffer = NULL; + } +} + + +/* Count the number of bits, assuming the A represents an unsigned big + integer of length LEN bytes. */ +static unsigned int +count_bits (const unsigned char *a, size_t len) +{ + unsigned int n = len * 8; + int i; + + for (; len && !*a; len--, a++, n -=8) + ; + if (len) + { + for (i=7; i && !(*a & (1<> 8; /* 2 byte length header */ + *p++ = n; + *p++ = 4; /* key packet version */ + *p++ = timestamp >> 24; + *p++ = timestamp >> 16; + *p++ = timestamp >> 8; + *p++ = timestamp; + *p++ = 1; /* RSA */ + nbits = count_bits (m, mlen); + *p++ = nbits >> 8; + *p++ = nbits; + memcpy (p, m, mlen); p += mlen; + nbits = count_bits (e, elen); + *p++ = nbits >> 8; + *p++ = nbits; + memcpy (p, e, elen); p += elen; + + log_printhex ("fprbuf:", buffer, n+3); + gcry_md_hash_buffer (GCRY_MD_SHA1, fpr, buffer, n+3); + + xfree (buffer); + + rc = iso7816_put_data (slot, (card_version > 0x0007? 0xC7 : 0xC6) + + keynumber, fpr, 20); + if (rc) + log_error ("failed to store the fingerprint: rc=%04X\n", rc); + + return rc; +} + + +static void +send_fpr_if_not_null (CTRL ctrl, const char *keyword, + int number, const unsigned char *fpr) +{ + int i; + char buf[41]; + char numbuf[25]; + + for (i=0; i < 20 && !fpr[i]; i++) + ; + if (i==20) + return; /* All zero. */ + for (i=0; i< 20; i++) + sprintf (buf+2*i, "%02X", fpr[i]); + if (number == -1) + *numbuf = 0; /* Don't print the key number */ + else + sprintf (numbuf, "%d", number); + send_status_info (ctrl, keyword, + numbuf, (size_t)strlen(numbuf), + buf, (size_t)strlen (buf), NULL, 0); +} + +static void +send_key_data (CTRL ctrl, const char *name, + const unsigned char *a, size_t alen) +{ + char *p, *buf = xmalloc (alen*2+1); + + for (p=buf; alen; a++, alen--, p += 2) + sprintf (p, "%02X", *a); + + send_status_info (ctrl, "KEY-DATA", + name, (size_t)strlen(name), + buf, (size_t)strlen (buf), + NULL, 0); + xfree (buf); +} + + + +static int +do_learn_status (APP app, CTRL ctrl) +{ + void *relptr; + unsigned char *value; + size_t valuelen; + int i; + + relptr = get_one_do (app->slot, 0x005B, &value, &valuelen); + if (relptr) + { + send_status_info (ctrl, "DISP-NAME", value, valuelen, NULL, 0); + xfree (relptr); + } + relptr = get_one_do (app->slot, 0x5F2D, &value, &valuelen); + if (relptr) + { + send_status_info (ctrl, "DISP-LANG", value, valuelen, NULL, 0); + xfree (relptr); + } + relptr = get_one_do (app->slot, 0x5F35, &value, &valuelen); + if (relptr) + { + send_status_info (ctrl, "DISP-SEX", value, valuelen, NULL, 0); + xfree (relptr); + } + relptr = get_one_do (app->slot, 0x5F50, &value, &valuelen); + if (relptr) + { + send_status_info (ctrl, "PUBKEY-URL", value, valuelen, NULL, 0); + xfree (relptr); + } + relptr = get_one_do (app->slot, 0x005E, &value, &valuelen); + if (relptr) + { + send_status_info (ctrl, "LOGIN-DATA", value, valuelen, NULL, 0); + xfree (relptr); + } + + relptr = get_one_do (app->slot, 0x00C5, &value, &valuelen); + if (relptr && valuelen >= 60) + { + for (i=0; i < 3; i++) + send_fpr_if_not_null (ctrl, "KEY-FPR", i+1, value+i*20); + } + xfree (relptr); + relptr = get_one_do (app->slot, 0x00C6, &value, &valuelen); + if (relptr && valuelen >= 60) + { + for (i=0; i < 3; i++) + send_fpr_if_not_null (ctrl, "CA-FPR", i+1, value+i*20); + } + xfree (relptr); + relptr = get_one_do (app->slot, 0x00C4, &value, &valuelen); + if (relptr) + { + char numbuf[7*23]; + + for (i=0,*numbuf=0; i < valuelen && i < 7; i++) + sprintf (numbuf+strlen (numbuf), " %d", value[i]); + send_status_info (ctrl, "CHV-STATUS", numbuf, strlen (numbuf), NULL, 0); + xfree (relptr); + } + + { + unsigned long ul = get_sig_counter (app); + char numbuf[23]; + + sprintf (numbuf, "%lu", ul); + send_status_info (ctrl, "SIG-COUNTER", numbuf, strlen (numbuf), NULL, 0); + } + return 0; +} + + +/* Handle the SETATTR operation. All arguments are already basically + checked. */ +static int +do_setattr (APP app, const char *name, + int (*pincb)(void*, const char *, char **), + void *pincb_arg, + const unsigned char *value, size_t valuelen) +{ + gpg_error_t rc; + int idx; + static struct { + const char *name; + int tag; + } table[] = { + { "DISP-NAME", 0x005B }, + { "LOGIN-DATA", 0x005E }, + { "DISP-LANG", 0x5F2D }, + { "DISP-SEX", 0x5F35 }, + { "PUBKEY-URL", 0x5F50 }, + { "CHV-STATUS-1", 0x00C4 }, + { "CA-FPR-1", 0x00CA }, + { "CA-FPR-2", 0x00CB }, + { "CA-FPR-3", 0x00CC }, + { NULL, 0 } + }; + + + for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++) + ; + if (!table[idx].name) + return gpg_error (GPG_ERR_INV_NAME); + + if (!app->did_chv3) + { + char *pinvalue; + + rc = pincb (pincb_arg, "Admin PIN (CHV3)", + &pinvalue); +/* pinvalue = xstrdup ("12345678"); */ +/* rc = 0; */ + if (rc) + { + log_info ("PIN callback returned error: %s\n", gpg_strerror (rc)); + return rc; + } + + rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue)); + xfree (pinvalue); + if (rc) + { + log_error ("verify CHV3 failed\n"); + rc = gpg_error (GPG_ERR_GENERAL); + return rc; + } + app->did_chv3 = 1; + } + + rc = iso7816_put_data (app->slot, table[idx].tag, value, valuelen); + if (rc) + log_error ("failed to set `%s': %s\n", table[idx].name, gpg_strerror (rc)); + /* FIXME: If this fails we should *once* try again after + doing a verify command, so that in case of a problem with + tracking the verify operation we have a fallback. */ + + return rc; +} + +/* Handle the PASSWD command. */ +static int +do_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode, + int (*pincb)(void*, const char *, char **), + void *pincb_arg) +{ + int rc = 0; + int chvno = atoi (chvnostr); + char *pinvalue; + + if (reset_mode && chvno == 3) + { + rc = gpg_error (GPG_ERR_INV_ID); + goto leave; + } + else if (reset_mode || chvno == 3) + { + rc = pincb (pincb_arg, "Admin PIN", &pinvalue); + if (rc) + { + log_error ("error getting PIN: %s\n", gpg_strerror (rc)); + goto leave; + } + rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue)); + xfree (pinvalue); + if (rc) + { + log_error ("verify CHV3 failed: rc=%04X\n", rc); + goto leave; + } + } + else if (chvno == 1) + { + rc = pincb (pincb_arg, "Signature PIN", &pinvalue); + if (rc) + { + log_error ("error getting PIN: %s\n", gpg_strerror (rc)); + goto leave; + } + rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue)); + xfree (pinvalue); + if (rc) + { + log_error ("verify CHV1 failed: rc=%04X\n", rc); + goto leave; + } + } + else if (chvno == 2) + { + rc = pincb (pincb_arg, "Decryption PIN", &pinvalue); + if (rc) + { + log_error ("error getting PIN: %s\n", gpg_strerror (rc)); + goto leave; + } + rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue)); + xfree (pinvalue); + if (rc) + { + log_error ("verify CHV2 failed: rc=%04X\n", rc); + goto leave; + } + } + else + { + rc = gpg_error (GPG_ERR_INV_ID); + goto leave; + } + + + rc = pincb (pincb_arg, chvno == 1? "New Signature PIN" : + chvno == 2? "New Decryption PIN" : + chvno == 3? "New Admin PIN" : "?", &pinvalue); + if (rc) + { + log_error ("error getting new PIN: %s\n", gpg_strerror (rc)); + goto leave; + } + + if (reset_mode) + rc = iso7816_reset_retry_counter (app->slot, 0x80 + chvno, + pinvalue, strlen (pinvalue)); + else + rc = iso7816_change_reference_data (app->slot, 0x80 + chvno, + NULL, 0, + pinvalue, strlen (pinvalue)); + xfree (pinvalue); + + + leave: + return rc; +} + + + +/* Handle the GENKEY command. */ +static int +do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, + int (*pincb)(void*, const char *, char **), + void *pincb_arg) +{ + int rc; + int i; + char numbuf[30]; + unsigned char fprbuf[20]; + const unsigned char *fpr; + const unsigned char *keydata, *m, *e; + unsigned char *buffer; + size_t buflen, keydatalen, n, mlen, elen; + time_t created_at; + int keyno = atoi (keynostr); + int force = (flags & 1); + time_t start_at; + + if (keyno < 1 || keyno > 3) + return gpg_error (GPG_ERR_INV_ID); + keyno--; + + rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen); + if (rc) + { + log_error ("error reading application data\n"); + return gpg_error (GPG_ERR_GENERAL); + } + fpr = find_tlv (buffer, buflen, 0x00C5, &n, 0); + if (!fpr || n != 60) + { + rc = gpg_error (GPG_ERR_GENERAL); + log_error ("error reading fingerprint DO\n"); + goto leave; + } + fpr += 20*keyno; + for (i=0; i < 20 && !fpr[i]; i++) + ; + if (i!=20 && !force) + { + rc = gpg_error (GPG_ERR_EEXIST); + log_error ("key already exists\n"); + goto leave; + } + else if (i!=20) + log_info ("existing key will be replaced\n"); + else + log_info ("generating new key\n"); + + { + char *pinvalue; + rc = pincb (pincb_arg, "Admin PIN", &pinvalue); + if (rc) + { + log_error ("error getting PIN: %s\n", gpg_strerror (rc)); + return rc; + } + rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue)); + xfree (pinvalue); + } + if (rc) + { + log_error ("verify CHV3 failed: rc=%04X\n", rc); + goto leave; + } + + xfree (buffer); buffer = NULL; +#if 1 + log_info ("please wait while key is being generated ...\n"); + start_at = time (NULL); + rc = iso7816_generate_keypair +#else +#warning key generation temporary replaced by reading an existing key. + rc = iso7816_read_public_key +#endif + (app->slot, + keyno == 0? "\xB6" : + keyno == 1? "\xB8" : "\xA4", + 2, + &buffer, &buflen); + if (rc) + { + rc = gpg_error (GPG_ERR_CARD); + log_error ("generating key failed\n"); + goto leave; + } + log_info ("key generation completed (%d seconds)\n", + (int)(time (NULL) - start_at)); + keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen, 0); + if (!keydata) + { + rc = gpg_error (GPG_ERR_CARD); + log_error ("response does not contain the public key data\n"); + goto leave; + } + + m = find_tlv (keydata, keydatalen, 0x0081, &mlen, 0); + if (!m) + { + rc = gpg_error (GPG_ERR_CARD); + log_error ("response does not contain the RSA modulus\n"); + goto leave; + } +/* log_printhex ("RSA n:", m, mlen); */ + send_key_data (ctrl, "n", m, mlen); + + e = find_tlv (keydata, keydatalen, 0x0082, &elen, 0); + if (!e) + { + rc = gpg_error (GPG_ERR_CARD); + log_error ("response does not contain the RSA public exponent\n"); + goto leave; + } +/* log_printhex ("RSA e:", e, elen); */ + send_key_data (ctrl, "e", e, elen); + + created_at = gnupg_get_time (); + sprintf (numbuf, "%lu", (unsigned long)created_at); + send_status_info (ctrl, "KEY-CREATED-AT", + numbuf, (size_t)strlen(numbuf), NULL, 0); + + rc = store_fpr (app->slot, keyno, (u32)created_at, + m, mlen, e, elen, fprbuf, app->card_version); + if (rc) + goto leave; + send_fpr_if_not_null (ctrl, "KEY-FPR", -1, fprbuf); + + + leave: + xfree (buffer); + return rc; +} + + +static unsigned long +get_sig_counter (APP app) +{ + void *relptr; + unsigned char *value; + size_t valuelen; + unsigned long ul; + + relptr = get_one_do (app->slot, 0x0093, &value, &valuelen); + if (!relptr) + return 0; + if (valuelen == 3 ) + ul = (value[0] << 16) | (value[1] << 8) | value[2]; + else + { + log_error ("invalid structure of OpenPGP card (DO 0x93)\n"); + ul = 0; + } + xfree (relptr); + return ul; +} + +static int +compare_fingerprint (APP app, int keyno, unsigned char *sha1fpr) +{ + const unsigned char *fpr; + unsigned char *buffer; + size_t buflen, n; + int rc, i; + + assert (keyno >= 1 && keyno <= 3); + + rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen); + if (rc) + { + log_error ("error reading application data\n"); + return gpg_error (GPG_ERR_GENERAL); + } + fpr = find_tlv (buffer, buflen, 0x00C5, &n, 0); + if (!fpr || n != 60) + { + xfree (buffer); + log_error ("error reading fingerprint DO\n"); + return gpg_error (GPG_ERR_GENERAL); + } + fpr += (keyno-1)*20; + for (i=0; i < 20; i++) + if (sha1fpr[i] != fpr[i]) + { + xfree (buffer); + return gpg_error (GPG_ERR_WRONG_SECKEY); + } + xfree (buffer); + return 0; +} + + + +/* Compute a digital signature on INDATA which is expected to be the + raw message digest. For this application the KEYIDSTR consists of + the serialnumber and the fingerprint delimited by a slash. + + Note that this fucntion may return the error code + GPG_ERR_WRONG_CARD to indicate that the card currently present does + not match the one required for the requested action (e.g. the + serial number does not match). */ +static int +do_sign (APP app, const char *keyidstr, int hashalgo, + int (*pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen ) +{ + static unsigned char sha1_prefix[15] = /* Object ID is 1.3.14.3.2.26 */ + { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, + 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 }; + static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */ + { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03, + 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 }; + int rc; + unsigned char data[35]; + unsigned char tmp_sn[20]; /* actually 16 but we use it also for the fpr. */ + const char *s; + int n; + const char *fpr = NULL; + unsigned long sigcount; + + if (!keyidstr || !*keyidstr) + return gpg_error (GPG_ERR_INV_VALUE); + if (indatalen != 20) + return gpg_error (GPG_ERR_INV_VALUE); + + /* Check whether an OpenPGP card of any version has been requested. */ + if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12)) + return gpg_error (GPG_ERR_INV_ID); + + for (s=keyidstr, n=0; hexdigitp (s); s++, n++) + ; + if (n != 32) + return gpg_error (GPG_ERR_INV_ID); + else if (!*s) + ; /* no fingerprint given: we allow this for now. */ + else if (*s == '/') + fpr = s + 1; + else + return gpg_error (GPG_ERR_INV_ID); + + for (s=keyidstr, n=0; n < 16; s += 2, n++) + tmp_sn[n] = xtoi_2 (s); + + if (app->serialnolen != 16) + return gpg_error (GPG_ERR_INV_CARD); + if (memcmp (app->serialno, tmp_sn, 16)) + return gpg_error (GPG_ERR_WRONG_CARD); + + /* If a fingerprint has been specified check it against the one on + the card. This is allows for a meaningful error message in case + the key on the card has been replaced but the shadow information + known to gpg was not updated. If there is no fingerprint, gpg + will detect a bogus signature anyway due to the + verify-after-signing feature. */ + if (fpr) + { + for (s=fpr, n=0; hexdigitp (s); s++, n++) + ; + if (n != 40) + return gpg_error (GPG_ERR_INV_ID); + else if (!*s) + ; /* okay */ + else + return gpg_error (GPG_ERR_INV_ID); + + for (s=fpr, n=0; n < 20; s += 2, n++) + tmp_sn[n] = xtoi_2 (s); + rc = compare_fingerprint (app, 1, tmp_sn); + if (rc) + return rc; + } + + if (hashalgo == GCRY_MD_SHA1) + memcpy (data, sha1_prefix, 15); + else if (hashalgo == GCRY_MD_RMD160) + memcpy (data, rmd160_prefix, 15); + else + return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + memcpy (data+15, indata, indatalen); + + sigcount = get_sig_counter (app); + log_info ("signatures created so far: %lu\n", sigcount); + + /* FIXME: Check whether we are really required to enter the PIN for + each signature. There is a DO for this. */ + if (!app->did_chv1 || 1) + { + char *pinvalue; + + { + char *prompt; + if (asprintf (&prompt, "Signature PIN [sigs done: %lu]", sigcount) < 0) + return gpg_error_from_errno (errno); + rc = pincb (pincb_arg, prompt, &pinvalue); + free (prompt); + } +/* pinvalue = xstrdup ("123456"); */ +/* rc = 0; */ + if (rc) + { + log_info ("PIN callback returned error: %s\n", gpg_strerror (rc)); + return rc; + } + + rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue)); + xfree (pinvalue); + if (rc) + { + log_error ("verify CHV1 failed\n"); + rc = gpg_error (GPG_ERR_GENERAL); + return rc; + } + app->did_chv1 = 1; + } + + rc = iso7816_compute_ds (app->slot, data, 35, outdata, outdatalen); + return rc; +} + +/* Compute a digital signature using the INTERNAL AUTHENTICATE command + on INDATA which is expected to be the raw message digest. For this + application the KEYIDSTR consists of the serialnumber and the + fingerprint delimited by a slash. + + Note that this fucntion may return the error code + GPG_ERR_WRONG_CARD to indicate that the card currently present does + not match the one required for the requested action (e.g. the + serial number does not match). */ +static int +do_auth (APP app, const char *keyidstr, + int (*pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen ) +{ + int rc; + unsigned char tmp_sn[20]; /* actually 16 but we use it also for the fpr. */ + const char *s; + int n; + const char *fpr = NULL; + + if (!keyidstr || !*keyidstr) + return gpg_error (GPG_ERR_INV_VALUE); + if (indatalen > 50) /* For a 1024 bit key. */ + return gpg_error (GPG_ERR_INV_VALUE); + + /* Check whether an OpenPGP card of any version has been requested. */ + if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12)) + return gpg_error (GPG_ERR_INV_ID); + + for (s=keyidstr, n=0; hexdigitp (s); s++, n++) + ; + if (n != 32) + return gpg_error (GPG_ERR_INV_ID); + else if (!*s) + ; /* no fingerprint given: we allow this for now. */ + else if (*s == '/') + fpr = s + 1; + else + return gpg_error (GPG_ERR_INV_ID); + + for (s=keyidstr, n=0; n < 16; s += 2, n++) + tmp_sn[n] = xtoi_2 (s); + + if (app->serialnolen != 16) + return gpg_error (GPG_ERR_INV_CARD); + if (memcmp (app->serialno, tmp_sn, 16)) + return gpg_error (GPG_ERR_WRONG_CARD); + + /* If a fingerprint has been specified check it against the one on + the card. This is allows for a meaningful error message in case + the key on the card has been replaced but the shadow information + known to gpg was not updated. If there is no fingerprint, gpg + will detect a bogus signature anyway due to the + verify-after-signing feature. */ + if (fpr) + { + for (s=fpr, n=0; hexdigitp (s); s++, n++) + ; + if (n != 40) + return gpg_error (GPG_ERR_INV_ID); + else if (!*s) + ; /* okay */ + else + return gpg_error (GPG_ERR_INV_ID); + + for (s=fpr, n=0; n < 20; s += 2, n++) + tmp_sn[n] = xtoi_2 (s); + rc = compare_fingerprint (app, 3, tmp_sn); + if (rc) + return rc; + } + + if (!app->did_chv2) + { + char *pinvalue; + + rc = pincb (pincb_arg, "Authentication/Decryption PIN", &pinvalue); + if (rc) + { + log_info ("PIN callback returned error: %s\n", gpg_strerror (rc)); + return rc; + } + + rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue)); + xfree (pinvalue); + if (rc) + { + log_error ("verify CHV2 failed\n"); + rc = gpg_error (GPG_ERR_GENERAL); + return rc; + } + app->did_chv2 = 1; + } + + rc = iso7816_internal_authenticate (app->slot, indata, indatalen, + outdata, outdatalen); + return rc; +} + + +static int +do_decipher (APP app, const char *keyidstr, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen ) +{ + int rc; + unsigned char tmp_sn[20]; /* actually 16 but we use it also for the fpr. */ + const char *s; + int n; + const char *fpr = NULL; + + if (!keyidstr || !*keyidstr || !indatalen) + return gpg_error (GPG_ERR_INV_VALUE); + + /* Check whether an OpenPGP card of any version has been requested. */ + if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12)) + return gpg_error (GPG_ERR_INV_ID); + + for (s=keyidstr, n=0; hexdigitp (s); s++, n++) + ; + if (n != 32) + return gpg_error (GPG_ERR_INV_ID); + else if (!*s) + ; /* no fingerprint given: we allow this for now. */ + else if (*s == '/') + fpr = s + 1; + else + return gpg_error (GPG_ERR_INV_ID); + + for (s=keyidstr, n=0; n < 16; s += 2, n++) + tmp_sn[n] = xtoi_2 (s); + + if (app->serialnolen != 16) + return gpg_error (GPG_ERR_INV_CARD); + if (memcmp (app->serialno, tmp_sn, 16)) + return gpg_error (GPG_ERR_WRONG_CARD); + + /* If a fingerprint has been specified check it against the one on + the card. This is allows for a meaningful error message in case + the key on the card has been replaced but the shadow information + known to gpg was not updated. If there is no fingerprint, the + decryption will won't produce the right plaintext anyway. */ + if (fpr) + { + for (s=fpr, n=0; hexdigitp (s); s++, n++) + ; + if (n != 40) + return gpg_error (GPG_ERR_INV_ID); + else if (!*s) + ; /* okay */ + else + return gpg_error (GPG_ERR_INV_ID); + + for (s=fpr, n=0; n < 20; s += 2, n++) + tmp_sn[n] = xtoi_2 (s); + rc = compare_fingerprint (app, 2, tmp_sn); + if (rc) + return rc; + } + + if (!app->did_chv2) + { + char *pinvalue; + + rc = pincb (pincb_arg, "Decryption PIN", &pinvalue); +/* pinvalue = xstrdup ("123456"); */ +/* rc = 0; */ + if (rc) + { + log_info ("PIN callback returned error: %s\n", gpg_strerror (rc)); + return rc; + } + + rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue)); + xfree (pinvalue); + if (rc) + { + log_error ("verify CHV2 failed\n"); + rc = gpg_error (GPG_ERR_GENERAL); + return rc; + } + app->did_chv2 = 1; + } + + rc = iso7816_decipher (app->slot, indata, indatalen, outdata, outdatalen); + return rc; +} + + + + +/* Select the OpenPGP application on the card in SLOT. This function + must be used before any other OpenPGP application functions. */ +int +app_select_openpgp (APP app, unsigned char **sn, size_t *snlen) +{ + static char const aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 }; + int slot = app->slot; + int rc; + unsigned char *buffer; + size_t buflen; + + rc = iso7816_select_application (slot, aid, sizeof aid); + if (!rc) + { + /* fixme: get the full AID and check that the version is okay + with us. */ + rc = iso7816_get_data (slot, 0x004F, &buffer, &buflen); + if (rc) + goto leave; + if (opt.verbose) + { + log_info ("got AID: "); + log_printhex ("", buffer, buflen); + } + + if (sn) + { + *sn = buffer; + *snlen = buflen; + app->card_version = buffer[6] << 8; + app->card_version |= buffer[7]; + } + else + xfree (buffer); + + if (opt.verbose > 1) + dump_all_do (slot); + + app->fnc.learn_status = do_learn_status; + app->fnc.setattr = do_setattr; + app->fnc.genkey = do_genkey; + app->fnc.sign = do_sign; + app->fnc.auth = do_auth; + app->fnc.decipher = do_decipher; + app->fnc.change_pin = do_change_pin; + } + +leave: + return rc; +} + + + +/* This function is a hack to retrieve essential information about the + card to be displayed by simple tools. It mostly resembles what the + LEARN command returns. All parameters return allocated strings or + buffers or NULL if the data object is not available. All returned + values are sanitized. */ +int +app_openpgp_cardinfo (APP app, + char **serialno, + char **disp_name, + char **pubkey_url, + unsigned char **fpr1, + unsigned char **fpr2, + unsigned char **fpr3) +{ + int rc; + void *relptr; + unsigned char *value; + size_t valuelen; + + if (serialno) + { + time_t dummy; + + *serialno = NULL; + rc = app_get_serial_and_stamp (app, serialno, &dummy); + if (rc) + { + log_error ("error getting serial number: %s\n", gpg_strerror (rc)); + return rc; + } + } + + if (disp_name) + { + *disp_name = NULL; + relptr = get_one_do (app->slot, 0x005B, &value, &valuelen); + if (relptr) + { + *disp_name = make_printable_string (value, valuelen, 0); + xfree (relptr); + } + } + + if (pubkey_url) + { + *pubkey_url = NULL; + relptr = get_one_do (app->slot, 0x5F50, &value, &valuelen); + if (relptr) + { + *pubkey_url = make_printable_string (value, valuelen, 0); + xfree (relptr); + } + } + + if (fpr1) + *fpr1 = NULL; + if (fpr2) + *fpr2 = NULL; + if (fpr3) + *fpr3 = NULL; + relptr = get_one_do (app->slot, 0x00C5, &value, &valuelen); + if (relptr && valuelen >= 60) + { + if (fpr1) + { + *fpr1 = xmalloc (20); + memcpy (*fpr1, value + 0, 20); + } + if (fpr2) + { + *fpr2 = xmalloc (20); + memcpy (*fpr2, value + 20, 20); + } + if (fpr3) + { + *fpr3 = xmalloc (20); + memcpy (*fpr3, value + 40, 20); + } + } + xfree (relptr); + + return 0; +} + + + +/* This function is currently only used by the sc-copykeys program to + store a key on the smartcard. APP ist the application handle, + KEYNO is the number of the key and PINCB, PINCB_ARG are used to ask + for the SO PIN. TEMPLATE and TEMPLATE_LEN describe a buffer with + the key template to store. CREATED_AT is the timestamp used to + create the fingerprint. M, MLEN is the RSA modulus and E, ELEN the + RSA public exponent. This function silently overwrites an existing + key.*/ +int +app_openpgp_storekey (APP app, int keyno, + unsigned char *template, size_t template_len, + time_t created_at, + const unsigned char *m, size_t mlen, + const unsigned char *e, size_t elen, + int (*pincb)(void*, const char *, char **), + void *pincb_arg) +{ + int rc; + unsigned char fprbuf[20]; + + if (keyno < 1 || keyno > 3) + return gpg_error (GPG_ERR_INV_ID); + keyno--; + + { + char *pinvalue; + rc = pincb (pincb_arg, "Admin PIN", &pinvalue); + if (rc) + { + log_error ("error getting PIN: %s\n", gpg_strerror (rc)); + return rc; + } + rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue)); + xfree (pinvalue); + } + if (rc) + { + log_error ("verify CHV3 failed: rc=%04X\n", rc); + goto leave; + } + + rc = iso7816_put_data (app->slot, + (app->card_version > 0x0007? 0xE0 : 0xE9) + keyno, + template, template_len); + if (rc) + { + log_error ("failed to store the key: rc=%04X\n", rc); + rc = gpg_error (GPG_ERR_CARD); + goto leave; + } + +/* log_printhex ("RSA n:", m, mlen); */ +/* log_printhex ("RSA e:", e, elen); */ + + rc = store_fpr (app->slot, keyno, (u32)created_at, + m, mlen, e, elen, fprbuf, app->card_version); + + leave: + return rc; +} + + +/* Utility function for external tools: Read the public RSA key at + KEYNO and return modulus and exponent in (M,MLEN) and (E,ELEN). */ +int +app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen, + unsigned char **e, size_t *elen) +{ + int rc; + const unsigned char *keydata, *a; + unsigned char *buffer; + size_t buflen, keydatalen, alen; + + *m = NULL; + *e = NULL; + + if (keyno < 1 || keyno > 3) + return gpg_error (GPG_ERR_INV_ID); + keyno--; + + rc = iso7816_read_public_key(app->slot, + keyno == 0? "\xB6" : + keyno == 1? "\xB8" : "\xA4", + 2, + &buffer, &buflen); + if (rc) + { + rc = gpg_error (GPG_ERR_CARD); + log_error ("reading key failed\n"); + goto leave; + } + + keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen, 0); + if (!keydata) + { + log_error ("response does not contain the public key data\n"); + rc = gpg_error (GPG_ERR_CARD); + goto leave; + } + + a = find_tlv (keydata, keydatalen, 0x0081, &alen, 0); + if (!a) + { + log_error ("response does not contain the RSA modulus\n"); + rc = gpg_error (GPG_ERR_CARD); + goto leave; + } + *mlen = alen; + *m = xmalloc (alen); + memcpy (*m, a, alen); + + a = find_tlv (keydata, keydatalen, 0x0082, &alen, 0); + if (!e) + { + log_error ("response does not contain the RSA public exponent\n"); + rc = gpg_error (GPG_ERR_CARD); + goto leave; + } + *elen = alen; + *e = xmalloc (alen); + memcpy (*e, a, alen); + + leave: + xfree (buffer); + if (rc) + { + xfree (*m); *m = NULL; + xfree (*e); *e = NULL; + } + return rc; +} diff --git a/scd/app.c b/scd/app.c new file mode 100644 index 000000000..7a85df336 --- /dev/null +++ b/scd/app.c @@ -0,0 +1,278 @@ +/* app.c - Application selection. + * 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 "app-common.h" +#include "apdu.h" +#include "iso7816.h" + +/* The select the best fitting application and return a context. + Returns NULL if no application was found or no card is present. */ +APP +select_application (void) +{ + int reader_port = 32768; /* First USB reader. */ + int slot; + int rc; + APP app; + + slot = apdu_open_reader (reader_port); + if (slot == -1) + { + log_error ("card reader not available\n"); + return NULL; + } + + app = xtrycalloc (1, sizeof *app); + if (!app) + { + rc = out_of_core (); + log_info ("error allocating context: %s\n", gpg_strerror (rc)); + /*apdu_close_reader (slot);*/ + return NULL; + } + + app->slot = slot; + rc = app_select_openpgp (app, &app->serialno, &app->serialnolen); + if (rc) + { +/* apdu_close_reader (slot); */ + log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc)); + xfree (app); + return NULL; + } + + app->initialized = 1; + return app; +} + + + +/* Retrieve the serial number and the time of the last update of the + card. The serial number is returned as a malloced string (hex + encoded) in SERIAL and the time of update is returned in STAMP. If + no update time is available the returned value is 0. Caller must + free SERIAL unless the function returns an error. */ +int +app_get_serial_and_stamp (APP app, char **serial, time_t *stamp) +{ + unsigned char *buf, *p; + int i; + + if (!app || !serial || !stamp) + return gpg_error (GPG_ERR_INV_VALUE); + + *serial = NULL; + *stamp = 0; /* not available */ + + buf = xtrymalloc (app->serialnolen * 2 + 1); + if (!buf) + return gpg_error_from_errno (errno); + for (p=buf, i=0; i < app->serialnolen; p +=2, i++) + sprintf (p, "%02X", app->serialno[i]); + *p = 0; + *serial = buf; + return 0; +} + + +/* Write out the application specifig status lines for the LEARN + command. */ +int +app_write_learn_status (APP app, CTRL ctrl) +{ + if (!app) + return gpg_error (GPG_ERR_INV_VALUE); + if (!app->initialized) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (!app->fnc.learn_status) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + return app->fnc.learn_status (app, ctrl); +} + + +/* Perform a SETATTR operation. */ +int +app_setattr (APP app, const char *name, + int (*pincb)(void*, const char *, char **), + void *pincb_arg, + const unsigned char *value, size_t valuelen) +{ + if (!app || !name || !*name || !value) + return gpg_error (GPG_ERR_INV_VALUE); + if (!app->initialized) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (!app->fnc.setattr) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + return app->fnc.setattr (app, name, pincb, pincb_arg, value, valuelen); +} + +/* Create the signature and return the allocated result in OUTDATA. + If a PIN is required the PINCB will be used to ask for the PIN; it + should return the PIN in an allocated buffer and put it into PIN. */ +int +app_sign (APP app, const char *keyidstr, int hashalgo, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen ) +{ + int rc; + + if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb) + return gpg_error (GPG_ERR_INV_VALUE); + if (!app->initialized) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (!app->fnc.sign) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + rc = app->fnc.sign (app, keyidstr, hashalgo, + pincb, pincb_arg, + indata, indatalen, + outdata, outdatalen); + if (opt.verbose) + log_info ("operation sign result: %s\n", gpg_strerror (rc)); + return rc; +} + +/* Create the signature using the INTERNAL AUTHENTICATE command and + return the allocated result in OUTDATA. If a PIN is required the + PINCB will be used to ask for the PIN; it should return the PIN in + an allocated buffer and put it into PIN. */ +int +app_auth (APP app, const char *keyidstr, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen ) +{ + int rc; + + if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb) + return gpg_error (GPG_ERR_INV_VALUE); + if (!app->initialized) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (!app->fnc.auth) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + rc = app->fnc.auth (app, keyidstr, + pincb, pincb_arg, + indata, indatalen, + outdata, outdatalen); + if (opt.verbose) + log_info ("operation auth result: %s\n", gpg_strerror (rc)); + return rc; +} + + +/* Decrypt the data in INDATA and return the allocated result in OUTDATA. + If a PIN is required the PINCB will be used to ask for the PIN; it + should return the PIN in an allocated buffer and put it into PIN. */ +int +app_decipher (APP app, const char *keyidstr, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen ) +{ + int rc; + + if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb) + return gpg_error (GPG_ERR_INV_VALUE); + if (!app->initialized) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (!app->fnc.decipher) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + rc = app->fnc.decipher (app, keyidstr, + pincb, pincb_arg, + indata, indatalen, + outdata, outdatalen); + if (opt.verbose) + log_info ("operation decipher result: %s\n", gpg_strerror (rc)); + return rc; +} + + +/* Perform a SETATTR operation. */ +int +app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, + int (*pincb)(void*, const char *, char **), + void *pincb_arg) +{ + int rc; + + if (!app || !keynostr || !*keynostr || !pincb) + return gpg_error (GPG_ERR_INV_VALUE); + if (!app->initialized) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (!app->fnc.genkey) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + rc = app->fnc.genkey (app, ctrl, keynostr, flags, pincb, pincb_arg); + if (opt.verbose) + log_info ("operation genkey result: %s\n", gpg_strerror (rc)); + return rc; +} + + +/* Perform a GET CHALLENGE operation. This fucntion is special as it + directly accesses the card without any application specific + wrapper. */ +int +app_get_challenge (APP app, size_t nbytes, unsigned char *buffer) +{ + if (!app || !nbytes || !buffer) + return gpg_error (GPG_ERR_INV_VALUE); + if (!app->initialized) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + return iso7816_get_challenge (app->slot, nbytes, buffer); +} + + + +/* Perform a CHANGE REFERENCE DATA or RESET RETRY COUNTER operation. */ +int +app_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode, + int (*pincb)(void*, const char *, char **), + void *pincb_arg) +{ + int rc; + + if (!app || !chvnostr || !*chvnostr || !pincb) + return gpg_error (GPG_ERR_INV_VALUE); + if (!app->initialized) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (!app->fnc.change_pin) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + rc = app->fnc.change_pin (app, ctrl, chvnostr, reset_mode, pincb, pincb_arg); + if (opt.verbose) + log_info ("operation change_pin result: %s\n", gpg_strerror (rc)); + return rc; +} + + + + + + diff --git a/scd/card-common.h b/scd/card-common.h new file mode 100644 index 000000000..31f0dfe8f --- /dev/null +++ b/scd/card-common.h @@ -0,0 +1,73 @@ +/* card-common.h - Common declarations for all card types + * Copyright (C) 2001, 2002 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 CARD_COMMON_H +#define CARD_COMMON_H + +/* Declaration of private data structure used by card-p15.c */ +struct p15private_s; + + +struct card_ctx_s { + int reader; /* used reader */ + struct sc_context *ctx; + struct sc_card *scard; + struct sc_pkcs15_card *p15card; /* only if there is a pkcs15 application */ + struct p15private_s *p15priv; /* private data used by card-p15.c */ + + struct { + int initialized; /* the card has been initialied and the function + pointers may be used. However for + unsupported operations the particular + function pointer is set to NULL */ + + int (*enum_keypairs) (CARD card, int idx, + unsigned char *keygrip, char **keyid); + int (*enum_certs) (CARD card, int idx, char **certid, int *certtype); + int (*read_cert) (CARD card, const char *certidstr, + unsigned char **cert, size_t *ncert); + int (*sign) (CARD card, + const char *keyidstr, int hashalgo, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen ); + int (*decipher) (CARD card, const char *keyidstr, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen); + } fnc; + +}; + +/*-- card.c --*/ +gpg_error_t map_sc_err (int rc); +int card_help_get_keygrip (KsbaCert cert, unsigned char *array); + +/*-- card-15.c --*/ +void p15_release_private_data (CARD card); + +/* constructors */ +void card_p15_bind (CARD card); +void card_dinsig_bind (CARD card); + + +#endif /*CARD_COMMON_H*/ diff --git a/scd/card-p15.c b/scd/card-p15.c new file mode 100644 index 000000000..3cf4ba519 --- /dev/null +++ b/scd/card-p15.c @@ -0,0 +1,502 @@ +/* card-p15.c - PKCS-15 based card access + * Copyright (C) 2002 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 + +#ifdef HAVE_OPENSC +#include +#include + +#include "scdaemon.h" +#include "card-common.h" + + +struct p15private_s { + int n_prkey_rsa_objs; + struct sc_pkcs15_object *prkey_rsa_objs[32]; + int n_cert_objs; + struct sc_pkcs15_object *cert_objs[32]; +}; + + +/* Allocate private data. */ +static int +init_private_data (CARD card) +{ + struct p15private_s *priv; + int rc; + + if (card->p15priv) + return 0; /* already done. */ + + priv = xtrycalloc (1, sizeof *priv); + if (!priv) + return out_of_core (); + + /* OpenSC (0.7.0) is a bit strange in that the get_objects functions + tries to be a bit too clever and implicitly does an enumeration + which eventually leads to the fact that every call to this + fucntion returns one more macthing object. The old code in + p15_enum_keypairs assume that it would alwyas return the same + numer of objects and used this to figure out what the last object + enumerated is. We now do an enum_objects just once and keep it + in the private data. */ + rc = sc_pkcs15_get_objects (card->p15card, SC_PKCS15_TYPE_PRKEY_RSA, + priv->prkey_rsa_objs, + DIM (priv->prkey_rsa_objs)); + if (rc < 0) + { + log_error ("private keys enumeration failed: %s\n", sc_strerror (rc)); + xfree (priv); + return gpg_error (GPG_ERR_CARD); + } + priv->n_prkey_rsa_objs = rc; + + /* Read all certificate objects. */ + rc = sc_pkcs15_get_objects (card->p15card, SC_PKCS15_TYPE_CERT_X509, + priv->cert_objs, + DIM (priv->cert_objs)); + if (rc < 0) + { + log_error ("private keys enumeration failed: %s\n", sc_strerror (rc)); + xfree (priv); + return gpg_error (GPG_ERR_CARD); + } + priv->n_cert_objs = rc; + + card->p15priv = priv; + return 0; +} + + +/* Release private data used in this module. */ +void +p15_release_private_data (CARD card) +{ + if (!card->p15priv) + return; + xfree (card->p15priv); + card->p15priv = NULL; +} + + + +/* See card.c for interface description */ +static int +p15_enum_keypairs (CARD card, int idx, + unsigned char *keygrip, char **keyid) +{ + int rc; + KsbaError krc; + struct p15private_s *priv; + struct sc_pkcs15_object *tmpobj; + int nobjs; + struct sc_pkcs15_prkey_info *pinfo; + struct sc_pkcs15_cert_info *certinfo; + struct sc_pkcs15_cert *certder; + KsbaCert cert; + + rc = init_private_data (card); + if (rc) + return rc; + priv = card->p15priv; + nobjs = priv->n_prkey_rsa_objs; + rc = 0; + if (idx >= nobjs) + return -1; + pinfo = priv->prkey_rsa_objs[idx]->data; + + /* now we need to read the certificate so that we can calculate the + keygrip */ + rc = sc_pkcs15_find_cert_by_id (card->p15card, &pinfo->id, &tmpobj); + if (rc) + { + log_info ("certificate for private key %d not found: %s\n", + idx, sc_strerror (rc)); + /* note, that we return the ID anyway */ + rc = gpg_error (GPG_ERR_MISSING_CERTIFICATE); + goto return_keyid; + } + certinfo = tmpobj->data; + rc = sc_pkcs15_read_certificate (card->p15card, certinfo, &certder); + if (rc) + { + log_info ("failed to read certificate for private key %d: %s\n", + idx, sc_strerror (rc)); + return gpg_error (GPG_ERR_CARD); + } + + cert = ksba_cert_new (); + if (!cert) + { + gpg_error_t tmperr = out_of_core (); + sc_pkcs15_free_certificate (certder); + return tmperr; + } + krc = ksba_cert_init_from_mem (cert, certder->data, certder->data_len); + sc_pkcs15_free_certificate (certder); + if (krc) + { + log_error ("failed to parse the certificate for private key %d: %s\n", + idx, ksba_strerror (krc)); + ksba_cert_release (cert); + return gpg_error (GPG_ERR_CARD); + } + if (card_help_get_keygrip (cert, keygrip)) + { + log_error ("failed to calculate the keygrip of private key %d\n", idx); + ksba_cert_release (cert); + return gpg_error (GPG_ERR_CARD); + } + ksba_cert_release (cert); + + rc = 0; + return_keyid: + if (keyid) + { + char *p; + int i; + + *keyid = p = xtrymalloc (9+pinfo->id.len*2+1); + if (!*keyid) + return out_of_core (); + p = stpcpy (p, "P15-5015."); + for (i=0; i < pinfo->id.len; i++, p += 2) + sprintf (p, "%02X", pinfo->id.value[i]); + *p = 0; + } + + return rc; +} + +/* See card.c for interface description */ +static int +p15_enum_certs (CARD card, int idx, char **certid, int *type) +{ + int rc; + struct p15private_s *priv; + struct sc_pkcs15_object *obj; + struct sc_pkcs15_cert_info *cinfo; + int nobjs; + + rc = init_private_data (card); + if (rc) + return rc; + priv = card->p15priv; + nobjs = priv->n_cert_objs; + rc = 0; + if (idx >= nobjs) + return -1; + obj = priv->cert_objs[idx]; + cinfo = obj->data; + + if (certid) + { + char *p; + int i; + + *certid = p = xtrymalloc (9+cinfo->id.len*2+1); + if (!*certid) + return out_of_core (); + p = stpcpy (p, "P15-5015."); + for (i=0; i < cinfo->id.len; i++, p += 2) + sprintf (p, "%02X", cinfo->id.value[i]); + *p = 0; + } + if (type) + { + if (!obj->df) + *type = 0; /* unknown */ + else if (obj->df->type == SC_PKCS15_CDF) + *type = 100; + else if (obj->df->type == SC_PKCS15_CDF_TRUSTED) + *type = 101; + else if (obj->df->type == SC_PKCS15_CDF_USEFUL) + *type = 102; + else + *type = 0; /* error -> unknown */ + } + + return rc; +} + + + +static int +idstr_to_id (const char *idstr, struct sc_pkcs15_id *id) +{ + const char *s; + int n; + + /* For now we only support the standard DF */ + if (strncmp (idstr, "P15-5015.", 9) ) + return gpg_error (GPG_ERR_INV_ID); + for (s=idstr+9, n=0; hexdigitp (s); s++, n++) + ; + if (*s || (n&1)) + return gpg_error (GPG_ERR_INV_ID); /*invalid or odd number of digits*/ + n /= 2; + if (!n || n > SC_PKCS15_MAX_ID_SIZE) + return gpg_error (GPG_ERR_INV_ID); /* empty or too large */ + for (s=idstr+9, n=0; *s; s += 2, n++) + id->value[n] = xtoi_2 (s); + id->len = n; + return 0; +} + + +/* See card.c for interface description */ +static int +p15_read_cert (CARD card, const char *certidstr, + unsigned char **cert, size_t *ncert) +{ + struct sc_pkcs15_object *tmpobj; + struct sc_pkcs15_id certid; + struct sc_pkcs15_cert_info *certinfo; + struct sc_pkcs15_cert *certder; + int rc; + + if (!card || !certidstr || !cert || !ncert) + return gpg_error (GPG_ERR_INV_VALUE); + if (!card->p15card) + return gpg_error (GPG_ERR_NO_PKCS15_APP); + + rc = idstr_to_id (certidstr, &certid); + if (rc) + return rc; + + rc = sc_pkcs15_find_cert_by_id (card->p15card, &certid, &tmpobj); + if (rc) + { + log_info ("certificate '%s' not found: %s\n", + certidstr, sc_strerror (rc)); + return -1; + } + certinfo = tmpobj->data; + rc = sc_pkcs15_read_certificate (card->p15card, certinfo, &certder); + if (rc) + { + log_info ("failed to read certificate '%s': %s\n", + certidstr, sc_strerror (rc)); + return gpg_error (GPG_ERR_CARD); + } + + *cert = xtrymalloc (certder->data_len); + if (!*cert) + { + gpg_error_t tmperr = out_of_core (); + sc_pkcs15_free_certificate (certder); + return tmperr; + } + memcpy (*cert, certder->data, certder->data_len); + *ncert = certder->data_len; + sc_pkcs15_free_certificate (certder); + return 0; +} + + + + + +static int +p15_prepare_key (CARD card, const char *keyidstr, + int (pincb)(void*, const char *, char **), + void *pincb_arg, struct sc_pkcs15_object **r_keyobj) +{ + struct sc_pkcs15_id keyid; + struct sc_pkcs15_pin_info *pin; + struct sc_pkcs15_object *keyobj, *pinobj; + char *pinvalue; + int rc; + + rc = idstr_to_id (keyidstr, &keyid); + if (rc) + return rc; + + rc = sc_pkcs15_find_prkey_by_id (card->p15card, &keyid, &keyobj); + if (rc < 0) + { + log_error ("private key not found: %s\n", sc_strerror(rc)); + return gpg_error (GPG_ERR_NO_SECRET_KEY); + } + + rc = sc_pkcs15_find_pin_by_auth_id (card->p15card, + &keyobj->auth_id, &pinobj); + if (rc) + { + log_error ("failed to find PIN by auth ID: %s\n", sc_strerror (rc)); + return gpg_error (GPG_ERR_BAD_PIN_METHOD); + } + pin = pinobj->data; + + /* Fixme: pack this into a verification loop */ + /* Fixme: we might want to pass pin->min_length and + pin->stored_length */ + rc = pincb (pincb_arg, pinobj->label, &pinvalue); + if (rc) + { + log_info ("PIN callback returned error: %s\n", gnupg_strerror (rc)); + return rc; + } + + rc = sc_pkcs15_verify_pin (card->p15card, pin, + pinvalue, strlen (pinvalue)); + xfree (pinvalue); + if (rc) + { + log_info ("PIN verification failed: %s\n", sc_strerror (rc)); + return gpg_error (GPG_ERR_BAD_PIN); + } + + /* fixme: check wheter we need to release KEYOBJ in case of an error */ + *r_keyobj = keyobj; + return 0; +} + + +/* See card.c for interface description */ +static int +p15_sign (CARD card, const char *keyidstr, int hashalgo, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen ) +{ + unsigned int cryptflags; + struct sc_pkcs15_object *keyobj; + int rc; + unsigned char *outbuf = NULL; + size_t outbuflen; + + if (hashalgo != GCRY_MD_SHA1) + return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + + rc = p15_prepare_key (card, keyidstr, pincb, pincb_arg, &keyobj); + if (rc) + return rc; + + cryptflags = SC_ALGORITHM_RSA_PAD_PKCS1; + + outbuflen = 1024; + outbuf = xtrymalloc (outbuflen); + if (!outbuf) + return out_of_core (); + + rc = sc_pkcs15_compute_signature (card->p15card, keyobj, + cryptflags, + indata, indatalen, + outbuf, outbuflen ); + if (rc < 0) + { + log_error ("failed to create signature: %s\n", sc_strerror (rc)); + rc = gpg_error (GPG_ERR_CARD); + } + else + { + *outdatalen = rc; + *outdata = outbuf; + outbuf = NULL; + rc = 0; + } + + xfree (outbuf); + return rc; +} + + +/* See card.c for description */ +static int +p15_decipher (CARD card, const char *keyidstr, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen ) +{ + struct sc_pkcs15_object *keyobj; + int rc; + unsigned char *outbuf = NULL; + size_t outbuflen; + + rc = p15_prepare_key (card, keyidstr, pincb, pincb_arg, &keyobj); + if (rc) + return rc; + + if (card && card->scard && card->scard->driver + && !strcasecmp (card->scard->driver->short_name, "tcos")) + { + /* very ugly hack to force the use of a local key. We need this + until we have fixed the initialization code for TCOS cards */ + struct sc_pkcs15_prkey_info *prkey = keyobj->data; + if ( !(prkey->key_reference & 0x80)) + { + prkey->key_reference |= 0x80; + log_debug ("using TCOS hack to force the use of local keys\n"); + } + if (*keyidstr && keyidstr[strlen(keyidstr)-1] == '6') + { + prkey->key_reference |= 1; + log_debug ("warning: using even more TCOS hacks\n"); + } + } + + outbuflen = indatalen < 256? 256 : indatalen; + outbuf = xtrymalloc (outbuflen); + if (!outbuf) + return out_of_core (); + + rc = sc_pkcs15_decipher (card->p15card, keyobj, + 0, + indata, indatalen, + outbuf, outbuflen); + if (rc < 0) + { + log_error ("failed to decipher the data: %s\n", sc_strerror (rc)); + rc = gpg_error (GPG_ERR_CARD); + } + else + { + *outdatalen = rc; + *outdata = outbuf; + outbuf = NULL; + rc = 0; + } + + xfree (outbuf); + return rc; +} + + + +/* Bind our operations to the card */ +void +card_p15_bind (CARD card) +{ + card->fnc.enum_keypairs = p15_enum_keypairs; + card->fnc.enum_certs = p15_enum_certs; + card->fnc.read_cert = p15_read_cert; + card->fnc.sign = p15_sign; + card->fnc.decipher = p15_decipher; +} +#endif /*HAVE_OPENSC*/ diff --git a/scd/card.c b/scd/card.c new file mode 100644 index 000000000..02b7bfdbf --- /dev/null +++ b/scd/card.c @@ -0,0 +1,564 @@ +/* card.c - SCdaemon card functions + * Copyright (C) 2002 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 + +#ifdef HAVE_OPENSC +#include +#endif +#include + +#include "scdaemon.h" +#include "card-common.h" + +/* Map the SC error codes to the GNUPG ones */ +gpg_error_t +map_sc_err (int rc) +{ + gpg_err_code_t e; + + switch (rc) + { + case 0: e = 0; break; +#ifdef HAVE_OPENSC + case SC_ERROR_NOT_SUPPORTED: e = GPG_ERR_NOT_SUPPORTED; break; + case SC_ERROR_PKCS15_APP_NOT_FOUND: e = GPG_ERR_NO_PKCS15_APP; break; + case SC_ERROR_OUT_OF_MEMORY: e = GPG_ERR_ENOMEM; break; + case SC_ERROR_CARD_NOT_PRESENT: e = GPG_ERR_CARD_NOT_PRESENT; break; + case SC_ERROR_CARD_REMOVED: e = GPG_ERR_CARD_REMOVED; break; + case SC_ERROR_INVALID_CARD: e = GPG_ERR_INV_CARD; break; +#endif + default: e = GPG_ERR_CARD; break; + } + return gpg_err_make (GPG_ERR_SOURCE_UNKNOWN, e); +} + +/* Get the keygrip from CERT, return 0 on success */ +int +card_help_get_keygrip (KsbaCert cert, unsigned char *array) +{ + gcry_sexp_t s_pkey; + int rc; + KsbaSexp p; + size_t n; + + p = ksba_cert_get_public_key (cert); + if (!p) + return -1; /* oops */ + n = gcry_sexp_canon_len (p, 0, NULL, NULL); + if (!n) + return -1; /* libksba did not return a proper S-expression */ + rc = gcry_sexp_sscan ( &s_pkey, NULL, p, n); + xfree (p); + if (rc) + return -1; /* can't parse that S-expression */ + array = gcry_pk_get_keygrip (s_pkey, array); + gcry_sexp_release (s_pkey); + if (!array) + return -1; /* failed to calculate the keygrip */ + return 0; +} + + + + + + + +/* Create a new context for the card and figures out some basic + information of the card. Detects whgether a PKCS_15 application is + stored. + + Common errors: GPG_ERR_CARD_NOT_PRESENT */ +int +card_open (CARD *rcard) +{ +#ifdef HAVE_OPENSC + CARD card; + int rc; + + card = xtrycalloc (1, sizeof *card); + if (!card) + return out_of_core (); + card->reader = 0; + + rc = sc_establish_context (&card->ctx, "scdaemon"); + if (rc) + { + log_error ("failed to establish SC context: %s\n", sc_strerror (rc)); + rc = map_sc_err (rc); + goto leave; + } + if (card->reader >= card->ctx->reader_count) + { + log_error ("no card reader available\n"); + rc = gpg_error (GPG_ERR_CARD); + goto leave; + } + card->ctx->error_file = log_get_stream (); + card->ctx->debug = opt.debug_sc; + card->ctx->debug_file = log_get_stream (); + + if (sc_detect_card_presence (card->ctx->reader[card->reader], 0) != 1) + { + rc = gpg_error (GPG_ERR_CARD_NOT_PRESENT); + goto leave; + } + + rc = sc_connect_card (card->ctx->reader[card->reader], 0, &card->scard); + if (rc) + { + log_error ("failed to connect card in reader %d: %s\n", + card->reader, sc_strerror (rc)); + rc = map_sc_err (rc); + goto leave; + } + if (opt.verbose) + log_info ("connected to card in reader %d using driver `%s'\n", + card->reader, card->scard->driver->name); + + rc = sc_lock (card->scard); + if (rc) + { + log_error ("can't lock card in reader %d: %s\n", + card->reader, sc_strerror (rc)); + rc = map_sc_err (rc); + goto leave; + } + + + leave: + if (rc) + card_close (card); + else + *rcard = card; + + return rc; +#else + return gpg_error (GPG_ERR_NOT_SUPPORTED); +#endif +} + + +/* Close a card and release all resources */ +void +card_close (CARD card) +{ + if (card) + { +#ifdef HAVE_OPENSC + if (card->p15card) + { + sc_pkcs15_unbind (card->p15card); + card->p15card = NULL; + } + if (card->p15priv) + p15_release_private_data (card); + if (card->scard) + { + sc_unlock (card->scard); + sc_disconnect_card (card->scard, 0); + card->scard = NULL; + } + if (card->ctx) + { + sc_release_context (card->ctx); + card->ctx = NULL; + } +#endif + xfree (card); + } +} + +/* Locate a simple TLV encoded data object in BUFFER of LENGTH and + return a pointer to value as well as its length in NBYTES. Return + NULL if it was not found. Note, that the function does not check + whether the value fits into the provided buffer. */ +#ifdef HAVE_OPENSC +static const char * +find_simple_tlv (const unsigned char *buffer, size_t length, + int tag, size_t *nbytes) +{ + const char *s = buffer; + size_t n = length; + size_t len; + + for (;;) + { + buffer = s; + if (n < 2) + return NULL; /* buffer too short for tag and length. */ + len = s[1]; + s += 2; n -= 2; + if (len == 255) + { + if (n < 2) + return NULL; /* we expected 2 more bytes with the length. */ + len = (s[0] << 8) | s[1]; + s += 2; n -= 2; + } + if (*buffer == tag) + { + *nbytes = len; + return s; + } + if (len > n) + return NULL; /* buffer too short to skip to the next tag. */ + s += len; n -= len; + } +} +#endif /*HAVE_OPENSC*/ + +/* Find the ICC Serial Number within the provided BUFFER of LENGTH + (which should contain the GDO file) and return it as a hex encoded + string and allocated string in SERIAL. Return an error code when + the ICCSN was not found. */ +#ifdef HAVE_OPENSC +static int +find_iccsn (const unsigned char *buffer, size_t length, char **serial) +{ + size_t n; + const unsigned char *s; + char *p; + + s = find_simple_tlv (buffer, length, 0x5A, &n); + if (!s) + return gpg_error (GPG_ERR_CARD); + length -= s - buffer; + if (n > length) + { + /* Oops, it does not fit into the buffer. This is an invalid + encoding (or the buffer is too short. However, I have some + test cards with such an invalid encoding and therefore I use + this ugly workaround to return something I can further + experiment with. */ + if (n == 0x0D && length+1 == n) + { + log_debug ("enabling BMI testcard workaround\n"); + n--; + } + else + return gpg_error (GPG_ERR_CARD); /* Bad encoding; does + not fit into buffer. */ + } + if (!n) + return gpg_error (GPG_ERR_CARD); /* Well, that is too short. */ + + *serial = p = xtrymalloc (2*n+1); + if (!*serial) + return out_of_core (); + for (; n; n--, p += 2, s++) + sprintf (p, "%02X", *s); + *p = 0; + return 0; +} +#endif /*HAVE_OPENSC*/ + +/* Retrieve the serial number and the time of the last update of the + card. The serial number is returned as a malloced string (hex + encoded) in SERIAL and the time of update is returned in STAMP. + If no update time is available the returned value is 0. The serial + is mandatory for a PKCS_15 application and an error will be + returned if this value is not availbale. For non-PKCS-15 cards a + serial number is constructed by other means. Caller must free + SERIAL unless the function returns an error. */ +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 gpg_error (GPG_ERR_INV_VALUE); + + *serial = NULL; + *stamp = 0; /* not available */ + +#ifdef HAVE_OPENSC + if (!card->fnc.initialized) + { + card->fnc.initialized = 1; + /* The first use of this card tries to figure out the type of the card + and sets up the function pointers. */ + rc = sc_pkcs15_bind (card->scard, &card->p15card); + if (rc) + { + if (rc != SC_ERROR_PKCS15_APP_NOT_FOUND) + log_error ("binding of existing PKCS-15 failed in reader %d: %s\n", + card->reader, sc_strerror (rc)); + card->p15card = NULL; + rc = 0; + } + if (card->p15card) + card_p15_bind (card); + else + card_dinsig_bind (card); + card->fnc.initialized = 1; + } + + + /* We should lookup the iso 7812-1 and 8583-3 - argh ISO + practice is suppressing innovation - IETF rules! So we + always get the serialnumber from the 2F02 GDO file. */ + /* FIXME: in case we can't parse the 2F02 EF and we have a P15 card, + we should get the serial number from the respective P15 file */ + sc_format_path ("3F002F02", &path); + rc = sc_select_file (card->scard, &path, &file); + if (rc) + { + log_error ("sc_select_file failed: %s\n", sc_strerror (rc)); + return gpg_error (GPG_ERR_CARD); + } + if (file->type != SC_FILE_TYPE_WORKING_EF + || file->ef_structure != SC_FILE_EF_TRANSPARENT) + { + log_error ("wrong type or structure of GDO file\n"); + sc_file_free (file); + return gpg_error (GPG_ERR_CARD); + } + + if (!file->size || file->size >= DIM(buf) ) + { /* FIXME: Use a real parser */ + log_error ("unsupported size of GDO file (%d)\n", file->size); + sc_file_free (file); + return gpg_error (GPG_ERR_CARD); + } + buflen = file->size; + + rc = sc_read_binary (card->scard, 0, buf, buflen, 0); + sc_file_free (file); + if (rc < 0) + { + log_error ("error reading GDO file: %s\n", sc_strerror (rc)); + return gpg_error (GPG_ERR_CARD); + } + if (rc != buflen) + { + log_error ("short read on GDO file\n"); + return gpg_error (GPG_ERR_CARD); + } + + rc = find_iccsn (buf, buflen, serial); + if (gpg_err_code (rc) == GPG_ERR_CARD) + log_error ("invalid structure of GDO file\n"); + if (!rc && card->p15card && !strcmp (*serial, "D27600000000000000000000")) + { /* This is a German card with a silly serial number. Try to get + the serial number from the EF(TokenInfo). We indicate such a + serial number by the using the prefix: "FF0100". */ + const char *efser = card->p15card->serial_number; + char *p; + + if (!efser) + efser = ""; + + xfree (*serial); + *serial = NULL; + p = xtrymalloc (strlen (efser) + 7); + if (!p) + rc = out_of_core (); + else + { + strcpy (p, "FF0100"); + strcpy (p+6, efser); + *serial = p; + } + } + else if (!rc && **serial == 'F' && (*serial)[1] == 'F') + { /* The serial number starts with our special prefix. This + requires that we put our default prefix "FF0000" in front. */ + char *p = xtrymalloc (strlen (*serial) + 7); + if (!p) + { + xfree (*serial); + *serial = NULL; + rc = out_of_core (); + } + else + { + strcpy (p, "FF0000"); + strcpy (p+6, *serial); + xfree (*serial); + *serial = p; + } + } + return rc; +#else + return gpg_error (GPG_ERR_NOT_SUPPORTED); +#endif +} + + +/* Enumerate all keypairs on the card and return the Keygrip as well + as the internal identification of the key. KEYGRIP must be a + caller provided buffer with a size of 20 bytes which will receive + the KEYGRIP of the keypair. If KEYID is not NULL, it returns the + ID field of the key in allocated memory; this is a string without + spaces. The function returns -1 when all keys have been + enumerated. Note that the error GPG_ERR_MISSING_CERTIFICATE may be + returned if there is just the private key but no public key (ie.e a + certificate) available. Applications might want to continue + enumerating after this error.*/ +int +card_enum_keypairs (CARD card, int idx, + unsigned char *keygrip, + char **keyid) +{ + int rc; + + if (keyid) + *keyid = NULL; + + if (!card || !keygrip) + return gpg_error (GPG_ERR_INV_VALUE); + if (idx < 0) + return gpg_error (GPG_ERR_INV_INDEX); + if (!card->fnc.initialized) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (!card->fnc.enum_keypairs) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + rc = card->fnc.enum_keypairs (card, idx, keygrip, keyid); + if (opt.verbose) + log_info ("card operation enum_keypairs result: %s\n", + gpg_strerror (rc)); + return rc; +} + + +/* Enumerate all trusted certificates available on the card, return + their ID in CERT and the type in CERTTYPE. Types of certificates + are: + 0 := Unknown + 100 := Regular X.509 cert + 101 := Trusted X.509 cert + 102 := Useful X.509 cert + */ +int +card_enum_certs (CARD card, int idx, char **certid, int *certtype) +{ + int rc; + + if (certid) + *certid = NULL; + + if (!card) + return gpg_error (GPG_ERR_INV_VALUE); + if (idx < 0) + return gpg_error (GPG_ERR_INV_INDEX); + if (!card->fnc.initialized) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (!card->fnc.enum_certs) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + rc = card->fnc.enum_certs (card, idx, certid, certtype); + if (opt.verbose) + log_info ("card operation enum_certs result: %s\n", + gpg_strerror (rc)); + return rc; +} + + + +/* Read the certificate identified by CERTIDSTR which is the + hexadecimal encoded ID of the certificate, prefixed with the string + "3F005015.". The certificate is return in DER encoded form in CERT + and NCERT. */ +int +card_read_cert (CARD card, const char *certidstr, + unsigned char **cert, size_t *ncert) +{ + int rc; + + if (!card || !certidstr || !cert || !ncert) + return gpg_error (GPG_ERR_INV_VALUE); + if (!card->fnc.initialized) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (!card->fnc.read_cert) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + rc = card->fnc.read_cert (card, certidstr, cert, ncert); + if (opt.verbose) + log_info ("card operation read_cert result: %s\n", gpg_strerror (rc)); + return rc; +} + + +/* Create the signature and return the allocated result in OUTDATA. + If a PIN is required the PINCB will be used to ask for the PIN; it + should return the PIN in an allocated buffer and put it into PIN. */ +int +card_sign (CARD card, const char *keyidstr, int hashalgo, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen ) +{ + int rc; + + if (!card || !indata || !indatalen || !outdata || !outdatalen || !pincb) + return gpg_error (GPG_ERR_INV_VALUE); + if (!card->fnc.initialized) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (!card->fnc.sign) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + rc = card->fnc.sign (card, keyidstr, hashalgo, + pincb, pincb_arg, + indata, indatalen, + outdata, outdatalen); + if (opt.verbose) + log_info ("card operation sign result: %s\n", gpg_strerror (rc)); + return rc; +} + + +/* Create the signature and return the allocated result in OUTDATA. + If a PIN is required the PINCB will be used to ask for the PIN; it + should return the PIN in an allocated buffer and put it into PIN. */ +int +card_decipher (CARD card, const char *keyidstr, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen ) +{ + int rc; + + if (!card || !indata || !indatalen || !outdata || !outdatalen || !pincb) + return gpg_error (GPG_ERR_INV_VALUE); + if (!card->fnc.initialized) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (!card->fnc.decipher) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + rc = card->fnc.decipher (card, keyidstr, + pincb, pincb_arg, + indata, indatalen, + outdata, outdatalen); + if (opt.verbose) + log_info ("card operation decipher result: %s\n", gpg_strerror (rc)); + return rc; +} + diff --git a/scd/command.c b/scd/command.c new file mode 100644 index 000000000..c53af84f9 --- /dev/null +++ b/scd/command.c @@ -0,0 +1,1034 @@ +/* command.c - SCdaemon command handler + * Copyright (C) 2001, 2002, 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 +#include + +#include + +#include "scdaemon.h" +#include "app-common.h" + +/* maximum length aloowed as a PIN; used for INQUIRE NEEDPIN */ +#define MAXLEN_PIN 100 + +#define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t)) + +/* Data used to associate an Assuan context with local server data */ +struct server_local_s { + ASSUAN_CONTEXT assuan_ctx; +}; + + +/* Check whether the option NAME appears in LINE */ +static int +has_option (const char *line, const char *name) +{ + const char *s; + int n = strlen (name); + + s = strstr (line, name); + return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n))); +} + + + + +/* Note, that this reset_notify is also used for cleanup purposes. */ +static void +reset_notify (ASSUAN_CONTEXT ctx) +{ + CTRL ctrl = assuan_get_pointer (ctx); + + if (ctrl->card_ctx) + { + card_close (ctrl->card_ctx); + ctrl->card_ctx = NULL; + xfree (ctrl->in_data.value); + ctrl->in_data.value = NULL; + } + if (ctrl->app_ctx) + { + /* FIXME: close the application. */ + xfree (ctrl->app_ctx); + ctrl->app_ctx = NULL; + } +} + + +static int +option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value) +{ + return 0; +} + + +/* If the card has not yet been opened, do it. Note that this + function returns an Assuan error, so don't map the error a second + time */ +static AssuanError +open_card (CTRL ctrl) +{ + if (ctrl->app_ctx) + return 0; /* Already initialized for one specific application. */ + if (ctrl->card_ctx) + return 0; /* Already initialized using a card context. */ + + ctrl->app_ctx = select_application (); + if (!ctrl->app_ctx) + { /* No application found - fall back to old mode. */ + int rc = card_open (&ctrl->card_ctx); + if (rc) + return map_to_assuan_status (rc); + } + return 0; +} + + +/* Do the percent and plus/space unescaping in place and return tghe + length of the valid buffer. */ +static size_t +percent_plus_unescape (unsigned char *string) +{ + unsigned char *p = string; + size_t n = 0; + + while (*string) + { + if (*string == '%' && string[1] && string[2]) + { + string++; + *p++ = xtoi_2 (string); + n++; + string+= 2; + } + else if (*string == '+') + { + *p++ = ' '; + n++; + string++; + } + else + { + *p++ = *string++; + n++; + } + } + + return n; +} + + + +/* SERIALNO + + Return the serial number of the card using a status reponse. This + functon should be used to check for the presence of a card. + + This function is special in that it can be used to reset the card. + Most other functions will return an error when a card change has + been detected and the use of this function is therefore required. + + Background: We want to keep the client clear of handling card + changes between operations; i.e. the client can assume that all + operations are done on the same card unless he calls this function. + */ +static int +cmd_serialno (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int rc = 0; + char *serial_and_stamp; + char *serial; + time_t stamp; + + if ((rc = open_card (ctrl))) + return rc; + + if (ctrl->app_ctx) + rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp); + else + rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp); + if (rc) + return map_to_assuan_status (rc); + rc = asprintf (&serial_and_stamp, "%s %lu", serial, (unsigned long)stamp); + xfree (serial); + if (rc < 0) + return ASSUAN_Out_Of_Core; + rc = 0; + assuan_write_status (ctx, "SERIALNO", serial_and_stamp); + free (serial_and_stamp); + return 0; +} + + + + +/* LEARN [--force] + + Learn all useful information of the currently inserted card. When + used without the force options, the command might do an INQUIRE + like this: + + INQUIRE KNOWNCARDP + + The client should just send an "END" if the processing should go on + or a "CANCEL" to force the function to terminate with a Cancel + error message. The response of this command is a list of status + lines formatted as this: + + S APPTYPE + + This returns the type of the application, currently the strings: + + P15 = PKCS-15 structure used + DINSIG = DIN SIG + OPENPGP = OpenPGP card + + are implemented. These strings are aliases for the AID + + S KEYPAIRINFO + + If there is no certificate yet stored on the card a single "X" is + returned as the keygrip. In addition to the keypair info, information + about all certificates stored on the card is also returned: + + S CERTINFO + + Where CERTTYPE is a number indicating the type of certificate: + 0 := Unknown + 100 := Regular X.509 cert + 101 := Trusted X.509 cert + 102 := Useful X.509 cert + + For certain cards, more information will be returned: + + S KEY-FPR + + For OpenPGP cards this returns the stored fingerprints of the + keys. This can be used check whether a key is available on the + card. NO may be 1, 2 or 3. + + S CA-FPR + + Similar to above, these are the fingerprints of keys assumed to be + ultimately trusted. + + S DISP-NAME + + The name of the card holder as stored on the card; percent + aescaping takes place, spaces are encoded as '+' + + S PUBKEY-URL + + The URL to be used for locating the entire public key. + +*/ +static int +cmd_learn (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int rc = 0; + int idx; + + if ((rc = open_card (ctrl))) + return rc; + + /* Unless the force option is used we try a shortcut by identifying + the card using a serial number and inquiring the client with + that. The client may choose to cancel the operation if he already + knows about this card */ + { + char *serial_and_stamp; + char *serial; + time_t stamp; + + if (ctrl->app_ctx) + rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp); + else + rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp); + if (rc) + return map_to_assuan_status (rc); + rc = asprintf (&serial_and_stamp, "%s %lu", serial, (unsigned long)stamp); + xfree (serial); + if (rc < 0) + return ASSUAN_Out_Of_Core; + rc = 0; + assuan_write_status (ctx, "SERIALNO", serial_and_stamp); + + if (!has_option (line, "--force")) + { + char *command; + + rc = asprintf (&command, "KNOWNCARDP %s", serial_and_stamp); + if (rc < 0) + { + free (serial_and_stamp); + return ASSUAN_Out_Of_Core; + } + rc = 0; + rc = assuan_inquire (ctx, command, NULL, NULL, 0); + free (command); /* (must use standard free here) */ + if (rc) + { + if (rc != ASSUAN_Canceled) + log_error ("inquire KNOWNCARDP failed: %s\n", + assuan_strerror (rc)); + free (serial_and_stamp); + return rc; + } + /* not canceled, so we have to proceeed */ + } + free (serial_and_stamp); + } + + /* Return information about the certificates. */ + if (ctrl->app_ctx) + rc = -1; /* This information is not yet available for applications. */ + for (idx=0; !rc; idx++) + { + char *certid; + int certtype; + + rc = card_enum_certs (ctrl->card_ctx, idx, &certid, &certtype); + if (!rc) + { + char *buf; + + buf = xtrymalloc (40 + 1 + strlen (certid) + 1); + if (!buf) + rc = out_of_core (); + else + { + sprintf (buf, "%d %s", certtype, certid); + assuan_write_status (ctx, "CERTINFO", buf); + xfree (buf); + } + } + xfree (certid); + } + if (rc == -1) + rc = 0; + + + /* Return information about the keys. */ + if (ctrl->app_ctx) + rc = -1; /* This information is not yet available for applications. */ + for (idx=0; !rc; idx++) + { + unsigned char keygrip[20]; + char *keyid; + int no_cert = 0; + + rc = card_enum_keypairs (ctrl->card_ctx, idx, keygrip, &keyid); + if (gpg_err_code (rc) == GPG_ERR_MISSING_CERT && keyid) + { + /* this does happen with an incomplete personalized + card; i.e. during the time we have stored the key on the + card but not stored the certificate; probably becuase it + has not yet been received back from the CA. Note that we + must release KEYID in this case. */ + rc = 0; + no_cert = 1; + } + if (!rc) + { + char *buf, *p; + + buf = p = xtrymalloc (40 + 1 + strlen (keyid) + 1); + if (!buf) + rc = out_of_core (); + else + { + int i; + + if (no_cert) + *p++ = 'X'; + else + { + for (i=0; i < 20; i++, p += 2) + sprintf (p, "%02X", keygrip[i]); + } + *p++ = ' '; + strcpy (p, keyid); + assuan_write_status (ctx, "KEYPAIRINFO", buf); + xfree (buf); + } + } + xfree (keyid); + } + if (rc == -1) + rc = 0; + + if (!rc && ctrl->app_ctx) + rc = app_write_learn_status (ctrl->app_ctx, ctrl); + + + return map_to_assuan_status (rc); +} + + + +/* READCERT + + */ +static int +cmd_readcert (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int rc; + unsigned char *cert; + size_t ncert; + + if ((rc = open_card (ctrl))) + return rc; + + if (ctrl->app_ctx) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + + rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert); + if (rc) + { + log_error ("card_read_cert failed: %s\n", gpg_strerror (rc)); + } + if (!rc) + { + rc = assuan_send_data (ctx, cert, ncert); + xfree (cert); + if (rc) + return rc; + } + + return map_to_assuan_status (rc); +} + + +/* READKEY + + Return the public key for the given cert or key ID as an standard + S-Expression. */ +static int +cmd_readkey (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int rc; + unsigned char *cert = NULL; + size_t ncert, n; + KsbaCert kc = NULL; + KsbaSexp p; + + if ((rc = open_card (ctrl))) + return rc; + + if (ctrl->app_ctx) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + + rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert); + if (rc) + { + log_error ("card_read_cert failed: %s\n", gpg_strerror (rc)); + goto leave; + } + + kc = ksba_cert_new (); + if (!kc) + { + rc = out_of_core (); + xfree (cert); + goto leave; + } + rc = ksba_cert_init_from_mem (kc, cert, ncert); + if (rc) + { + log_error ("failed to parse the certificate: %s\n", ksba_strerror (rc)); + rc = map_ksba_err (rc); + goto leave; + } + + p = ksba_cert_get_public_key (kc); + if (!p) + { + rc = gpg_error (GPG_ERR_NO_PUBKEY); + goto leave; + } + + n = gcry_sexp_canon_len (p, 0, NULL, NULL); + rc = assuan_send_data (ctx, p, n); + rc = map_assuan_err (rc); + xfree (p); + + + leave: + ksba_cert_release (kc); + xfree (cert); + return map_to_assuan_status (rc); +} + + + + +/* SETDATA + + The client should use this command to tell us the data he want to + sign. */ +static int +cmd_setdata (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int n; + char *p; + unsigned char *buf; + + /* parse the hexstring */ + for (p=line,n=0; hexdigitp (p); p++, n++) + ; + if (*p) + return set_error (Parameter_Error, "invalid hexstring"); + if (!n) + return set_error (Parameter_Error, "no data given"); + if ((n&1)) + return set_error (Parameter_Error, "odd number of digits"); + n /= 2; + buf = xtrymalloc (n); + if (!buf) + return ASSUAN_Out_Of_Core; + + ctrl->in_data.value = buf; + ctrl->in_data.valuelen = n; + for (p=line, n=0; n < ctrl->in_data.valuelen; p += 2, n++) + buf[n] = xtoi_2 (p); + return 0; +} + + + +static int +pin_cb (void *opaque, const char *info, char **retstr) +{ + ASSUAN_CONTEXT ctx = opaque; + char *command; + int rc; + char *value; + size_t valuelen; + + *retstr = NULL; + log_debug ("asking for PIN '%s'\n", info); + + rc = asprintf (&command, "NEEDPIN %s", info); + if (rc < 0) + return out_of_core (); + + /* FIXME: Write an inquire function which returns the result in + secure memory */ + rc = assuan_inquire (ctx, command, &value, &valuelen, MAXLEN_PIN); + free (command); + if (rc) + return map_assuan_err (rc); + + if (!valuelen || value[valuelen-1]) + { + /* We require that the returned value is an UTF-8 string */ + xfree (value); + return gpg_error (GPG_ERR_INV_RESPONSE); + } + *retstr = value; + return 0; +} + + +/* PKSIGN + + */ +static int +cmd_pksign (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int rc; + unsigned char *outdata; + size_t outdatalen; + char *keyidstr; + + if ((rc = open_card (ctrl))) + return rc; + + /* We have to use a copy of the key ID because the function may use + the pin_cb which in turn uses the assuan line buffer and thus + overwriting the original line with the keyid */ + keyidstr = strdup (line); + if (!keyidstr) + return ASSUAN_Out_Of_Core; + + if (ctrl->app_ctx) + rc = app_sign (ctrl->app_ctx, + keyidstr, GCRY_MD_SHA1, + pin_cb, ctx, + ctrl->in_data.value, ctrl->in_data.valuelen, + &outdata, &outdatalen); + else + rc = card_sign (ctrl->card_ctx, + keyidstr, GCRY_MD_SHA1, + pin_cb, ctx, + ctrl->in_data.value, ctrl->in_data.valuelen, + &outdata, &outdatalen); + free (keyidstr); + if (rc) + { + log_error ("card_sign failed: %s\n", gpg_strerror (rc)); + } + else + { + rc = assuan_send_data (ctx, outdata, outdatalen); + xfree (outdata); + if (rc) + return rc; /* that is already an assuan error code */ + } + + return map_to_assuan_status (rc); +} + +/* PKAUTH + + */ +static int +cmd_pkauth (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int rc; + unsigned char *outdata; + size_t outdatalen; + char *keyidstr; + + if ((rc = open_card (ctrl))) + return rc; + + if (!ctrl->app_ctx) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + + /* We have to use a copy of the key ID because the function may use + the pin_cb which in turn uses the assuan line buffer and thus + overwriting the original line with the keyid */ + keyidstr = strdup (line); + if (!keyidstr) + return ASSUAN_Out_Of_Core; + + rc = app_auth (ctrl->app_ctx, + keyidstr, + pin_cb, ctx, + ctrl->in_data.value, ctrl->in_data.valuelen, + &outdata, &outdatalen); + free (keyidstr); + if (rc) + { + log_error ("app_auth_sign failed: %s\n", gpg_strerror (rc)); + } + else + { + rc = assuan_send_data (ctx, outdata, outdatalen); + xfree (outdata); + if (rc) + return rc; /* that is already an assuan error code */ + } + + return map_to_assuan_status (rc); +} + +/* PKDECRYPT + + */ +static int +cmd_pkdecrypt (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int rc; + unsigned char *outdata; + size_t outdatalen; + char *keyidstr; + + if ((rc = open_card (ctrl))) + return rc; + + keyidstr = strdup (line); + if (!keyidstr) + return ASSUAN_Out_Of_Core; + if (ctrl->app_ctx) + rc = app_decipher (ctrl->app_ctx, + keyidstr, + pin_cb, ctx, + ctrl->in_data.value, ctrl->in_data.valuelen, + &outdata, &outdatalen); + else + rc = card_decipher (ctrl->card_ctx, + keyidstr, + pin_cb, ctx, + ctrl->in_data.value, ctrl->in_data.valuelen, + &outdata, &outdatalen); + free (keyidstr); + if (rc) + { + log_error ("card_create_signature failed: %s\n", gpg_strerror (rc)); + } + else + { + rc = assuan_send_data (ctx, outdata, outdatalen); + xfree (outdata); + if (rc) + return rc; /* that is already an assuan error code */ + } + + return map_to_assuan_status (rc); +} + + +/* SETATTR + + This command is used to store data on a a smartcard. The allowed + names and values are depend on the currently selected smartcard + application. NAME and VALUE must be percent and '+' escaped. + + However, the curent implementation assumes that Name is not escaped; + this works as long as noone uses arbitrary escaping. + + A PIN will be requested for most NAMEs. See the corresponding + setattr function of the actually used application (app-*.c) for + details. */ +static int +cmd_setattr (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int rc; + char *keyword; + int keywordlen; + size_t nbytes; + + if ((rc = open_card (ctrl))) + return rc; + + keyword = line; + for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) + ; + if (*line) + *line++ = 0; + while (spacep (line)) + line++; + nbytes = percent_plus_unescape (line); + + rc = app_setattr (ctrl->app_ctx, keyword, pin_cb, ctx, line, nbytes); + + return map_to_assuan_status (rc); +} + +/* GENKEY [--force] + + Generate a key on-card identified by NO, which is application + specific. Return values are application specific. For OpenPGP + cards 2 status lines are returned: + + S KEY-FPR + S KEY-CREATED-AT + S KEY-DATA [p|n] + + + --force is required to overwriet an already existing key. The + KEY-CREATED-AT is required for further processing because it is + part of the hashed key material for the fingerprint. + + The public part of the key can also later be retrieved using the + READKEY command. + + */ +static int +cmd_genkey (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int rc; + char *keyno; + int force = has_option (line, "--force"); + + /* Skip over options. */ + while ( *line == '-' && line[1] == '-' ) + { + while (!spacep (line)) + line++; + while (spacep (line)) + line++; + } + if (!*line) + return set_error (Parameter_Error, "no key number given"); + keyno = line; + while (!spacep (line)) + line++; + *line = 0; + + if ((rc = open_card (ctrl))) + return rc; + + if (!ctrl->app_ctx) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + + rc = app_genkey (ctrl->app_ctx, ctrl, keyno, force? 1:0, pin_cb, ctx); + + return map_to_assuan_status (rc); +} + + +/* RANDOM + + Get NBYTES of random from the card and send them back as data. +*/ +static int +cmd_random (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int rc; + size_t nbytes; + unsigned char *buffer; + + if (!*line) + return set_error (Parameter_Error, "number of requested bytes missing"); + nbytes = strtoul (line, NULL, 0); + + if ((rc = open_card (ctrl))) + return rc; + + if (!ctrl->app_ctx) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + + buffer = xtrymalloc (nbytes); + if (!buffer) + return ASSUAN_Out_Of_Core; + + rc = app_get_challenge (ctrl->app_ctx, nbytes, buffer); + if (!rc) + { + rc = assuan_send_data (ctx, buffer, nbytes); + xfree (buffer); + return rc; /* that is already an assuan error code */ + } + xfree (buffer); + + return map_to_assuan_status (rc); +} + + +/* PASSWD [--reset] + + Change the PIN or reset thye retry counter of the card holder + verfication vector CHVNO. */ +static int +cmd_passwd (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int rc; + char *chvnostr; + int reset_mode = has_option (line, "--reset"); + + /* Skip over options. */ + while (*line == '-' && line[1] == '-') + { + while (!spacep (line)) + line++; + while (spacep (line)) + line++; + } + if (!*line) + return set_error (Parameter_Error, "no CHV number given"); + chvnostr = line; + while (!spacep (line)) + line++; + *line = 0; + + if ((rc = open_card (ctrl))) + return rc; + + if (!ctrl->app_ctx) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + + rc = app_change_pin (ctrl->app_ctx, ctrl, chvnostr, reset_mode, pin_cb, ctx +); + if (rc) + log_error ("command passwd failed: %s\n", gpg_strerror (rc)); + return map_to_assuan_status (rc); +} + + + + +/* Tell the assuan library about our commands */ +static int +register_commands (ASSUAN_CONTEXT ctx) +{ + static struct { + const char *name; + int (*handler)(ASSUAN_CONTEXT, char *line); + } table[] = { + { "SERIALNO", cmd_serialno }, + { "LEARN", cmd_learn }, + { "READCERT", cmd_readcert }, + { "READKEY", cmd_readkey }, + { "SETDATA", cmd_setdata }, + { "PKSIGN", cmd_pksign }, + { "PKAUTH", cmd_pkauth }, + { "PKDECRYPT", cmd_pkdecrypt }, + { "INPUT", NULL }, + { "OUTPUT", NULL }, + { "SETATTR", cmd_setattr }, + { "GENKEY", cmd_genkey }, + { "RANDOM", cmd_random }, + { "PASSWD", cmd_passwd }, + { NULL } + }; + int i, rc; + + for (i=0; table[i].name; i++) + { + rc = assuan_register_command (ctx, table[i].name, table[i].handler); + if (rc) + return rc; + } + assuan_set_hello_line (ctx, "GNU Privacy Guard's Smartcard server ready"); + + assuan_register_reset_notify (ctx, reset_notify); + assuan_register_option_handler (ctx, option_handler); + return 0; +} + + +/* Startup the server. If LISTEN_FD is given as -1, this is simple + piper server, otherwise it is a regular server */ +void +scd_command_handler (int listen_fd) +{ + int rc; + ASSUAN_CONTEXT ctx; + struct server_control_s ctrl; + + memset (&ctrl, 0, sizeof ctrl); + scd_init_default_ctrl (&ctrl); + + if (listen_fd == -1) + { + int filedes[2]; + + filedes[0] = 0; + filedes[1] = 1; + rc = assuan_init_pipe_server (&ctx, filedes); + } + else + { + rc = assuan_init_socket_server (&ctx, listen_fd); + } + if (rc) + { + log_error ("failed to initialize the server: %s\n", + assuan_strerror(rc)); + scd_exit (2); + } + rc = register_commands (ctx); + if (rc) + { + log_error ("failed to register commands with Assuan: %s\n", + assuan_strerror(rc)); + scd_exit (2); + } + assuan_set_pointer (ctx, &ctrl); + ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local); + ctrl.server_local->assuan_ctx = ctx; + + if (DBG_ASSUAN) + assuan_set_log_stream (ctx, log_get_stream ()); + + for (;;) + { + rc = assuan_accept (ctx); + if (rc == -1) + { + break; + } + else if (rc) + { + log_info ("Assuan accept problem: %s\n", assuan_strerror (rc)); + break; + } + + rc = assuan_process (ctx); + if (rc) + { + log_info ("Assuan processing failed: %s\n", assuan_strerror (rc)); + continue; + } + } + reset_notify (ctx); /* used for cleanup */ + + assuan_deinit_server (ctx); +} + + +/* Send a line with status information via assuan and escape all given + buffers. The variable elements are pairs of (char *, size_t), + terminated with a (NULL, 0). */ +void +send_status_info (CTRL ctrl, const char *keyword, ...) +{ + va_list arg_ptr; + const unsigned char *value; + size_t valuelen; + char buf[950], *p; + size_t n; + ASSUAN_CONTEXT ctx = ctrl->server_local->assuan_ctx; + + va_start (arg_ptr, keyword); + + p = buf; + n = 0; + while ( (value = va_arg (arg_ptr, const unsigned char *)) ) + { + valuelen = va_arg (arg_ptr, size_t); + if (!valuelen) + continue; /* empty buffer */ + if (n) + { + *p++ = ' '; + n++; + } + for ( ; valuelen && n < DIM (buf)-2; n++, valuelen--, value++) + { + if (*value < ' ' || *value == '+') + { + sprintf (p, "%%%02X", *value); + p += 3; + } + else if (*value == ' ') + *p++ = '+'; + else + *p++ = *value; + } + } + *p = 0; + assuan_write_status (ctx, keyword, buf); + + va_end (arg_ptr); +} + diff --git a/scd/iso7816.c b/scd/iso7816.c new file mode 100644 index 000000000..8903d8a5c --- /dev/null +++ b/scd/iso7816.c @@ -0,0 +1,371 @@ +/* iso7816.c - ISO 7816 commands + * 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 "iso7816.h" +#include "apdu.h" + +#define CMD_SELECT_FILE 0xA4 +#define CMD_VERIFY 0x20 +#define CMD_CHANGE_REFERENCE_DATA 0x24 +#define CMD_RESET_RETRY_COUNTER 0x2C +#define CMD_GET_DATA 0xCA +#define CMD_PUT_DATA 0xDA +#define CMD_PSO 0x2A +#define CMD_INTERNAL_AUTHENTICATE 0x88 +#define CMD_GENERATE_KEYPAIR 0x47 +#define CMD_GET_CHALLENGE 0x84 + +static gpg_error_t +map_sw (int sw) +{ + gpg_err_code_t ec; + + switch (sw) + { + case SW_EEPROM_FAILURE: ec = GPG_ERR_HARDWARE; break; + case SW_WRONG_LENGTH: ec = GPG_ERR_INV_VALUE; break; + case SW_CHV_WRONG: ec = GPG_ERR_BAD_PIN; break; + case SW_CHV_BLOCKED: ec = GPG_ERR_PIN_BLOCKED; break; + case SW_USE_CONDITIONS: ec = GPG_ERR_USE_CONDITIONS; break; + case SW_NOT_SUPPORTED: ec = GPG_ERR_NOT_SUPPORTED; break; + case SW_BAD_PARAMETER: ec = GPG_ERR_INV_VALUE; break; + case SW_REF_NOT_FOUND: ec = GPG_ERR_NO_OBJ; break; + case SW_BAD_P0_P1: ec = GPG_ERR_INV_VALUE; break; + case SW_INS_NOT_SUP: ec = GPG_ERR_CARD; break; + case SW_CLA_NOT_SUP: ec = GPG_ERR_CARD; break; + case SW_SUCCESS: ec = 0; break; + + case SW_HOST_OUT_OF_CORE: ec = GPG_ERR_ENOMEM; break; + case SW_HOST_INV_VALUE: ec = GPG_ERR_INV_VALUE; break; + case SW_HOST_INCOMPLETE_CARD_RESPONSE: ec = GPG_ERR_CARD; break; + default: + if ((sw & 0x010000)) + ec = GPG_ERR_GENERAL; /* Should not happen. */ + else if ((sw & 0xff00) == SW_MORE_DATA) + ec = 0; /* This should actually never been seen here. */ + else + ec = GPG_ERR_CARD; + } + return gpg_error (ec); +} + +/* This function is specialized version of the SELECT FILE command. + SLOT is the card and reader as created for example by + apdu_open_reader (), AID is a buffer of size AIDLEN holding the + requested application ID. The function can't be used to enumerate + AIDs and won't return the AID on success. The return value is 0 + for okay or GNUPG error code. Note that ISO error codes are + internally mapped. */ +gpg_error_t +iso7816_select_application (int slot, const char *aid, size_t aidlen) +{ + int sw; + + sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, 0, aidlen, aid); + return map_sw (sw); +} + + +/* Perform a VERIFY command on SLOT using the card holder verification + vector CHVNO with a CHV of lenght CHVLEN. Returns 0 on success. */ +gpg_error_t +iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen) +{ + int sw; + + sw = apdu_send_simple (slot, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv); + return map_sw (sw); +} + +/* Perform a CHANGE_REFERENCE_DATA command on SLOT for the card holder + verification vector CHVNO. If the OLDCHV is NULL (and OLDCHVLEN + 0), a "change reference data" is done, otherwise an "exchange + reference data". The new reference data is expected in NEWCHV of + length NEWCHVLEN. */ +gpg_error_t +iso7816_change_reference_data (int slot, int chvno, + const char *oldchv, size_t oldchvlen, + const char *newchv, size_t newchvlen) +{ + int sw; + char *buf; + + if ((!oldchv && oldchvlen) + || (oldchv && !oldchvlen) + || !newchv || !newchvlen ) + return gpg_error (GPG_ERR_INV_VALUE); + + buf = xtrymalloc (oldchvlen + newchvlen); + if (!buf) + return out_of_core (); + if (oldchvlen) + memcpy (buf, oldchv, oldchvlen); + memcpy (buf+oldchvlen, newchv, newchvlen); + + sw = apdu_send_simple (slot, 0x00, CMD_CHANGE_REFERENCE_DATA, + oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf); + xfree (buf); + return map_sw (sw); + +} + +gpg_error_t +iso7816_reset_retry_counter (int slot, int chvno, + const char *newchv, size_t newchvlen) +{ + int sw; + + if (!newchv || !newchvlen ) + return gpg_error (GPG_ERR_INV_VALUE); + + sw = apdu_send_simple (slot, 0x00, CMD_RESET_RETRY_COUNTER, + 2, chvno, newchvlen, newchv); + return map_sw (sw); +} + + +/* Perform a GET DATA command requesting TAG and storing the result in + a newly allocated buffer at the address passed by RESULT. Return + the length of this data at the address of RESULTLEN. */ +gpg_error_t +iso7816_get_data (int slot, int tag, + unsigned char **result, size_t *resultlen) +{ + int sw; + + if (!result || !resultlen) + return gpg_error (GPG_ERR_INV_VALUE); + *result = NULL; + *resultlen = 0; + + sw = apdu_send (slot, 0x00, CMD_GET_DATA, + ((tag >> 8) & 0xff), (tag & 0xff), -1, NULL, + result, resultlen); + if (sw != SW_SUCCESS) + { + /* Make sure that pending buffers are released. */ + xfree (*result); + *result = NULL; + *resultlen = 0; + return map_sw (sw); + } + + return 0; +} + + +/* Perform a PUT DATA command on card in SLOT. Write DATA of length + DATALEN to TAG. */ +gpg_error_t +iso7816_put_data (int slot, int tag, + const unsigned char *data, size_t datalen) +{ + int sw; + + sw = apdu_send_simple (slot, 0x00, CMD_PUT_DATA, + ((tag >> 8) & 0xff), (tag & 0xff), + datalen, data); + return map_sw (sw); +} + + +/* Perform the security operation COMPUTE DIGITAL SIGANTURE. On + success 0 is returned and the data is availavle in a newly + allocated buffer stored at RESULT with its length stored at + RESULTLEN. */ +gpg_error_t +iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen, + unsigned char **result, size_t *resultlen) +{ + int sw; + + if (!data || !datalen || !result || !resultlen) + return gpg_error (GPG_ERR_INV_VALUE); + *result = NULL; + *resultlen = 0; + + sw = apdu_send (slot, 0x00, CMD_PSO, 0x9E, 0x9A, datalen, data, + result, resultlen); + if (sw != SW_SUCCESS) + { + /* Make sure that pending buffers are released. */ + xfree (*result); + *result = NULL; + *resultlen = 0; + return map_sw (sw); + } + + return 0; +} + + +/* Perform the security operation DECIPHER. On + success 0 is returned and the plaintext is available in a newly + allocated buffer stored at RESULT with its length stored at + RESULTLEN. */ +gpg_error_t +iso7816_decipher (int slot, const unsigned char *data, size_t datalen, + unsigned char **result, size_t *resultlen) +{ + int sw; + unsigned char *buf; + + if (!data || !datalen || !result || !resultlen) + return gpg_error (GPG_ERR_INV_VALUE); + *result = NULL; + *resultlen = 0; + + /* We need to prepend the padding indicator. */ + buf = xtrymalloc (datalen + 1); + if (!buf) + return out_of_core (); + *buf = 0; /* Padding indicator. */ + memcpy (buf+1, data, datalen); + sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, datalen+1, buf, + result, resultlen); + xfree (buf); + if (sw != SW_SUCCESS) + { + /* Make sure that pending buffers are released. */ + xfree (*result); + *result = NULL; + *resultlen = 0; + return map_sw (sw); + } + + return 0; +} + + +gpg_error_t +iso7816_internal_authenticate (int slot, + const unsigned char *data, size_t datalen, + unsigned char **result, size_t *resultlen) +{ + int sw; + + if (!data || !datalen || !result || !resultlen) + return gpg_error (GPG_ERR_INV_VALUE); + *result = NULL; + *resultlen = 0; + + sw = apdu_send (slot, 0x00, CMD_INTERNAL_AUTHENTICATE, 0, 0, + datalen, data, result, resultlen); + if (sw != SW_SUCCESS) + { + /* Make sure that pending buffers are released. */ + xfree (*result); + *result = NULL; + *resultlen = 0; + return map_sw (sw); + } + + return 0; +} + + +static gpg_error_t +generate_keypair (int slot, int readonly, + const unsigned char *data, size_t datalen, + unsigned char **result, size_t *resultlen) +{ + int sw; + + if (!data || !datalen || !result || !resultlen) + return gpg_error (GPG_ERR_INV_VALUE); + *result = NULL; + *resultlen = 0; + + sw = apdu_send (slot, 0x00, CMD_GENERATE_KEYPAIR, readonly? 0x81:0x80, 0, + datalen, data, result, resultlen); + if (sw != SW_SUCCESS) + { + /* Make sure that pending buffers are released. */ + xfree (*result); + *result = NULL; + *resultlen = 0; + return map_sw (sw); + } + + return 0; +} + + +gpg_error_t +iso7816_generate_keypair (int slot, + const unsigned char *data, size_t datalen, + unsigned char **result, size_t *resultlen) +{ + return generate_keypair (slot, 0, data, datalen, result, resultlen); +} + + +gpg_error_t +iso7816_read_public_key (int slot, + const unsigned char *data, size_t datalen, + unsigned char **result, size_t *resultlen) +{ + return generate_keypair (slot, 1, data, datalen, result, resultlen); +} + + + +gpg_error_t +iso7816_get_challenge (int slot, int length, unsigned char *buffer) +{ + int sw; + unsigned char *result; + size_t resultlen, n; + + if (!buffer || length < 1) + return gpg_error (GPG_ERR_INV_VALUE); + + do + { + result = NULL; + n = length > 254? 254 : length; + sw = apdu_send_le (slot, 0x00, CMD_GET_CHALLENGE, 0, 0, -1, NULL, + n, + &result, &resultlen); + if (sw != SW_SUCCESS) + { + /* Make sure that pending buffers are released. */ + xfree (result); + return map_sw (sw); + } + if (resultlen > n) + resultlen = n; + memcpy (buffer, result, resultlen); + buffer += resultlen; + length -= resultlen; + xfree (result); + } + while (length > 0); + + return 0; +} diff --git a/scd/iso7816.h b/scd/iso7816.h new file mode 100644 index 000000000..d7e77a101 --- /dev/null +++ b/scd/iso7816.h @@ -0,0 +1,56 @@ +/* iso7816.h - ISO 7816 commands + * 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 ISO7816_H +#define ISO7816_H + +gpg_error_t iso7816_select_application (int slot, + const char *aid, size_t aidlen); +gpg_error_t iso7816_verify (int slot, + int chvno, const char *chv, size_t chvlen); +gpg_error_t iso7816_change_reference_data (int slot, int chvno, + const char *oldchv, size_t oldchvlen, + const char *newchv, size_t newchvlen); +gpg_error_t iso7816_reset_retry_counter (int slot, int chvno, + const char *newchv, size_t newchvlen); +gpg_error_t iso7816_get_data (int slot, int tag, + unsigned char **result, size_t *resultlen); +gpg_error_t iso7816_put_data (int slot, int tag, + const unsigned char *data, size_t datalen); +gpg_error_t iso7816_compute_ds (int slot, + const unsigned char *data, size_t datalen, + unsigned char **result, size_t *resultlen); +gpg_error_t iso7816_decipher (int slot, + const unsigned char *data, size_t datalen, + unsigned char **result, size_t *resultlen); +gpg_error_t iso7816_internal_authenticate (int slot, + const unsigned char *data, size_t datalen, + unsigned char **result, size_t *resultlen); +gpg_error_t iso7816_generate_keypair (int slot, + const unsigned char *data, size_t datalen, + unsigned char **result, size_t *resultlen); +gpg_error_t iso7816_read_public_key (int slot, + const unsigned char *data, size_t datalen, + unsigned char **result, size_t *resultlen); +gpg_error_t iso7816_get_challenge (int slot, + int length, unsigned char *buffer); + + +#endif /*ISO7816_H*/ diff --git a/scd/sc-copykeys.c b/scd/sc-copykeys.c new file mode 100644 index 000000000..9caf39a8a --- /dev/null +++ b/scd/sc-copykeys.c @@ -0,0 +1,731 @@ +/* sc-copykeys.c - A tool to store keys on a smartcard. + * 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 +#include + +#define JNLIB_NEED_LOG_LOGV +#include "scdaemon.h" +#include + +#include "../common/ttyio.h" +#include "../common/simple-pwquery.h" +#include "apdu.h" /* for open_reader */ +#include "atr.h" +#include "app-common.h" + +#define _(a) (a) + + +enum cmd_and_opt_values +{ oVerbose = 'v', + oReaderPort = 500, + oDebug, + oDebugAll, + +aTest }; + + +static ARGPARSE_OPTS opts[] = { + + { 301, NULL, 0, "@Options:\n " }, + + { oVerbose, "verbose", 0, "verbose" }, + { oReaderPort, "reader-port", 1, "|N|connect to reader at port N"}, + { oDebug, "debug" ,4|16, "set debugging flags"}, + { oDebugAll, "debug-all" ,0, "enable full debugging"}, + {0} +}; + + +static void copykeys (APP app, const char *fname); + + +static const char * +my_strusage (int level) +{ + const char *p; + switch (level) + { + case 11: p = "sc-copykeys (GnuPG)"; + break; + case 13: p = VERSION; break; + case 17: p = PRINTABLE_OS_NAME; break; + case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n"); + break; + case 1: + case 40: p = _("Usage: sc-copykeys [options] (-h for help)\n"); + break; + case 41: p = _("Syntax: sc-copykeys [options] " + "file-with-key\n" + "Copy keys to a smartcards\n"); + break; + + default: p = NULL; + } + return p; +} + +/* Used by gcry for logging */ +static void +my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr) +{ + /* translate the log levels */ + switch (level) + { + case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break; + case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break; + case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break; + case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break; + case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break; + case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break; + case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break; + default: level = JNLIB_LOG_ERROR; break; + } + log_logv (level, fmt, arg_ptr); +} + + +int +main (int argc, char **argv ) +{ + ARGPARSE_ARGS pargs; + int slot, rc; + int reader_port = 32768; /* First USB reader. */ + struct app_ctx_s appbuf; + + memset (&appbuf, 0, sizeof appbuf); + + set_strusage (my_strusage); + gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); + log_set_prefix ("sc-copykeys", 1); + + /* check that the libraries are suitable. Do it here because + the option parsing may need services of the library */ + if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) ) + { + log_fatal( _("libgcrypt is too old (need %s, have %s)\n"), + NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) ); + } + + gcry_set_log_handler (my_gcry_logger, NULL); + gcry_control (GCRYCTL_DISABLE_SECMEM, 0); /* FIXME - we want to use it */ + /* FIXME? gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);*/ + + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags= 1; /* do not remove the args */ + while (arg_parse (&pargs, opts) ) + { + switch (pargs.r_opt) + { + case oVerbose: opt.verbose++; break; + case oDebug: opt.debug |= pargs.r.ret_ulong; break; + case oDebugAll: opt.debug = ~0; break; + default : pargs.err = 2; break; + } + } + if (log_get_errorcount(0)) + exit(2); + + if (argc != 1) + usage (1); + + slot = apdu_open_reader (reader_port); + if (slot == -1) + exit (1); + + /* FIXME: Use select_application. */ + appbuf.slot = slot; + rc = app_select_openpgp (&appbuf, &appbuf.serialno, &appbuf.serialnolen); + if (rc) + { + log_error ("selecting openpgp failed: %s\n", gpg_strerror (rc)); + exit (1); + } + appbuf.initialized = 1; + log_info ("openpgp application selected\n"); + + copykeys (&appbuf, *argv); + + + return 0; +} + + + +void +send_status_info (CTRL ctrl, const char *keyword, ...) +{ + /* DUMMY */ +} + + + +static char * +read_file (const char *fname, size_t *r_length) +{ + FILE *fp; + struct stat st; + char *buf; + size_t buflen; + + fp = fname? fopen (fname, "rb") : stdin; + if (!fp) + { + log_error ("can't open `%s': %s\n", + fname? fname: "[stdin]", strerror (errno)); + return NULL; + } + + if (fstat (fileno(fp), &st)) + { + log_error ("can't stat `%s': %s\n", + fname? fname: "[stdin]", strerror (errno)); + if (fname) + fclose (fp); + return NULL; + } + + buflen = st.st_size; + buf = xmalloc (buflen+1); + if (fread (buf, buflen, 1, fp) != 1) + { + log_error ("error reading `%s': %s\n", + fname? fname: "[stdin]", strerror (errno)); + if (fname) + fclose (fp); + xfree (buf); + return NULL; + } + if (fname) + fclose (fp); + + *r_length = buflen; + return buf; +} + + +static gcry_sexp_t +read_key (const char *fname) +{ + char *buf; + size_t buflen; + gcry_sexp_t private; + int rc; + + buf = read_file (fname, &buflen); + if (!buf) + return NULL; + + rc = gcry_sexp_new (&private, buf, buflen, 1); + if (rc) + { + log_error ("gcry_sexp_new failed: %s\n", gpg_strerror (rc)); + return NULL; + } + xfree (buf); + + return private; +} + + + +static gcry_mpi_t * +sexp_to_kparms (gcry_sexp_t sexp, unsigned long *created) +{ + gcry_sexp_t list, l2; + const char *name; + const char *s; + size_t n; + int i, idx; + const char *elems; + gcry_mpi_t *array; + + *created = 0; + list = gcry_sexp_find_token (sexp, "private-key", 0 ); + if(!list) + return NULL; + + /* quick hack to get the creation time. */ + l2 = gcry_sexp_find_token (list, "created", 0); + if (l2 && (name = gcry_sexp_nth_data (l2, 1, &n))) + { + char *tmp = xmalloc (n+1); + memcpy (tmp, name, n); + tmp[n] = 0; + *created = strtoul (tmp, NULL, 10); + xfree (tmp); + } + gcry_sexp_release (l2); + l2 = gcry_sexp_cadr (list); + gcry_sexp_release (list); + list = l2; + name = gcry_sexp_nth_data (list, 0, &n); + if(!name || n != 3 || memcmp (name, "rsa", 3)) + { + gcry_sexp_release (list); + return NULL; + } + + /* Parameter names used with RSA. */ + elems = "nedpqu"; + array = xcalloc (strlen(elems) + 1, sizeof *array); + for (idx=0, s=elems; *s; s++, idx++ ) + { + l2 = gcry_sexp_find_token (list, s, 1); + if (!l2) + { + for (i=0; i 32) + { + log_error ("public exponent too large (more than 32 bits)\n"); + goto failure; + } + nbits = gcry_mpi_get_nbits (rsa_p); + if (nbits != 512) + { + log_error ("length of first RSA prime is not 512\n"); + goto failure; + } + nbits = gcry_mpi_get_nbits (rsa_q); + if (nbits != 512) + { + log_error ("length of second RSA prime is not 512\n"); + goto failure; + } + + nbits = gcry_mpi_get_nbits (rsa_n); + if (nbits != 1024) + { + log_error ("length of RSA modulus is not 1024\n"); + goto failure; + } + + keyno = query_card (app); + if (!keyno) + goto failure; + + /* Build the private key template as described in section 4.3.3.6 of + the specs. + 0xC0 public exponent + 0xC1 prime p + 0xC2 prime q */ + template = tp = xmalloc (1+2 + 1+1+4 + 1+1+64 + 1+1+64); + *tp++ = 0xC0; + *tp++ = 4; + rc = gcry_mpi_print (GCRYMPI_FMT_USG, tp, 4, &n, rsa_e); + if (rc) + { + log_error ("mpi_print failed: %s\n", gpg_strerror (rc)); + goto failure; + } + assert (n <= 4); + memcpy (e, tp, n); + elen = n; + if (n != 4) + { + memmove (tp+4-n, tp, 4-n); + memset (tp, 0, 4-n); + } + tp += 4; + + *tp++ = 0xC1; + *tp++ = 64; + rc = gcry_mpi_print (GCRYMPI_FMT_USG, tp, 64, &n, rsa_p); + if (rc) + { + log_error ("mpi_print failed: %s\n", gpg_strerror (rc)); + goto failure; + } + assert (n == 64); + tp += 64; + + *tp++ = 0xC2; + *tp++ = 64; + rc = gcry_mpi_print (GCRYMPI_FMT_USG, tp, 64, &n, rsa_q); + if (rc) + { + log_error ("mpi_print failed: %s\n", gpg_strerror (rc)); + goto failure; + } + assert (n == 64); + tp += 64; + assert (tp - template == 138); + + /* (we need the modulus to calculate the fingerprint) */ + rc = gcry_mpi_print (GCRYMPI_FMT_USG, m, 128, &n, rsa_n); + if (rc) + { + log_error ("mpi_print failed: %s\n", gpg_strerror (rc)); + goto failure; + } + assert (n == 128); + mlen = 128; + + + rc = app_openpgp_storekey (app, keyno, + template, tp - template, + created_at, + m, mlen, + e, elen, + pincb, NULL); + + if (rc) + { + log_error ("error storing key: %s\n", gpg_strerror (rc)); + goto failure; + } + log_info ("key successfully stored\n"); + { + unsigned char *mm, *ee; + size_t mmlen, eelen; + int i; + + rc = app_openpgp_readkey (app, keyno, &mm, &mmlen, &ee, &eelen); + if (rc) + { + log_error ("error reading key back: %s\n", gpg_strerror (rc)); + goto failure; + } + + /* Strip leading zeroes. */ + for (i=0; i < mmlen && !mm[i]; i++) + ; + mmlen -= i; + memmove (mm, mm+i, mmlen); + for (i=0; i < eelen && !ee[i]; i++) + ; + eelen -= i; + memmove (ee, ee+i, eelen); + + if (eelen != elen || mmlen != mlen) + { + log_error ("key parameter length mismatch (n=%u/%u, e=%u/%u)\n", + (unsigned int)mlen, (unsigned int)mmlen, + (unsigned int)elen, (unsigned int)eelen); + xfree (mm); + xfree (ee); + goto failure; + } + + if (memcmp (m, mm, mlen)) + { + log_error ("key parameter n mismatch\n"); + log_printhex ("original n: ", m, mlen); + log_printhex (" copied n: ", mm, mlen); + xfree (mm); + xfree (ee); + goto failure; + } + if (memcmp (e, ee, elen)) + { + log_error ("key parameter e mismatch\n"); + log_printhex ("original e: ", e, elen); + log_printhex (" copied e: ", ee, elen); + xfree (mm); + xfree (ee); + goto failure; + } + xfree (mm); + xfree (ee); + } + + + gcry_mpi_release (rsa_e); + gcry_mpi_release (rsa_p); + gcry_mpi_release (rsa_q); + gcry_mpi_release (rsa_n); + return; + + failure: + gcry_mpi_release (rsa_e); + gcry_mpi_release (rsa_p); + gcry_mpi_release (rsa_q); + gcry_mpi_release (rsa_n); + exit (1); +} + + diff --git a/scd/sc-investigate.c b/scd/sc-investigate.c new file mode 100644 index 000000000..e8f0eb83c --- /dev/null +++ b/scd/sc-investigate.c @@ -0,0 +1,209 @@ +/* sc-investigate.c - A tool to look around on smartcards. + * Copyright (C) 2003 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include + +#include +#include +#include +#include + +#define JNLIB_NEED_LOG_LOGV +#include "scdaemon.h" +#include + +#include "apdu.h" /* for open_reader */ +#include "atr.h" +#include "app-common.h" + +#define _(a) (a) + + +enum cmd_and_opt_values +{ oVerbose = 'v', + oReaderPort = 500, + oDebug, + oDebugAll, + + oGenRandom, + +aTest }; + + +static ARGPARSE_OPTS opts[] = { + + { 301, NULL, 0, "@Options:\n " }, + + { oVerbose, "verbose", 0, "verbose" }, + { oReaderPort, "reader-port", 1, "|N|connect to reader at port N"}, + { oDebug, "debug" ,4|16, "set debugging flags"}, + { oDebugAll, "debug-all" ,0, "enable full debugging"}, + { oGenRandom, "gen-random", 4, "|N|generate N bytes of random"}, + {0} +}; + +static const char * +my_strusage (int level) +{ + const char *p; + switch (level) + { + case 11: p = "sc-investigate (GnuPG)"; + break; + case 13: p = VERSION; break; + case 17: p = PRINTABLE_OS_NAME; break; + case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n"); + break; + case 1: + case 40: p = _("Usage: sc-investigate [options] (-h for help)\n"); + break; + case 41: p = _("Syntax: sc-investigate [options] [args]]\n" + "Have a look at smartcards\n"); + break; + + default: p = NULL; + } + return p; +} + +/* Used by gcry for logging */ +static void +my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr) +{ + /* translate the log levels */ + switch (level) + { + case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break; + case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break; + case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break; + case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break; + case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break; + case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break; + case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break; + default: level = JNLIB_LOG_ERROR; break; + } + log_logv (level, fmt, arg_ptr); +} + + +int +main (int argc, char **argv ) +{ + ARGPARSE_ARGS pargs; + int slot, rc; + int reader_port = 32768; /* First USB reader. */ + struct app_ctx_s appbuf; + unsigned long gen_random = 0; + + memset (&appbuf, 0, sizeof appbuf); + + set_strusage (my_strusage); + gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); + log_set_prefix ("sc-investigate", 1); + + /* check that the libraries are suitable. Do it here because + the option parsing may need services of the library */ + if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) ) + { + log_fatal( _("libgcrypt is too old (need %s, have %s)\n"), + NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) ); + } + + gcry_set_log_handler (my_gcry_logger, NULL); + /* FIXME? gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);*/ + + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags= 1; /* do not remove the args */ + while (arg_parse (&pargs, opts) ) + { + switch (pargs.r_opt) + { + case oVerbose: opt.verbose++; break; + case oDebug: opt.debug |= pargs.r.ret_ulong; break; + case oDebugAll: opt.debug = ~0; break; + case oGenRandom: gen_random = pargs.r.ret_ulong; break; + default : pargs.err = 2; break; + } + } + if (log_get_errorcount(0)) + exit(2); + + if (opt.verbose < 2) + opt.verbose = 2; /* hack to let select_openpgp print some info. */ + + if (argc) + usage (1); + + slot = apdu_open_reader (reader_port); + if (slot == -1) + exit (1); + + if (!gen_random) + { + rc = atr_dump (slot, stdout); + if (rc) + log_error ("can't dump ATR: %s\n", gpg_strerror (rc)); + } + + appbuf.slot = slot; + rc = app_select_openpgp (&appbuf, NULL, NULL); + if (rc) + log_error ("selecting openpgp failed: %s\n", gpg_strerror (rc)); + else + { + appbuf.initialized = 1; + log_info ("openpgp application selected\n"); + + if (gen_random) + { + size_t nbytes; + unsigned char *buffer; + + buffer = xmalloc (4096); + do + { + nbytes = gen_random > 4096? 4096 : gen_random; + rc = app_get_challenge (&appbuf, nbytes, buffer); + if (rc) + log_error ("app_get_challenge failed: %s\n",gpg_strerror (rc)); + else + { + if (fwrite (buffer, nbytes, 1, stdout) != 1) + log_error ("writing to stdout failed: %s\n", + strerror (errno)); + gen_random -= nbytes; + } + } + while (gen_random && !log_get_errorcount (0)); + xfree (buffer); + } + } + + return log_get_errorcount (0)? 2:0; +} + + + +void +send_status_info (CTRL ctrl, const char *keyword, ...) +{ + /* DUMMY */ +} diff --git a/scd/scdaemon.c b/scd/scdaemon.c new file mode 100644 index 000000000..8e0ef37c9 --- /dev/null +++ b/scd/scdaemon.c @@ -0,0 +1,638 @@ +/* scdaemon.c - The GnuPG Smartcard Daemon + * Copyright (C) 2001, 2002 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 +#include +#include +#include +#include +#include +#include +#include + +#define JNLIB_NEED_LOG_LOGV +#include "scdaemon.h" +#include +#include + +#include /* malloc hooks */ + +#include "i18n.h" +#include "sysutils.h" + + + +enum cmd_and_opt_values +{ aNull = 0, + oCsh = 'c', + oQuiet = 'q', + oSh = 's', + oVerbose = 'v', + + oNoVerbose = 500, + oOptions, + oDebug, + oDebugAll, + oDebugWait, + oDebugSC, + oNoGreeting, + oNoOptions, + oHomedir, + oNoDetach, + oNoGrab, + oLogFile, + oServer, + oDaemon, + oBatch, + oReaderPort, + +aTest }; + + + +static ARGPARSE_OPTS opts[] = { + + { 301, NULL, 0, N_("@Options:\n ") }, + + { oServer, "server", 0, N_("run in server mode (foreground)") }, + { oDaemon, "daemon", 0, N_("run in daemon mode (background)") }, + { oVerbose, "verbose", 0, N_("verbose") }, + { oQuiet, "quiet", 0, N_("be somewhat more quiet") }, + { oSh, "sh", 0, N_("sh-style command output") }, + { oCsh, "csh", 0, N_("csh-style command output") }, + { oOptions, "options" , 2, N_("read options from file")}, + { oDebug, "debug" ,4|16, N_("set debugging flags")}, + { oDebugAll, "debug-all" ,0, N_("enable full debugging")}, + { oDebugWait,"debug-wait",1, "@"}, + { 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")}, + + {0} +}; + + +static volatile int caught_fatal_sig = 0; + +/* It is possible that we are currently running under setuid permissions */ +static int maybe_setuid = 1; + +/* Name of the communication socket */ +static char socket_name[128]; + +static const char * +my_strusage (int level) +{ + const char *p; + switch (level) + { + case 11: p = "scdaemon (GnuPG)"; + break; + case 13: p = VERSION; break; + case 17: p = PRINTABLE_OS_NAME; break; + case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n"); + break; + case 1: + case 40: p = _("Usage: scdaemon [options] (-h for help)"); + break; + case 41: p = _("Syntax: scdaemon [options] [command [args]]\n" + "Smartcard daemon for GnuPG\n"); + break; + + default: p = NULL; + } + return p; +} + + + +static void +i18n_init (void) +{ +#ifdef USE_SIMPLE_GETTEXT + set_gettext_file( PACKAGE ); +#else +#ifdef ENABLE_NLS + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); +#endif +#endif +} + + + +/* Used by gcry for logging */ +static void +my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr) +{ + /* translate the log levels */ + switch (level) + { + case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break; + case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break; + case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break; + case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break; + case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break; + case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break; + case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break; + default: level = JNLIB_LOG_ERROR; break; + } + log_logv (level, fmt, arg_ptr); +} + + +static void +cleanup (void) +{ + if (*socket_name) + { + char *p; + + remove (socket_name); + p = strrchr (socket_name, '/'); + if (p) + { + *p = 0; + rmdir (socket_name); + *p = '/'; + } + *socket_name = 0; + } +} + + +static RETSIGTYPE +cleanup_sh (int sig) +{ + if (caught_fatal_sig) + raise (sig); + caught_fatal_sig = 1; + + /* gcry_control( GCRYCTL_TERM_SECMEM );*/ + cleanup (); + +#ifndef HAVE_DOSISH_SYSTEM + { /* reset action to default action and raise signal again */ + struct sigaction nact; + nact.sa_handler = SIG_DFL; + sigemptyset( &nact.sa_mask ); + nact.sa_flags = 0; + sigaction( sig, &nact, NULL); + } +#endif + raise( sig ); +} + +int +main (int argc, char **argv ) +{ + ARGPARSE_ARGS pargs; + int orig_argc; + int may_coredump; + char **orig_argv; + FILE *configfp = NULL; + char *configname = NULL; + const char *shell; + unsigned configlineno; + int parse_debug = 0; + int default_config =1; + int greeting = 0; + int nogreeting = 0; + int pipe_server = 0; + int is_daemon = 0; + int nodetach = 0; + int csh_style = 0; + char *logfile = NULL; + int debug_wait = 0; + int reader_port = 32768; /* First USB reader. */ + + set_strusage (my_strusage); + gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); + /* Please note that we may running SUID(ROOT), so be very CAREFUL + when adding any stuff between here and the call to INIT_SECMEM() + somewhere after the option parsing */ + log_set_prefix ("scdaemon", 1|4); + i18n_init (); + + /* check that the libraries are suitable. Do it here because + the option parsing may need services of the library */ + if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) ) + { + log_fatal( _("libgcrypt is too old (need %s, have %s)\n"), + NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) ); + } + + ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free); + assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free); + gcry_set_log_handler (my_gcry_logger, NULL); + gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); + + may_coredump = disable_core_dumps (); + + shell = getenv ("SHELL"); + if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") ) + csh_style = 1; + + /* FIXME: Using this homedir option does only make sense when not + running as a system service. We might want to check for this by + looking at the uid or ebtter use an explict option for this */ + opt.homedir = getenv("GNUPGHOME"); + if (!opt.homedir || !*opt.homedir) + opt.homedir = GNUPG_DEFAULT_HOMEDIR; + + /* check whether we have a config file on the commandline */ + orig_argc = argc; + orig_argv = argv; + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags= 1|(1<<6); /* do not remove the args, ignore version */ + while (arg_parse( &pargs, opts)) + { + if (pargs.r_opt == oDebug || pargs.r_opt == oDebugAll) + parse_debug++; + else if (pargs.r_opt == oOptions) + { /* yes there is one, so we do not try the default one, but + read the option file when it is encountered at the + commandline */ + default_config = 0; + } + else if (pargs.r_opt == oNoOptions) + default_config = 0; /* --no-options */ + else if (pargs.r_opt == oHomedir) + opt.homedir = pargs.r.ret_str; + } + + /* initialize the secure memory. */ + gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); + maybe_setuid = 0; + + /* + Now we are working under our real uid + */ + + + if (default_config) + configname = make_filename (opt.homedir, "scdaemon.conf", NULL ); + + argc = orig_argc; + argv = orig_argv; + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags= 1; /* do not remove the args */ + next_pass: + if (configname) + { + configlineno = 0; + configfp = fopen (configname, "r"); + if (!configfp) + { + if (default_config) + { + if( parse_debug ) + log_info (_("NOTE: no default option file `%s'\n"), + configname ); + } + else + { + log_error (_("option file `%s': %s\n"), + configname, strerror(errno) ); + exit(2); + } + xfree (configname); + configname = NULL; + } + if (parse_debug && configname ) + log_info (_("reading options from `%s'\n"), configname ); + default_config = 0; + } + + while (optfile_parse( configfp, configname, &configlineno, &pargs, opts) ) + { + switch (pargs.r_opt) + { + case oQuiet: opt.quiet = 1; break; + case oVerbose: opt.verbose++; break; + case oBatch: opt.batch=1; break; + + case oDebug: opt.debug |= pargs.r.ret_ulong; break; + case oDebugAll: opt.debug = ~0; break; + case oDebugWait: debug_wait = pargs.r.ret_int; break; + case oDebugSC: opt.debug_sc = pargs.r.ret_int; break; + + case oOptions: + /* config files may not be nested (silently ignore them) */ + if (!configfp) + { + xfree(configname); + configname = xstrdup(pargs.r.ret_str); + goto next_pass; + } + break; + case oNoGreeting: nogreeting = 1; break; + case oNoVerbose: opt.verbose = 0; break; + case oNoOptions: break; /* no-options */ + case oHomedir: opt.homedir = pargs.r.ret_str; break; + case oNoDetach: nodetach = 1; break; + case oLogFile: logfile = pargs.r.ret_str; break; + case oCsh: csh_style = 1; break; + case oSh: csh_style = 0; break; + case oServer: pipe_server = 1; break; + case oDaemon: is_daemon = 1; break; + + case oReaderPort: reader_port = pargs.r.ret_int; break; + + default : pargs.err = configfp? 1:2; break; + } + } + if (configfp) + { + fclose( configfp ); + configfp = NULL; + xfree(configname); + configname = NULL; + goto next_pass; + } + xfree (configname); + configname = NULL; + if (log_get_errorcount(0)) + exit(2); + if (nogreeting ) + greeting = 0; + + if (greeting) + { + fprintf (stderr, "%s %s; %s\n", + strusage(11), strusage(13), strusage(14) ); + fprintf (stderr, "%s\n", strusage(15) ); + } +#ifdef IS_DEVELOPMENT_VERSION + log_info ("NOTE: this is a development version!\n"); +#endif + + + if (atexit (cleanup)) + { + log_error ("atexit failed\n"); + cleanup (); + exit (1); + } + + + if (debug_wait && pipe_server) + { + log_debug ("waiting for debugger - my pid is %u .....\n", + (unsigned int)getpid()); + sleep (debug_wait); + log_debug ("... okay\n"); + } + + /* now start with logging to a file if this is desired */ + if (logfile) + { + log_set_file (logfile); + log_set_prefix (NULL, 1|2|4); + } + + + if (pipe_server) + { /* this is the simple pipe based server */ + scd_command_handler (-1); + } + else if (!is_daemon) + { + log_info (_("please use the option `--daemon'" + " to run the program in the background\n")); + } + else + { /* regular server mode */ + int fd; + pid_t pid; + int i; + int len; + struct sockaddr_un serv_addr; + char *p; + + /* fixme: if there is already a running gpg-agent we should + share the same directory - and vice versa */ + *socket_name = 0; + snprintf (socket_name, DIM(socket_name)-1, + "/tmp/gpg-XXXXXX/S.scdaemon"); + socket_name[DIM(socket_name)-1] = 0; + p = strrchr (socket_name, '/'); + if (!p) + BUG (); + *p = 0;; + if (!mkdtemp(socket_name)) + { + log_error ("can't create directory `%s': %s\n", + socket_name, strerror(errno) ); + exit (1); + } + *p = '/'; + + if (strchr (socket_name, ':') ) + { + log_error ("colons are not allowed in the socket name\n"); + exit (1); + } + if (strlen (socket_name)+1 >= sizeof serv_addr.sun_path ) + { + log_error ("name of socket to long\n"); + exit (1); + } + + + fd = socket (AF_UNIX, SOCK_STREAM, 0); + if (fd == -1) + { + log_error ("can't create socket: %s\n", strerror(errno) ); + exit (1); + } + + memset (&serv_addr, 0, sizeof serv_addr); + serv_addr.sun_family = AF_UNIX; + strcpy (serv_addr.sun_path, socket_name); + len = (offsetof (struct sockaddr_un, sun_path) + + strlen(serv_addr.sun_path) + 1); + + if (bind (fd, (struct sockaddr*)&serv_addr, len) == -1) + { + log_error ("error binding socket to `%s': %s\n", + serv_addr.sun_path, strerror (errno) ); + close (fd); + exit (1); + } + + if (listen (fd, 5 ) == -1) + { + log_error ("listen() failed: %s\n", strerror (errno)); + close (fd); + exit (1); + } + + if (opt.verbose) + log_info ("listening on socket `%s'\n", socket_name ); + + + fflush (NULL); + pid = fork (); + if (pid == (pid_t)-1) + { + log_fatal ("fork failed: %s\n", strerror (errno) ); + exit (1); + } + else if (pid) + { /* we are the parent */ + char *infostr; + + close (fd); + + /* create the info string: :: */ + if (asprintf (&infostr, "SCDAEMON_INFO=%s:%lu:1", + socket_name, (ulong)pid ) < 0) + { + log_error ("out of core\n"); + kill (pid, SIGTERM); + exit (1); + } + *socket_name = 0; /* don't let cleanup() remove the socket - + the child should do this from now on */ + if (argc) + { /* run the program given on the commandline */ + if (putenv (infostr)) + { + log_error ("failed to set environment: %s\n", + strerror (errno) ); + kill (pid, SIGTERM ); + exit (1); + } + execvp (argv[0], argv); + log_error ("failed to run the command: %s\n", strerror (errno)); + kill (pid, SIGTERM); + exit (1); + } + else + { + /* print the environment string, so that the caller can use + shell's eval to set it */ + if (csh_style) + { + *strchr (infostr, '=') = ' '; + printf ( "setenv %s\n", infostr); + } + else + { + printf ( "%s; export SCDAEMON_INFO;\n", infostr); + } + free (infostr); + exit (0); + } + /* NOTREACHED */ + } /* end parent */ + + /* this is the child */ + + /* detach from tty and put process into a new session */ + if (!nodetach ) + { /* close stdin, stdout and stderr unless it is the log stream */ + for (i=0; i <= 2; i++) + { + if ( log_get_fd () != i) + close (i); + } + if (setsid() == -1) + { + log_error ("setsid() failed: %s\n", strerror(errno) ); + cleanup (); + exit (1); + } + } + + /* setup signals */ + { + struct sigaction oact, nact; + + nact.sa_handler = cleanup_sh; + sigemptyset (&nact.sa_mask); + nact.sa_flags = 0; + + sigaction (SIGHUP, NULL, &oact); + if (oact.sa_handler != SIG_IGN) + sigaction (SIGHUP, &nact, NULL); + sigaction( SIGTERM, NULL, &oact ); + if (oact.sa_handler != SIG_IGN) + sigaction (SIGTERM, &nact, NULL); + nact.sa_handler = SIG_IGN; + sigaction (SIGPIPE, &nact, NULL); + sigaction (SIGINT, &nact, NULL); + } + + if (chdir("/")) + { + log_error ("chdir to / failed: %s\n", strerror (errno)); + exit (1); + } + + scd_command_handler (fd); + + close (fd); + } + + return 0; +} + +void +scd_exit (int rc) +{ + #if 0 +#warning no update_random_seed_file + update_random_seed_file(); + #endif +#if 0 + /* at this time a bit annoying */ + if (opt.debug & DBG_MEMSTAT_VALUE) + { + gcry_control( GCRYCTL_DUMP_MEMORY_STATS ); + gcry_control( GCRYCTL_DUMP_RANDOM_STATS ); + } + if (opt.debug) + gcry_control (GCRYCTL_DUMP_SECMEM_STATS ); +#endif + gcry_control (GCRYCTL_TERM_SECMEM ); + rc = rc? rc : log_get_errorcount(0)? 2 : 0; + exit (rc); +} + + +void +scd_init_default_ctrl (CTRL ctrl) +{ + +} + diff --git a/scd/scdaemon.h b/scd/scdaemon.h new file mode 100644 index 000000000..b21e19f8c --- /dev/null +++ b/scd/scdaemon.h @@ -0,0 +1,127 @@ +/* scdaemon.h - Global definitions for the SCdaemon + * Copyright (C) 2001, 2002, 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 SCDAEMON_H +#define SCDAEMON_H + +#ifdef GPG_ERR_SOURCE_DEFAULT +#error GPG_ERR_SOURCE_DEFAULT already defined +#endif +#define GPG_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_SCD +#include +#include + +#include +#include +#include "../common/util.h" +#include "../common/errors.h" + +/* Convenience funcion to be used instead of returning the old + GNUPG_Out_Of_Core. */ +static __inline__ gpg_error_t +out_of_core (void) +{ + return gpg_error (gpg_err_code_from_errno (errno)); +} + + +#define MAX_DIGEST_LEN 24 + +/* A large struct name "opt" to keep global flags */ +struct { + unsigned int debug; /* debug flags (DBG_foo_VALUE) */ + int debug_sc; /* OpenSC debug level */ + int verbose; /* verbosity level */ + int quiet; /* be as quiet as possible */ + int dry_run; /* don't change any persistent data */ + int batch; /* batch mode */ + const char *homedir; /* configuration directory name */ +} opt; + + +#define DBG_COMMAND_VALUE 1 /* debug commands i/o */ +#define DBG_MPI_VALUE 2 /* debug mpi details */ +#define DBG_CRYPTO_VALUE 4 /* debug low level crypto */ +#define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */ +#define DBG_CACHE_VALUE 64 /* debug the caching */ +#define DBG_MEMSTAT_VALUE 128 /* show memory statistics */ +#define DBG_HASHING_VALUE 512 /* debug hashing operations */ +#define DBG_ASSUAN_VALUE 1024 +#define DBG_CARD_IO_VALUE 2048 + +#define DBG_COMMAND (opt.debug & DBG_COMMAND_VALUE) +#define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE) +#define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE) +#define DBG_CACHE (opt.debug & DBG_CACHE_VALUE) +#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE) +#define DBG_ASSUAN (opt.debug & DBG_ASSUAN_VALUE) +#define DBG_CARD_IO (opt.debug & DBG_CARD_IO_VALUE) + +struct server_local_s; +struct card_ctx_s; +struct app_ctx_s; + +struct server_control_s { + struct server_local_s *server_local; + struct card_ctx_s *card_ctx; + struct app_ctx_s *app_ctx; + struct { + unsigned char *value; + int valuelen; + } in_data; /* helper to store the value we are going to sign */ + +}; + +typedef struct server_control_s *CTRL; +typedef struct card_ctx_s *CARD; +typedef struct app_ctx_s *APP; + +/*-- scdaemon.c --*/ +void scd_exit (int rc); +void scd_init_default_ctrl (CTRL ctrl); + +/*-- command.c --*/ +void scd_command_handler (int); +void send_status_info (CTRL ctrl, const char *keyword, ...); + +/*-- card.c --*/ +int card_open (CARD *rcard); +void card_close (CARD card); +int card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp); +int card_enum_keypairs (CARD card, int idx, + unsigned char *keygrip, + char **keyid); +int card_enum_certs (CARD card, int idx, char **certid, int *certtype); +int card_read_cert (CARD card, const char *certidstr, + unsigned char **cert, size_t *ncert); +int card_sign (CARD card, + const char *keyidstr, int hashalgo, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen ); +int card_decipher (CARD card, const char *keyidstr, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen); + + +#endif /*SCDAEMON_H*/ diff --git a/sm/ChangeLog b/sm/ChangeLog new file mode 100644 index 000000000..59a6b3271 --- /dev/null +++ b/sm/ChangeLog @@ -0,0 +1,816 @@ +2003-07-31 Werner Koch + + * Makefile.am (gpgsm_LDADD): Added INTLLIBS. + +2003-07-29 Werner Koch + + * gpgsm.c (main): Add secmem features and set the random seed file. + (gpgsm_exit): Update the random seed file and enable debug output. + +2003-07-27 Werner Koch + + Adjusted for gcry_mpi_print and gcry_mpi_scan API change. + +2003-06-24 Werner Koch + + * server.c (gpgsm_status_with_err_code): New. + * verify.c (gpgsm_verify): Use it here instead of the old + tokenizing version. + + * verify.c (strtimestamp): Renamed to strtimestamp_r + + Adjusted for changes in the libgcrypt API. Some more fixes for the + libgpg-error stuff. + +2003-06-04 Werner Koch + + * call-agent.c (init_membuf,put_membuf,get_membuf): Removed. + Include new membuf header and changed used type. + + Renamed error codes from INVALID to INV and removed _ERROR suffixes. + +2003-06-03 Werner Koch + + Changed all error codes in all files to the new libgpg-error scheme. + + * gpgsm.h: Include gpg-error.h . + * Makefile.am: Link with libgpg-error. + +2003-04-29 Werner Koch + + * Makefile.am: Use libassuan. Don't override LDFLAGS anymore. + * server.c (register_commands): Adjust for new Assuan semantics. + +2002-12-03 Werner Koch + + * call-agent.c (gpgsm_agent_passwd): New. + * gpgsm.c (main): New command --passwd and --call-protect-tool + (run_protect_tool): New. + +2002-11-25 Werner Koch + + * verify.c (gpgsm_verify): Handle content-type attribute. + +2002-11-13 Werner Koch + + * call-agent.c (start_agent): Try to use $GPG_TTY instead of + ttyname. Changed ttyname to test stdin becuase it can be assumed + that output redirection is more common that input redirection. + +2002-11-12 Werner Koch + + * gpgsm.c: New command --call-dirmngr. + * call-dirmngr.c (gpgsm_dirmngr_run_command) + (run_command_inq_cb,run_command_cb) + (run_command_status_cb): New. + +2002-11-11 Werner Koch + + * certcheck.c (gpgsm_check_cms_signature): Don't double free + s_sig but free s_pkey at leave. + +2002-11-10 Werner Koch + + * gpgsm.c: Removed duplicate --list-secret-key entry. + +2002-09-19 Werner Koch + + * certcheck.c (gpgsm_check_cert_sig): Add cert hash debugging. + + * certchain.c (find_up): Print info when the cert was not found + by the autorithyKeyIdentifier. + +2002-09-03 Werner Koch + + * gpgsm.c (main): Disable the internal libgcrypt locking. + +2002-08-21 Werner Koch + + * import.c (print_imported_summary): Cleaned up. Print new + not_imported value. + (check_and_store): Update non_imported counter. + (print_import_problem): New. + (check_and_store): Print error status message. + * server.c (get_status_string): Added STATUS_IMPORT_PROBLEM. + +2002-08-20 Werner Koch + + * gpgsm.c (main): Use the log file only in server mode. + + * import.c (print_imported_summary): New. + (check_and_store): Update the counters, take new argument. + (import_one): Factored out core of gpgsm_import. + (gpgsm_import): Print counters. + (gpgsm_import_files): New. + * gpgsm.c (main): Use the new function for import. + +2002-08-19 Werner Koch + + * decrypt.c (gpgsm_decrypt): Return a better error status token. + * verify.c (gpgsm_verify): Don't error on messages with no signing + time or no message digest. This is only the case for messages + without any signed attributes. + +2002-08-16 Werner Koch + + * certpath.c: Renamed to .. + * certchain.c: this. Renamed all all other usages of "path" in the + context of certificates to "chain". + + * call-agent.c (learn_cb): Special treatment when the issuer + certificate is missing. + +2002-08-10 Werner Koch + + * Makefile.am (INCLUDES): Add definition for localedir. + + * keylist.c (list_cert_colon): Print the short fingerprint in the + key ID field. + * fingerprint.c (gpgsm_get_short_fingerprint): New. + * verify.c (gpgsm_verify): Print more verbose info for a good + signature. + +2002-08-09 Werner Koch + + * decrypt.c (prepare_decryption): Hack to detected already + unpkcsedone keys. + + * gpgsm.c (emergency_cleanup): New. + (main): Initialize the signal handler. + + * sign.c (gpgsm_sign): Reset the hash context for subsequent + signers and release it at the end. + +2002-08-05 Werner Koch + + * server.c (cmd_signer): New command "SIGNER" + (register_commands): Register it. + (cmd_sign): Pass the signer list to gpgsm_sign. + * certlist.c (gpgsm_add_to_certlist): Add SECRET argument, check + for secret key if set and changed all callers. + * sign.c (gpgsm_sign): New argument SIGNERLIST and implemt + multiple signers. + * gpgsm.c (main): Support more than one -u. + + * server.c (cmd_recipient): Return reason code 1 for No_Public_Key + which is actually what gets returned from add_to_certlist. + +2002-07-26 Werner Koch + + * certcheck.c (gpgsm_check_cert_sig): Implement proper cleanup. + (gpgsm_check_cms_signature): Ditto. + +2002-07-22 Werner Koch + + * keydb.c (keydb_add_resource): Register a lock file. + (lock_all, unlock_all): Implemented. + + * delete.c: New. + * gpgsm.c: Made --delete-key work. + * server.c (cmd_delkeys): New. + (register_commands): New command DELKEYS. + + * decrypt.c (gpgsm_decrypt): Print a convenience note when RC2 is + used and a STATUS_ERROR with the algorithm oid. + +2002-07-03 Werner Koch + + * server.c (gpgsm_status2): Insert a blank between all optional + arguments when using assuan. + * server.c (cmd_recipient): No more need for extra blank in constants. + * import.c (print_imported_status): Ditto. + * gpgsm.c (main): Ditto. + +2002-07-02 Werner Koch + + * verify.c (gpgsm_verify): Extend the STATUS_BADSIG line with + the fingerprint. + + * certpath.c (check_cert_policy): Don't use log_error to print a + warning. + + * keydb.c (keydb_store_cert): Add optional ar EXISTED and changed + all callers. + * call-agent.c (learn_cb): Print info message only for real imports. + + * import.c (gpgsm_import): Moved duplicated code to ... + (check_and_store): new function. Added magic to import the entire + chain. Print status only for real imports and moved printing code + to .. + (print_imported_status): New. + + * call-dirmngr.c (gpgsm_dirmngr_isvalid): print status of dirmngr + call in very verbose mode. + + * gpgsm.c (main): Use the same error codes for STATUS_INV_RECP as + with the server mode. + +2002-06-29 Werner Koch + + * gpgsm.c: New option --auto-issuer-key-retrieve. + * certpath.c (find_up): Try to retrieve an issuer key from an + external source and from the ephemeral key DB. + (find_up_store_certs_cb): New. + + * keydb.c (keydb_set_ephemeral): Does now return the old + state. Call the backend only when required. + + * call-dirmngr.c (start_dirmngr): Use GNUPG_DEFAULT_DIRMNGR. + (lookup_status_cb): Issue status only when CTRL is not NULL. + (gpgsm_dirmngr_lookup): Document that CTRL is optional. + + * call-agent.c (start_agent): Use GNUPG_DEFAULT_AGENT. + +2002-06-28 Werner Koch + + * server.c (cmd_recipient): Add more reason codes. + +2002-06-27 Werner Koch + + * certpath.c (gpgsm_basic_cert_check): Use + --debug-no-path-validation to also bypass this basic check. + + * gpgsm.c (main): Use GNUPG_DEFAULT_HOMEDIR constant. + + * call-agent.c (start_agent): Create and pass the list of FD to + keep in the child to assuan. + * call-dirmngr.c (start_dirmngr): Ditto. + +2002-06-26 Werner Koch + + * import.c (gpgsm_import): Print an STATUS_IMPORTED. + + * gpgsm.c: --debug-no-path-validation does not take an argument. + +2002-06-25 Werner Koch + + * certdump.c (print_dn_part): Always print a leading slash, + removed NEED_DELIM arg and changed caller. + + * export.c (gpgsm_export): Print LFs to FP and not stdout. + (print_short_info): Ditto. Make use of gpgsm_print_name. + + * server.c (cmd_export): Use output-fd instead of data lines; this + was actually the specified way. + +2002-06-24 Werner Koch + + * gpgsm.c: Removed duped help entry for --list-keys. + + * gpgsm.c, gpgsm.h: New option --debug-no-path-validation. + + * certpath.c (gpgsm_validate_path): Use it here instead of the + debug flag hack. + + * certpath.c (check_cert_policy): Return No_Policy_Match if the + policy file could not be opened. + +2002-06-20 Werner Koch + + * certlist.c (gpgsm_add_to_certlist): Fixed locating of a + certificate with the required key usage. + + * gpgsm.c (main): Fixed a segv when using --outfile without an + argument. + + * keylist.c (print_capabilities): Also check for non-repudiation + and data encipherment. + * certlist.c (cert_usage_p): Test for signing and encryption was + swapped. Add a case for certification usage, handle + non-repudiation and data encipherment. + (gpgsm_cert_use_cert_p): New. + (gpgsm_add_to_certlist): Added a CTRL argument and changed all + callers to pass it. + * certpath.c (gpgsm_validate_path): Use it here to print a status + message. Added a CTRL argument and changed all callers to pass it. + * decrypt.c (gpgsm_decrypt): Print a status message for wrong key + usage. + * verify.c (gpgsm_verify): Ditto. + * keydb.c (classify_user_id): Allow a colon delimited fingerprint. + +2002-06-19 Werner Koch + + * call-agent.c (learn_cb): Use log_info instead of log_error on + successful import. + + * keydb.c (keydb_set_ephemeral): New. + (keydb_store_cert): New are ephemeral, changed all callers. + * keylist.c (list_external_cb): Store cert as ephemeral. + * export.c (gpgsm_export): Kludge to export epehmeral certificates. + + * gpgsm.c (main): New command --list-external-keys. + +2002-06-17 Werner Koch + + * certreqgen.c (read_parameters): Improved error handling. + (gpgsm_genkey): Print error message. + +2002-06-13 Werner Koch + + * gpgsm.c (main): New option --log-file. + +2002-06-12 Werner Koch + + * call-dirmngr.c (lookup_status_cb): New. + (gpgsm_dirmngr_lookup): Use the status CB. Add new arg CTRL and + changed caller to pass it. + + * gpgsm.c (open_fwrite): New. + (main): Allow --output for --verify. + + * sign.c (hash_and_copy_data): New. + (gpgsm_sign): Implemented normal (non-detached) signatures. + * gpgsm.c (main): Ditto. + + * certpath.c (gpgsm_validate_path): Special error handling for + no policy match. + +2002-06-10 Werner Koch + + * server.c (get_status_string): Add STATUS_ERROR. + + * certpath.c (gpgsm_validate_path): Tweaked the error checking to + return error codes in a more sensitive way. + * verify.c (gpgsm_verify): Send status TRUST_NEVER also for a bad + CA certificate and when the certificate has been revoked. Issue + TRUST_FULLY even when the cert has expired. Append an error token + to these status lines. Issue the new generic error status when a + cert was not found and when leaving the function. + +2002-06-04 Werner Koch + + * gpgsm.c (main): New command --list-sigs + * keylist.c (list_cert_std): New. Use it whenever colon mode is + not used. + (list_cert_chain): New. + +2002-05-31 Werner Koch + + * gpgsm.c (main): Don't print the "go ahead" message for an + invalid command. + +2002-05-23 Werner Koch + + * import.c (gpgsm_import): Add error messages. + +2002-05-21 Werner Koch + + * keylist.c (list_internal_keys): Renamed from gpgsm_list_keys. + (list_external_keys): New. + (gpgsm_list_keys): Dispatcher for above. + * call-dirmngr.c (lookup_cb,pattern_from_strlist) + (gpgsm_dirmngr_lookup): New. + * server.c (option_handler): Handle new option --list-mode. + (do_listkeys): Handle options and actually use the mode argument. + (get_status_string): New code TRUNCATED. + + * import.c (gpgsm_import): Try to identify the type of input and + handle certs-only messages. + +2002-05-14 Werner Koch + + * gpgsm.c: New option --faked-system-time + * sign.c (gpgsm_sign): And use it here. + * certpath.c (gpgsm_validate_path): Ditto. + +2002-05-03 Werner Koch + + * certpath.c (gpgsm_validate_path): Added EXPTIME arg and changed + all callers. + * verify.c (gpgsm_verify): Tweaked usage of log_debug and + log_error. Return EXPSIG status and add expiretime to VALIDSIG. + +2002-04-26 Werner Koch + + * gpgsm.h (DBG_AGENT,DBG_AGENT_VALUE): Replaced by DBG_ASSUAN_*. + Changed all users. + + * call-agent.c (start_agent): Be more silent without -v. + * call-dirmngr.c (start_dirmngr): Ditto. + +2002-04-25 Werner Koch + + * call-agent.c (start_agent): Make copies of old locales and check + for setlocale. + +2002-04-25 Marcus Brinkmann + + * call-agent.c (start_agent): Fix error handling logic so the + locale is always correctly reset. + +2002-04-25 Marcus Brinkmann + + * server.c (option_handler): Accept display, ttyname, ttytype, + lc_ctype and lc_messages options. + * gpgsm.c (main): Allocate memory for these options. + * gpgsm.h (struct opt): Make corresponding members non-const. + +2002-04-24 Marcus Brinkmann + + * gpgsm.h (struct opt): New members display, ttyname, ttytype, + lc_ctype, lc_messages. + * gpgsm.c (enum cmd_and_opt_values): New members oDisplay, + oTTYname, oTTYtype, oLCctype, oLCmessages. + (opts): New entries for these options. + (main): Handle these new options. + * call-agent.c (start_agent): Set the various display and tty + parameter after resetting. + +2002-04-18 Werner Koch + + * certreqgen.c (gpgsm_genkey): Write status output on success. + +2002-04-15 Werner Koch + + * gpgsm.c (main): Check ksba version. + + * certpath.c (find_up): New to use the authorithKeyIdentifier. + Use it in all other functions to locate the signing cert.. + +2002-04-11 Werner Koch + + * certlist.c (cert_usable_p): New. + (gpgsm_cert_use_sign_p,gpgsm_cert_use_encrypt_p): New. + (gpgsm_cert_use_verify_p,gpgsm_cert_use_decrypt_p): New. + (gpgsm_add_to_certlist): Check the key usage. + * sign.c (gpgsm_sign): Ditto. + * verify.c (gpgsm_verify): Print a message wehn an unsuitable + certificate was used. + * decrypt.c (gpgsm_decrypt): Ditto + * keylist.c (print_capabilities): Determine values from the cert. + +2002-03-28 Werner Koch + + * keylist.c (list_cert_colon): Fixed listing of crt record; the + issuer is not at the right place. Print a chainingID. + * certpath.c (gpgsm_walk_cert_chain): Be a bit more silent on + common errors. + +2002-03-21 Werner Koch + + * export.c: New. + * gpgsm.c: Add command --export. + * server.c (cmd_export): New. + +2002-03-13 Werner Koch + + * decrypt.c (gpgsm_decrypt): Allow multiple recipients. + +2002-03-12 Werner Koch + + * certpath.c (check_cert_policy): Print the policy list. + + * verify.c (gpgsm_verify): Detect certs-only message. + +2002-03-11 Werner Koch + + * import.c (gpgsm_import): Print a notice about imported certificates + when in verbose mode. + + * gpgsm.c (main): Print INV_RECP status. + * server.c (cmd_recipient): Ditto. + + * server.c (gpgsm_status2): New. Allows for a list of strings. + (gpgsm_status): Divert to gpgsm_status2. + + * encrypt.c (gpgsm_encrypt): Don't use a default key when no + recipients are given. Print a NO_RECP status. + +2002-03-06 Werner Koch + + * server.c (cmd_listkeys, cmd_listsecretkeys): Divert to + (do_listkeys): new. Add pattern parsing. + + * keylist.c (gpgsm_list_keys): Handle selection pattern. + + * gpgsm.c: New command --learn-card + * call-agent.c (learn_cb,gpgsm_agent_learn): New. + + * gpgsm.c (main): Print error messages for non-implemented commands. + + * base64.c (base64_reader_cb): Use case insensitive compare of the + Content-Type string to detect plain base-64. + +2002-03-05 Werner Koch + + * gpgsm.c, gpgsm.h: Add local_user. + * sign.c (gpgsm_get_default_cert): New. + (get_default_signer): Use the new function if local_user is not + set otherwise used that value. + * encrypt.c (get_default_recipient): Removed. + (gpgsm_encrypt): Use gpgsm_get_default_cert. + + * verify.c (gpgsm_verify): Better error text for a bad signature + found by comparing the hashs. + +2002-02-27 Werner Koch + + * call-dirmngr.c, call-agent.c: Add 2 more arguments to all uses + of assuan_transact. + +2002-02-25 Werner Koch + + * server.c (option_handler): Allow to use -2 for "send all certs + except the root cert". + * sign.c (add_certificate_list): Implement it here. + * certpath.c (gpgsm_is_root_cert): New. + +2002-02-19 Werner Koch + + * certpath.c (check_cert_policy): New. + (gpgsm_validate_path): And call it from here. + * gpgsm.c (main): New options --policy-file, + --disable-policy-checks and --enable-policy-checks. + * gpgsm.h (opt): Added policy_file, no_policy_checks. + +2002-02-18 Werner Koch + + * certpath.c (gpgsm_validate_path): Ask the agent to add the + certificate into the trusted list. + * call-agent.c (gpgsm_agent_marktrusted): New. + +2002-02-07 Werner Koch + + * certlist.c (gpgsm_add_to_certlist): Check that the specified + name identifies a certificate unambiguously. + (gpgsm_find_cert): Ditto. + + * server.c (cmd_listkeys): Check that the data stream is available. + (cmd_listsecretkeys): Ditto. + (has_option): New. + (cmd_sign): Fix ambiguousity in option recognition. + + * gpgsm.c (main): Enable --logger-fd. + + * encrypt.c (gpgsm_encrypt): Increased buffer size for better + performance. + + * call-agent.c (gpgsm_agent_pksign): Check the S-Exp received from + the agent. + + * keylist.c (list_cert_colon): Filter out control characters. + +2002-02-06 Werner Koch + + * decrypt.c (gpgsm_decrypt): Bail out after an decryption error. + + * server.c (reset_notify): Close input and output FDs. + (cmd_encrypt,cmd_decrypt,cmd_verify,cmd_sign.cmd_import) + (cmd_genkey): Close the FDs and release the recipient list even in + the error case. + +2002-02-01 Marcus Brinkmann + + * sign.c (gpgsm_sign): Do not release certificate twice. + +2002-01-29 Werner Koch + + * call-agent.c (gpgsm_agent_havekey): New. + * keylist.c (list_cert_colon): New arg HAVE_SECRET, print "crs" + when we know that the secret key is available. + (gpgsm_list_keys): New arg MODE, check whether a secret key is + available. Changed all callers. + * gpgsm.c (main): New command --list-secret-keys. + * server.c (cmd_listsecretkeys): New. + (cmd_listkeys): Return secret keys with "crs" record. + +2002-01-28 Werner Koch + + * certreqgen.c (create_request): Store the email address in the req. + +2002-01-25 Werner Koch + + * gpgsm.c (main): Disable core dumps. + + * sign.c (add_certificate_list): New. + (gpgsm_sign): Add the certificates to the CMS object. + * certpath.c (gpgsm_walk_cert_chain): New. + * gpgsm.h (server_control_s): Add included_certs. + * gpgsm.c: Add option --include-certs. + (gpgsm_init_default_ctrl): New. + (main): Call it. + * server.c (gpgsm_server): Ditto. + (option_handler): Support --include-certs. + +2002-01-23 Werner Koch + + * certpath.c (gpgsm_validate_path): Print the DN of a missing issuer. + * certdump.c (gpgsm_dump_string): New. + (print_dn): Replaced by above. + +2002-01-22 Werner Koch + + * certpath.c (unknown_criticals): New. + (allowed_ca): New. + (gpgsm_validate_path): Check validity, CA attribute, path length + and unknown critical extensions. + +2002-01-21 Werner Koch + + * gpgsm.c: Add option --enable-crl-checks. + + * call-agent.c (start_agent): Implemented socket based access. + * call-dirmngr.c (start_dirmngr): Ditto. + +2002-01-20 Werner Koch + + * server.c (option_handler): New. + (gpgsm_server): Register it with assuan. + +2002-01-19 Werner Koch + + * server.c (gpgsm_server): Use assuan_deinit_server and setup + assuan logging if enabled. + * call-agent.c (inq_ciphertext_cb): Don't show the session key in + an Assuan log file. + + * gpgsm.c (my_strusage): Take bugreport address from configure.ac + +2002-01-15 Werner Koch + + * import.c (gpgsm_import): Just do a basic cert check before + storing it. + * certpath.c (gpgsm_basic_cert_check): New. + + * keydb.c (keydb_store_cert): New. + * import.c (store_cert): Removed and change all caller to use + the new function. + * verify.c (store_cert): Ditto. + + * certlist.c (gpgsm_add_to_certlist): Validate the path + + * certpath.c (gpgsm_validate_path): Check the trust list. + * call-agent.c (gpgsm_agent_istrusted): New. + +2002-01-14 Werner Koch + + * call-dirmngr.c (inq_certificate): Changed for new interface semantic. + * certlist.c (gpgsm_find_cert): New. + +2002-01-13 Werner Koch + + * fingerprint.c (gpgsm_get_certid): Print the serial and not the + hash after the dot. + +2002-01-11 Werner Koch + + * call-dirmngr.c: New. + * certpath.c (gpgsm_validate_path): Check the CRL here. + * fingerprint.c (gpgsm_get_certid): New. + * gpgsm.c: New options --dirmngr-program and --disable-crl-checks. + +2002-01-10 Werner Koch + + * base64.c (gpgsm_create_writer): Allow to set the object name + +2002-01-08 Werner Koch + + * keydb.c (spacep): Removed because it is now in util.c + + * server.c (cmd_genkey): New. + * certreqgen.c: New. The parameter handling code has been taken + from gnupg/g10/keygen.c version 1.0.6. + * call-agent.c (gpgsm_agent_genkey): New. + +2002-01-02 Werner Koch + + * server.c (rc_to_assuan_status): Removed and changed all callers + to use map_to_assuan_status. + +2001-12-20 Werner Koch + + * verify.c (gpgsm_verify): Implemented non-detached signature + verification. Add OUT_FP arg, initialize a writer and changed all + callers. + * server.c (cmd_verify): Pass an out_fp if one has been set. + + * base64.c (base64_reader_cb): Try to detect an S/MIME body part. + + * certdump.c (print_sexp): Renamed to gpgsm_dump_serial, made + global. + (print_time): Renamed to gpgsm_dump_time, made global. + (gpgsm_dump_serial): Take a real S-Expression as argument and + print the first item. + * keylist.c (list_cert_colon): Ditto. + * keydb.c (keydb_search_issuer_sn): Ditto. + * decrypt.c (print_integer_sexp): Removed and made callers + use gpgsm_dump_serial. + * verify.c (print_time): Removed, made callers use gpgsm_dump_time. + +2001-12-19 Marcus Brinkmann + + * call-agent.c (start_agent): Add new argument to assuan_pipe_connect. + +2001-12-18 Werner Koch + + * verify.c (print_integer_sexp): Renamed from print_integer and + print the serial number according to the S-Exp rules. + * decrypt.c (print_integer_sexp): Ditto. + +2001-12-17 Werner Koch + + * keylist.c (list_cert_colon): Changed for new return value of + get_serial. + * keydb.c (keydb_search_issuer_sn): Ditto. + * certcheck.c (gpgsm_check_cert_sig): Likewise for other S-Exp + returingin functions. + * fingerprint.c (gpgsm_get_keygrip): Ditto. + * encrypt.c (encrypt_dek): Ditto + * certcheck.c (gpgsm_check_cms_signature): Ditto + * decrypt.c (prepare_decryption): Ditto. + * call-agent.c (gpgsm_agent_pkdecrypt): Removed arg ciphertextlen, + use KsbaSexp type and calculate the length. + + * certdump.c (print_sexp): Remaned from print_integer, changed caller. + + * Makefile.am: Use the LIBGCRYPT and LIBKSBA variables. + + * fingerprint.c (gpgsm_get_keygrip): Use the new + gcry_pk_get_keygrip to calculate the grip - note the algorithm and + therefore the grip values changed. + +2001-12-15 Werner Koch + + * certcheck.c (gpgsm_check_cms_signature): Removed the faked-key + kludge. + (gpgsm_create_cms_signature): Removed the commented fake key + code. This makes the function pretty simple. + + * gpgsm.c (main): Renamed the default key database to "keyring.kbx". + + * decrypt.c (gpgsm_decrypt): Write STATUS_DECRYPTION_*. + * sign.c (gpgsm_sign): Write a STATUS_SIG_CREATED. + +2001-12-14 Werner Koch + + * keylist.c (list_cert_colon): Kludge to show an email address + encoded in the subject's DN. + + * verify.c (gpgsm_verify): Add hash debug helpers + * sign.c (gpgsm_sign): Ditto. + + * base64.c (base64_reader_cb): Reset the linelen when we need to + skip the line and adjusted test; I somehow forgot about DeMorgan. + + * server.c (cmd_encrypt,cmd_decrypt,cmd_sign,cmd_verify) + (cmd_import): Close the FDs on success. + (close_message_fd): New. + (input_notify): Setting autodetect_encoding to 0 after initializing + it to 0 is pretty pointless. Easy to fix. + + * gpgsm.c (main): New option --debug-wait n, so that it is + possible to attach gdb when used in server mode. + + * sign.c (get_default_signer): Use keydb_classify_name here. + +2001-12-14 Marcus Brinkmann + + * call-agent.c (LINELENGTH): Removed. + (gpgsm_agent_pksign): Use ASSUAN_LINELENGTH, not LINELENGTH. + (gpgsm_agent_pkdecrypt): Likewise. + +2001-12-13 Werner Koch + + * keylist.c (list_cert_colon): Print alternative names of subject + and a few other values. + +2001-12-12 Werner Koch + + * gpgsm.c (main): New options --assume-{armor,base64,binary}. + * base64.c (base64_reader_cb): Fixed non-autodetection mode. + +2001-12-04 Werner Koch + + * call-agent.c (read_from_agent): Check for inquire responses. + (request_reply): Handle them using a new callback arg, changed all + callers. + (gpgsm_agent_pkdecrypt): New. + +2001-11-27 Werner Koch + + * base64.c: New. Changed all other functions to use this instead + of direct creation of ksba_reader/writer. + * gpgsm.c (main): Set ctrl.auto_encoding unless --no-armor is used. + +2001-11-26 Werner Koch + + * gpgsm.c: New option --agent-program + * call-agent.c (start_agent): Allow to override the default path + to the agent. + + * keydb.c (keydb_add_resource): Create keybox + + * keylist.c (gpgsm_list_keys): Fixed non-server keylisting. + + * server.c (rc_to_assuan_status): New. Use it for all commands. + + + Copyright 2001, 2002, 2003 Free Software Foundation, Inc. + + This file is free software; as a special exception the author gives + unlimited permission to copy and/or distribute it, with or without + modifications, as long as this notice is preserved. + + This file is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY, to the extent permitted by law; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/sm/Makefile.am b/sm/Makefile.am new file mode 100644 index 000000000..6345573e5 --- /dev/null +++ b/sm/Makefile.am @@ -0,0 +1,55 @@ +# Copyright (C) 2001, 2002, 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 + +## Process this file with automake to produce Makefile.in + +localedir = $(datadir)/locale +INCLUDES = -I../intl -DLOCALEDIR=\"$(localedir)\" + +bin_PROGRAMS = gpgsm + +AM_CPPFLAGS = -I$(top_srcdir)/common -I$(top_srcdir)/intl \ + $(LIBGCRYPT_CFLAGS) $(LIBASSUAN_CFLAGS) $(KSBA_CFLAGS) + +gpgsm_SOURCES = \ + gpgsm.c gpgsm.h \ + misc.c \ + keydb.c keydb.h \ + server.c \ + call-agent.c \ + call-dirmngr.c \ + fingerprint.c \ + base64.c \ + certlist.c \ + certdump.c \ + certcheck.c \ + certchain.c \ + keylist.c \ + verify.c \ + sign.c \ + encrypt.c \ + decrypt.c \ + import.c \ + export.c \ + delete.c \ + certreqgen.c + + +gpgsm_LDADD = ../jnlib/libjnlib.a ../kbx/libkeybox.a ../common/libcommon.a \ + $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(KSBA_LIBS) -lgpg-error \ + @INTLLIBS@ diff --git a/sm/call-agent.c b/sm/call-agent.c new file mode 100644 index 000000000..4d26e3450 --- /dev/null +++ b/sm/call-agent.c @@ -0,0 +1,713 @@ +/* call-agent.c - divert operations to the agent + * Copyright (C) 2001, 2002, 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 +#include +#ifdef HAVE_LOCALE_H +#include +#endif + +#include "gpgsm.h" +#include +#include +#include "i18n.h" +#include "keydb.h" /* fixme: Move this to import.c */ +#include "../common/membuf.h" + + +static ASSUAN_CONTEXT agent_ctx = NULL; +static int force_pipe_server = 0; + +struct cipher_parm_s { + ASSUAN_CONTEXT ctx; + const char *ciphertext; + size_t ciphertextlen; +}; + +struct genkey_parm_s { + ASSUAN_CONTEXT ctx; + const char *sexp; + size_t sexplen; +}; + +struct learn_parm_s { + int error; + ASSUAN_CONTEXT ctx; + membuf_t *data; +}; + + + +/* Try to connect to the agent via socket or fork it off and work by + pipes. Handle the server's initial greeting */ +static int +start_agent (void) +{ + int rc = 0; + char *infostr, *p; + ASSUAN_CONTEXT ctx; + char *dft_display = NULL; + char *dft_ttyname = NULL; + char *dft_ttytype = NULL; + char *old_lc = NULL; + char *dft_lc = NULL; + + if (agent_ctx) + return 0; /* fixme: We need a context for each thread or serialize + the access to the agent (which is suitable given that + the agent is not MT */ + + infostr = force_pipe_server? NULL : getenv ("GPG_AGENT_INFO"); + if (!infostr) + { + const char *pgmname; + const char *argv[3]; + int no_close_list[3]; + int i; + + if (opt.verbose) + log_info (_("no running gpg-agent - starting one\n")); + + if (fflush (NULL)) + { + gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); + log_error ("error flushing pending output: %s\n", strerror (errno)); + return tmperr; + } + + if (!opt.agent_program || !*opt.agent_program) + opt.agent_program = GNUPG_DEFAULT_AGENT; + if ( !(pgmname = strrchr (opt.agent_program, '/'))) + pgmname = opt.agent_program; + else + pgmname++; + + argv[0] = pgmname; + argv[1] = "--server"; + argv[2] = NULL; + + i=0; + if (log_get_fd () != -1) + no_close_list[i++] = log_get_fd (); + no_close_list[i++] = fileno (stderr); + no_close_list[i] = -1; + + /* connect to the agent and perform initial handshaking */ + rc = assuan_pipe_connect (&ctx, opt.agent_program, (char**)argv, + no_close_list); + } + else + { + int prot; + int pid; + + infostr = xstrdup (infostr); + if ( !(p = strchr (infostr, ':')) || p == infostr) + { + log_error (_("malformed GPG_AGENT_INFO environment variable\n")); + xfree (infostr); + force_pipe_server = 1; + return start_agent (); + } + *p++ = 0; + pid = atoi (p); + while (*p && *p != ':') + p++; + prot = *p? atoi (p+1) : 0; + if (prot != 1) + { + log_error (_("gpg-agent protocol version %d is not supported\n"), + prot); + xfree (infostr); + force_pipe_server = 1; + return start_agent (); + } + + rc = assuan_socket_connect (&ctx, infostr, pid); + xfree (infostr); + if (rc == ASSUAN_Connect_Failed) + { + log_error (_("can't connect to the agent - trying fall back\n")); + force_pipe_server = 1; + return start_agent (); + } + } + + if (rc) + { + log_error ("can't connect to the agent: %s\n", assuan_strerror (rc)); + return gpg_error (GPG_ERR_NO_AGENT); + } + agent_ctx = ctx; + + if (DBG_ASSUAN) + log_debug ("connection to agent established\n"); + + rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return map_assuan_err (rc); + + dft_display = getenv ("DISPLAY"); + if (opt.display || dft_display) + { + char *optstr; + if (asprintf (&optstr, "OPTION display=%s", + opt.display ? opt.display : dft_display) < 0) + return OUT_OF_CORE (errno); + rc = assuan_transact (agent_ctx, optstr, NULL, NULL, NULL, NULL, NULL, + NULL); + free (optstr); + if (rc) + return map_assuan_err (rc); + } + if (!opt.ttyname) + { + dft_ttyname = getenv ("GPG_TTY"); + if ((!dft_ttyname || !*dft_ttyname) && ttyname (0)) + dft_ttyname = ttyname (0); + } + if (opt.ttyname || dft_ttyname) + { + char *optstr; + if (asprintf (&optstr, "OPTION ttyname=%s", + opt.ttyname ? opt.ttyname : dft_ttyname) < 0) + return OUT_OF_CORE (errno); + rc = assuan_transact (agent_ctx, optstr, NULL, NULL, NULL, NULL, NULL, + NULL); + free (optstr); + if (rc) + return map_assuan_err (rc); + } + dft_ttytype = getenv ("TERM"); + if (opt.ttytype || (dft_ttyname && dft_ttytype)) + { + char *optstr; + if (asprintf (&optstr, "OPTION ttytype=%s", + opt.ttyname ? opt.ttytype : dft_ttytype) < 0) + return OUT_OF_CORE (errno); + rc = assuan_transact (agent_ctx, optstr, NULL, NULL, NULL, NULL, NULL, + NULL); + free (optstr); + if (rc) + return map_assuan_err (rc); + } +#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE) + old_lc = setlocale (LC_CTYPE, NULL); + if (old_lc) + { + old_lc = strdup (old_lc); + if (!old_lc) + return OUT_OF_CORE (errno); + } + dft_lc = setlocale (LC_CTYPE, ""); +#endif + if (opt.lc_ctype || (dft_ttyname && dft_lc)) + { + char *optstr; + if (asprintf (&optstr, "OPTION lc-ctype=%s", + opt.lc_ctype ? opt.lc_ctype : dft_lc) < 0) + rc = OUT_OF_CORE (errno); + else + { + rc = assuan_transact (agent_ctx, optstr, NULL, NULL, NULL, NULL, NULL, + NULL); + free (optstr); + if (rc) + rc = map_assuan_err (rc); + } + } +#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE) + if (old_lc) + { + setlocale (LC_CTYPE, old_lc); + free (old_lc); + } +#endif + if (rc) + return rc; +#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES) + old_lc = setlocale (LC_MESSAGES, NULL); + if (old_lc) + { + old_lc = strdup (old_lc); + if (!old_lc) + return OUT_OF_CORE (errno); + } + dft_lc = setlocale (LC_MESSAGES, ""); +#endif + if (opt.lc_messages || (dft_ttyname && dft_lc)) + { + char *optstr; + if (asprintf (&optstr, "OPTION lc-messages=%s", + opt.lc_messages ? opt.lc_messages : dft_lc) < 0) + rc = OUT_OF_CORE (errno); + else + { + rc = assuan_transact (agent_ctx, optstr, NULL, NULL, NULL, NULL, NULL, + NULL); + free (optstr); + if (rc) + rc = map_assuan_err (rc); + } + } +#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES) + if (old_lc) + { + setlocale (LC_MESSAGES, old_lc); + free (old_lc); + } +#endif + + return rc; +} + + +static AssuanError +membuf_data_cb (void *opaque, const void *buffer, size_t length) +{ + membuf_t *data = opaque; + + if (buffer) + put_membuf (data, buffer, length); + return 0; +} + + + + +/* Call the agent to do a sign operation using the key identified by + the hex string KEYGRIP. */ +int +gpgsm_agent_pksign (const char *keygrip, + unsigned char *digest, size_t digestlen, int digestalgo, + char **r_buf, size_t *r_buflen ) +{ + int rc, i; + char *p, line[ASSUAN_LINELENGTH]; + membuf_t data; + size_t len; + + *r_buf = NULL; + rc = start_agent (); + if (rc) + return rc; + + if (digestlen*2 + 50 > DIM(line)) + return gpg_error (GPG_ERR_GENERAL); + + rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return map_assuan_err (rc); + + snprintf (line, DIM(line)-1, "SIGKEY %s", keygrip); + line[DIM(line)-1] = 0; + rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return map_assuan_err (rc); + + sprintf (line, "SETHASH %d ", digestalgo); + p = line + strlen (line); + for (i=0; i < digestlen ; i++, p += 2 ) + sprintf (p, "%02X", digest[i]); + rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return map_assuan_err (rc); + + init_membuf (&data, 1024); + rc = assuan_transact (agent_ctx, "PKSIGN", + membuf_data_cb, &data, NULL, NULL, NULL, NULL); + if (rc) + { + xfree (get_membuf (&data, &len)); + return map_assuan_err (rc); + } + *r_buf = get_membuf (&data, r_buflen); + + if (!gcry_sexp_canon_len (*r_buf, *r_buflen, NULL, NULL)) + { + xfree (*r_buf); *r_buf = NULL; + return gpg_error (GPG_ERR_INV_VALUE); + } + + return *r_buf? 0 : OUT_OF_CORE (errno); +} + + + + +/* Handle a CIPHERTEXT inquiry. Note, we only send the data, + assuan_transact talkes care of flushing and writing the end */ +static AssuanError +inq_ciphertext_cb (void *opaque, const char *keyword) +{ + struct cipher_parm_s *parm = opaque; + AssuanError rc; + + assuan_begin_confidential (parm->ctx); + rc = assuan_send_data (parm->ctx, parm->ciphertext, parm->ciphertextlen); + assuan_end_confidential (parm->ctx); + return rc; +} + + +/* Call the agent to do a decrypt operation using the key identified by + the hex string KEYGRIP. */ +int +gpgsm_agent_pkdecrypt (const char *keygrip, + KsbaConstSexp ciphertext, + char **r_buf, size_t *r_buflen ) +{ + int rc; + char line[ASSUAN_LINELENGTH]; + membuf_t data; + struct cipher_parm_s cipher_parm; + size_t n, len; + char *buf, *endp; + size_t ciphertextlen; + + if (!keygrip || strlen(keygrip) != 40 || !ciphertext || !r_buf || !r_buflen) + return gpg_error (GPG_ERR_INV_VALUE); + *r_buf = NULL; + + ciphertextlen = gcry_sexp_canon_len (ciphertext, 0, NULL, NULL); + if (!ciphertextlen) + return gpg_error (GPG_ERR_INV_VALUE); + + rc = start_agent (); + if (rc) + return rc; + + rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return map_assuan_err (rc); + + assert ( DIM(line) >= 50 ); + snprintf (line, DIM(line)-1, "SETKEY %s", keygrip); + line[DIM(line)-1] = 0; + rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return map_assuan_err (rc); + + init_membuf (&data, 1024); + cipher_parm.ctx = agent_ctx; + cipher_parm.ciphertext = ciphertext; + cipher_parm.ciphertextlen = ciphertextlen; + rc = assuan_transact (agent_ctx, "PKDECRYPT", + membuf_data_cb, &data, + inq_ciphertext_cb, &cipher_parm, NULL, NULL); + if (rc) + { + xfree (get_membuf (&data, &len)); + return map_assuan_err (rc); + } + + put_membuf (&data, "", 1); /* make sure it is 0 terminated */ + buf = get_membuf (&data, &len); + if (!buf) + return gpg_error (GPG_ERR_ENOMEM); + /* FIXME: We would better a return a full S-exp and not just a part */ + assert (len); + len--; /* remove the terminating 0 */ + n = strtoul (buf, &endp, 10); + if (!n || *endp != ':') + return gpg_error (GPG_ERR_INV_SEXP); + endp++; + if (endp-buf+n > len) + return gpg_error (GPG_ERR_INV_SEXP); /* oops len does not + match internal len*/ + memmove (buf, endp, n); + *r_buflen = n; + *r_buf = buf; + return 0; +} + + + + + +/* Handle a KEYPARMS inquiry. Note, we only send the data, + assuan_transact takes care of flushing and writing the end */ +static AssuanError +inq_genkey_parms (void *opaque, const char *keyword) +{ + struct genkey_parm_s *parm = opaque; + AssuanError rc; + + rc = assuan_send_data (parm->ctx, parm->sexp, parm->sexplen); + return rc; +} + + + +/* Call the agent to generate a newkey */ +int +gpgsm_agent_genkey (KsbaConstSexp keyparms, KsbaSexp *r_pubkey) +{ + int rc; + struct genkey_parm_s gk_parm; + membuf_t data; + size_t len; + char *buf; + + *r_pubkey = NULL; + rc = start_agent (); + if (rc) + return rc; + + rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return map_assuan_err (rc); + + init_membuf (&data, 1024); + gk_parm.ctx = agent_ctx; + gk_parm.sexp = keyparms; + gk_parm.sexplen = gcry_sexp_canon_len (keyparms, 0, NULL, NULL); + if (!gk_parm.sexplen) + return gpg_error (GPG_ERR_INV_VALUE); + rc = assuan_transact (agent_ctx, "GENKEY", + membuf_data_cb, &data, + inq_genkey_parms, &gk_parm, NULL, NULL); + if (rc) + { + xfree (get_membuf (&data, &len)); + return map_assuan_err (rc); + } + buf = get_membuf (&data, &len); + if (!buf) + return gpg_error (GPG_ERR_ENOMEM); + if (!gcry_sexp_canon_len (buf, len, NULL, NULL)) + { + xfree (buf); + return gpg_error (GPG_ERR_INV_SEXP); + } + *r_pubkey = buf; + return 0; +} + + +/* Ask the agent whether the certificate is in the list of trusted + keys */ +int +gpgsm_agent_istrusted (KsbaCert cert) +{ + int rc; + char *fpr; + char line[ASSUAN_LINELENGTH]; + + rc = start_agent (); + if (rc) + return rc; + + fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); + if (!fpr) + { + log_error ("error getting the fingerprint\n"); + return gpg_error (GPG_ERR_GENERAL); + } + + snprintf (line, DIM(line)-1, "ISTRUSTED %s", fpr); + line[DIM(line)-1] = 0; + xfree (fpr); + + rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + return map_assuan_err (rc); +} + +/* Ask the agent to mark CERT as a trusted Root-CA one */ +int +gpgsm_agent_marktrusted (KsbaCert cert) +{ + int rc; + char *fpr, *dn; + char line[ASSUAN_LINELENGTH]; + + rc = start_agent (); + if (rc) + return rc; + + fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); + if (!fpr) + { + log_error ("error getting the fingerprint\n"); + return gpg_error (GPG_ERR_GENERAL); + } + + dn = ksba_cert_get_issuer (cert, 0); + if (!dn) + { + xfree (fpr); + return gpg_error (GPG_ERR_GENERAL); + } + snprintf (line, DIM(line)-1, "MARKTRUSTED %s S %s", fpr, dn); + line[DIM(line)-1] = 0; + ksba_free (dn); + xfree (fpr); + + rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + return map_assuan_err (rc); +} + + + +/* Ask the agent whether the a corresponding secret key is available + for the given keygrip */ +int +gpgsm_agent_havekey (const char *hexkeygrip) +{ + int rc; + char line[ASSUAN_LINELENGTH]; + + rc = start_agent (); + if (rc) + return rc; + + if (!hexkeygrip || strlen (hexkeygrip) != 40) + return gpg_error (GPG_ERR_INV_VALUE); + + snprintf (line, DIM(line)-1, "HAVEKEY %s", hexkeygrip); + line[DIM(line)-1] = 0; + + rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + return map_assuan_err (rc); +} + + +static AssuanError +learn_cb (void *opaque, const void *buffer, size_t length) +{ + struct learn_parm_s *parm = opaque; + size_t len; + char *buf; + KsbaCert cert; + int rc; + + if (parm->error) + return 0; + + if (buffer) + { + put_membuf (parm->data, buffer, length); + return 0; + } + /* END encountered - process what we have */ + buf = get_membuf (parm->data, &len); + if (!buf) + { + parm->error = gpg_error (GPG_ERR_ENOMEM); + return 0; + } + + + /* FIXME: this should go into import.c */ + cert = ksba_cert_new (); + if (!cert) + { + parm->error = gpg_error (GPG_ERR_ENOMEM); + return 0; + } + rc = ksba_cert_init_from_mem (cert, buf, len); + if (rc) + { + log_error ("failed to parse a certificate: %s\n", ksba_strerror (rc)); + ksba_cert_release (cert); + parm->error = map_ksba_err (rc); + return 0; + } + + rc = gpgsm_basic_cert_check (cert); + if (gpg_err_code (rc) == GPG_ERR_MISSING_CERT) + { /* For later use we store it in the ephemeral database. */ + log_info ("issuer certificate missing - storing as ephemeral\n"); + keydb_store_cert (cert, 1, NULL); + } + else if (rc) + log_error ("invalid certificate: %s\n", gpg_strerror (rc)); + else + { + int existed; + + if (!keydb_store_cert (cert, 0, &existed)) + { + if (opt.verbose > 1 && existed) + log_info ("certificate already in DB\n"); + else if (opt.verbose && !existed) + log_info ("certificate imported\n"); + } + } + + ksba_cert_release (cert); + init_membuf (parm->data, 4096); + return 0; +} + +/* Call the agent to learn about a smartcard */ +int +gpgsm_agent_learn () +{ + int rc; + struct learn_parm_s learn_parm; + membuf_t data; + size_t len; + + rc = start_agent (); + if (rc) + return rc; + + init_membuf (&data, 4096); + learn_parm.error = 0; + learn_parm.ctx = agent_ctx; + learn_parm.data = &data; + rc = assuan_transact (agent_ctx, "LEARN --send", + learn_cb, &learn_parm, + NULL, NULL, NULL, NULL); + xfree (get_membuf (&data, &len)); + if (rc) + return map_assuan_err (rc); + return learn_parm.error; +} + + +/* Ask the agent to change the passphrase of the key identified by HEXKEYGRIP. */ +int +gpgsm_agent_passwd (const char *hexkeygrip) +{ + int rc; + char line[ASSUAN_LINELENGTH]; + + rc = start_agent (); + if (rc) + return rc; + + if (!hexkeygrip || strlen (hexkeygrip) != 40) + return gpg_error (GPG_ERR_INV_VALUE); + + snprintf (line, DIM(line)-1, "PASSWD %s", hexkeygrip); + line[DIM(line)-1] = 0; + + rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + return map_assuan_err (rc); +} + diff --git a/sm/call-dirmngr.c b/sm/call-dirmngr.c new file mode 100644 index 000000000..b182b246c --- /dev/null +++ b/sm/call-dirmngr.c @@ -0,0 +1,632 @@ +/* call-dirmngr.c - communication with the dromngr + * Copyright (C) 2002, 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 +#include +#include + +#include "gpgsm.h" +#include +#include + +#include "i18n.h" + +struct membuf { + size_t len; + size_t size; + char *buf; + int out_of_core; +}; + + + +static ASSUAN_CONTEXT dirmngr_ctx = NULL; +static int force_pipe_server = 0; + +struct inq_certificate_parm_s { + ASSUAN_CONTEXT ctx; + KsbaCert cert; +}; + +struct lookup_parm_s { + CTRL ctrl; + ASSUAN_CONTEXT ctx; + void (*cb)(void *, KsbaCert); + void *cb_value; + struct membuf data; + int error; +}; + +struct run_command_parm_s { + ASSUAN_CONTEXT ctx; +}; + + +/* A simple implementation of a dynamic buffer. Use init_membuf() to + create a buffer, put_membuf to append bytes and get_membuf to + release and return the buffer. Allocation errors are detected but + only returned at the final get_membuf(), this helps not to clutter + the code with out of core checks. */ + +static void +init_membuf (struct membuf *mb, int initiallen) +{ + mb->len = 0; + mb->size = initiallen; + mb->out_of_core = 0; + mb->buf = xtrymalloc (initiallen); + if (!mb->buf) + mb->out_of_core = 1; +} + +static void +put_membuf (struct membuf *mb, const void *buf, size_t len) +{ + if (mb->out_of_core) + return; + + if (mb->len + len >= mb->size) + { + char *p; + + mb->size += len + 1024; + p = xtryrealloc (mb->buf, mb->size); + if (!p) + { + mb->out_of_core = 1; + return; + } + mb->buf = p; + } + memcpy (mb->buf + mb->len, buf, len); + mb->len += len; +} + +static void * +get_membuf (struct membuf *mb, size_t *len) +{ + char *p; + + if (mb->out_of_core) + { + xfree (mb->buf); + mb->buf = NULL; + return NULL; + } + + p = mb->buf; + *len = mb->len; + mb->buf = NULL; + mb->out_of_core = 1; /* don't allow a reuse */ + return p; +} + + + + + +/* Try to connect to the agent via socket or fork it off and work by + pipes. Handle the server's initial greeting */ +static int +start_dirmngr (void) +{ + int rc; + char *infostr, *p; + ASSUAN_CONTEXT ctx; + + if (dirmngr_ctx) + return 0; /* fixme: We need a context for each thread or serialize + the access to the dirmngr */ + + infostr = force_pipe_server? NULL : getenv ("DIRMNGR_INFO"); + if (!infostr) + { + const char *pgmname; + const char *argv[3]; + int no_close_list[3]; + int i; + + if (opt.verbose) + log_info (_("no running dirmngr - starting one\n")); + + if (fflush (NULL)) + { + gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); + log_error ("error flushing pending output: %s\n", strerror (errno)); + return tmperr; + } + + if (!opt.dirmngr_program || !*opt.dirmngr_program) + opt.dirmngr_program = GNUPG_DEFAULT_DIRMNGR; + if ( !(pgmname = strrchr (opt.dirmngr_program, '/'))) + pgmname = opt.dirmngr_program; + else + pgmname++; + + argv[0] = pgmname; + argv[1] = "--server"; + argv[2] = NULL; + + i=0; + if (log_get_fd () != -1) + no_close_list[i++] = log_get_fd (); + no_close_list[i++] = fileno (stderr); + no_close_list[i] = -1; + + /* connect to the agent and perform initial handshaking */ + rc = assuan_pipe_connect (&ctx, opt.dirmngr_program, (char**)argv, + no_close_list); + } + else + { + int prot; + int pid; + + infostr = xstrdup (infostr); + if ( !(p = strchr (infostr, ':')) || p == infostr) + { + log_error (_("malformed DIRMNGR_INFO environment variable\n")); + xfree (infostr); + force_pipe_server = 1; + return start_dirmngr (); + } + *p++ = 0; + pid = atoi (p); + while (*p && *p != ':') + p++; + prot = *p? atoi (p+1) : 0; + if (prot != 1) + { + log_error (_("dirmngr protocol version %d is not supported\n"), + prot); + xfree (infostr); + force_pipe_server = 1; + return start_dirmngr (); + } + + rc = assuan_socket_connect (&ctx, infostr, pid); + xfree (infostr); + if (rc == ASSUAN_Connect_Failed) + { + log_error (_("can't connect to the dirmngr - trying fall back\n")); + force_pipe_server = 1; + return start_dirmngr (); + } + } + + if (rc) + { + log_error ("can't connect to the dirmngr: %s\n", assuan_strerror (rc)); + return gpg_error (GPG_ERR_NO_DIRMNGR); + } + dirmngr_ctx = ctx; + + if (DBG_ASSUAN) + log_debug ("connection to dirmngr established\n"); + return 0; +} + + + +/* Handle a SENDCERT inquiry. */ +static AssuanError +inq_certificate (void *opaque, const char *line) +{ + struct inq_certificate_parm_s *parm = opaque; + AssuanError rc; + const unsigned char *der; + size_t derlen; + + if (!(!strncmp (line, "SENDCERT", 8) && (line[8] == ' ' || !line[8]))) + { + log_error ("unsupported inquiry `%s'\n", line); + return ASSUAN_Inquire_Unknown; + } + line += 8; + + if (!*line) + { /* send the current certificate */ + der = ksba_cert_get_image (parm->cert, &derlen); + if (!der) + rc = ASSUAN_Inquire_Error; + else + rc = assuan_send_data (parm->ctx, der, derlen); + } + else + { /* send the given certificate */ + int err; + KsbaCert cert; + + err = gpgsm_find_cert (line, &cert); + if (err) + { + log_error ("certificate not found: %s\n", gpg_strerror (err)); + rc = ASSUAN_Inquire_Error; + } + else + { + der = ksba_cert_get_image (cert, &derlen); + if (!der) + rc = ASSUAN_Inquire_Error; + else + rc = assuan_send_data (parm->ctx, der, derlen); + ksba_cert_release (cert); + } + } + + return rc; +} + + + +/* Call the directory manager to check whether the certificate is valid + Returns 0 for valid or usually one of the errors: + + GPG_ERR_CERTIFICATE_REVOKED + GPG_ERR_NO_CRL_KNOWN + GPG_ERR_CRL_TOO_OLD + */ +int +gpgsm_dirmngr_isvalid (KsbaCert cert) +{ + int rc; + char *certid; + char line[ASSUAN_LINELENGTH]; + struct inq_certificate_parm_s parm; + + rc = start_dirmngr (); + if (rc) + return rc; + + certid = gpgsm_get_certid (cert); + if (!certid) + { + log_error ("error getting the certificate ID\n"); + return gpg_error (GPG_ERR_GENERAL); + } + + if (opt.verbose > 1) + { + char *fpr = gpgsm_get_fingerprint_string (cert, GCRY_MD_SHA1); + log_info ("asking dirmngr about %s\n", fpr); + xfree (fpr); + } + + parm.ctx = dirmngr_ctx; + parm.cert = cert; + + snprintf (line, DIM(line)-1, "ISVALID %s", certid); + line[DIM(line)-1] = 0; + xfree (certid); + + rc = assuan_transact (dirmngr_ctx, line, NULL, NULL, + inq_certificate, &parm, NULL, NULL); + if (opt.verbose > 1) + log_info ("response of dirmngr: %s\n", rc? assuan_strerror (rc): "okay"); + return map_assuan_err (rc); +} + + + +/* Lookup helpers*/ +static AssuanError +lookup_cb (void *opaque, const void *buffer, size_t length) +{ + struct lookup_parm_s *parm = opaque; + size_t len; + char *buf; + KsbaCert cert; + int rc; + + if (parm->error) + return 0; + + if (buffer) + { + put_membuf (&parm->data, buffer, length); + return 0; + } + /* END encountered - process what we have */ + buf = get_membuf (&parm->data, &len); + if (!buf) + { + parm->error = gpg_error (GPG_ERR_ENOMEM); + return 0; + } + + cert = ksba_cert_new (); + if (!cert) + { + parm->error = gpg_error (GPG_ERR_ENOMEM); + return 0; + } + rc = ksba_cert_init_from_mem (cert, buf, len); + if (rc) + { + log_error ("failed to parse a certificate: %s\n", ksba_strerror (rc)); + } + else + { + parm->cb (parm->cb_value, cert); + } + + ksba_cert_release (cert); + init_membuf (&parm->data, 4096); + return 0; +} + +/* Return a properly escaped pattern from NAMES. The only error + return is NULL to indicate a malloc failure. */ +static char * +pattern_from_strlist (STRLIST names) +{ + STRLIST sl; + int n; + const char *s; + char *pattern, *p; + + for (n=0, sl=names; sl; sl = sl->next) + { + for (s=sl->d; *s; s++, n++) + { + if (*s == '%' || *s == ' ' || *s == '+') + n += 2; + } + n++; + } + + p = pattern = xtrymalloc (n+1); + if (!pattern) + return NULL; + + for (n=0, sl=names; sl; sl = sl->next) + { + for (s=sl->d; *s; s++) + { + switch (*s) + { + case '%': + *p++ = '%'; + *p++ = '2'; + *p++ = '5'; + break; + case ' ': + *p++ = '%'; + *p++ = '2'; + *p++ = '0'; + break; + case '+': + *p++ = '%'; + *p++ = '2'; + *p++ = 'B'; + break; + default: + *p++ = *s; + break; + } + } + *p++ = ' '; + } + if (p == pattern) + *pattern = 0; /* is empty */ + else + p[-1] = '\0'; /* remove trailing blank */ + + return pattern; +} + +static AssuanError +lookup_status_cb (void *opaque, const char *line) +{ + struct lookup_parm_s *parm = opaque; + + if (!strncmp (line, "TRUNCATED", 9) && (line[9]==' ' || !line[9])) + { + if (parm->ctrl) + { + for (line +=9; *line == ' '; line++) + ; + gpgsm_status (parm->ctrl, STATUS_TRUNCATED, line); + } + } + return 0; +} + + +/* Run the Directroy Managers lookup command using the pattern + compiled from the strings given in NAMES. The caller must provide + the callback CB which will be passed cert by cert. Note that CTRL + is optional. */ +int +gpgsm_dirmngr_lookup (CTRL ctrl, STRLIST names, + void (*cb)(void*, KsbaCert), void *cb_value) +{ + int rc; + char *pattern; + char line[ASSUAN_LINELENGTH]; + struct lookup_parm_s parm; + size_t len; + + rc = start_dirmngr (); + if (rc) + return rc; + + pattern = pattern_from_strlist (names); + if (!pattern) + return OUT_OF_CORE (errno); + snprintf (line, DIM(line)-1, "LOOKUP %s", pattern); + line[DIM(line)-1] = 0; + xfree (pattern); + + parm.ctrl = ctrl; + parm.ctx = dirmngr_ctx; + parm.cb = cb; + parm.cb_value = cb_value; + parm.error = 0; + init_membuf (&parm.data, 4096); + + rc = assuan_transact (dirmngr_ctx, line, lookup_cb, &parm, + NULL, NULL, lookup_status_cb, &parm); + xfree (get_membuf (&parm.data, &len)); + if (rc) + return map_assuan_err (rc); + return parm.error; +} + + + +/* Run Command helpers*/ + +/* Fairly simple callback to write all output of dirmngr to stdout. */ +static AssuanError +run_command_cb (void *opaque, const void *buffer, size_t length) +{ + if (buffer) + { + if ( fwrite (buffer, length, 1, stdout) != 1 ) + log_error ("error writing to stdout: %s\n", strerror (errno)); + } + return 0; +} + +/* Handle inquiries from the dirmngr COMMAND. */ +static AssuanError +run_command_inq_cb (void *opaque, const char *line) +{ + struct run_command_parm_s *parm = opaque; + AssuanError rc = 0; + + if ( !strncmp (line, "SENDCERT", 8) && (line[8] == ' ' || !line[8]) ) + { /* send the given certificate */ + int err; + KsbaCert cert; + const unsigned char *der; + size_t derlen; + + line += 8; + if (!*line) + return ASSUAN_Inquire_Error; + + err = gpgsm_find_cert (line, &cert); + if (err) + { + log_error ("certificate not found: %s\n", gpg_strerror (err)); + rc = ASSUAN_Inquire_Error; + } + else + { + der = ksba_cert_get_image (cert, &derlen); + if (!der) + rc = ASSUAN_Inquire_Error; + else + rc = assuan_send_data (parm->ctx, der, derlen); + ksba_cert_release (cert); + } + } + else if ( !strncmp (line, "PRINTINFO", 9) && (line[9] == ' ' || !line[9]) ) + { /* Simply show the message given in the argument. */ + line += 9; + log_info ("dirmngr: %s\n", line); + } + else + { + log_error ("unsupported inquiry `%s'\n", line); + rc = ASSUAN_Inquire_Unknown; + } + + return rc; +} + +static AssuanError +run_command_status_cb (void *opaque, const char *line) +{ + if (opt.verbose) + { + log_info ("dirmngr status: %s\n", line); + } + return 0; +} + + + +/* Pass COMMAND to dirmngr and print all output generated by Dirmngr + to stdout. A couple of inquiries are defined (see above). ARGC + arguments in ARGV are given to the Dirmngr. Spaces, plus and + percent characters within the argument strings are percent escaped + so that blanks can act as delimiters. */ +int +gpgsm_dirmngr_run_command (CTRL ctrl, const char *command, + int argc, char **argv) +{ + int rc; + int i; + const char *s; + char *line, *p; + size_t len; + struct run_command_parm_s parm; + + rc = start_dirmngr (); + if (rc) + return rc; + + parm.ctx = dirmngr_ctx; + + len = strlen (command) + 1; + for (i=0; i < argc; i++) + len += 1 + 3*strlen (argv[i]); /* enough space for percent escaping */ + line = xtrymalloc (len); + if (!line) + return OUT_OF_CORE (errno); + + p = stpcpy (line, command); + for (i=0; i < argc; i++) + { + *p++ = ' '; + for (s=argv[i]; *s; s++) + { + if (!isascii (*s)) + *p++ = *s; + else if (*s == ' ') + *p++ = '+'; + else if (!isprint (*s) || *s == '+') + { + sprintf (p, "%%%02X", *s); + p += 3; + } + else + *p++ = *s; + } + } + *p = 0; + + rc = assuan_transact (dirmngr_ctx, line, + run_command_cb, NULL, + run_command_inq_cb, &parm, + run_command_status_cb, NULL); + xfree (line); + log_info ("response of dirmngr: %s\n", rc? assuan_strerror (rc): "okay"); + return map_assuan_err (rc); +} diff --git a/sm/certchain.c b/sm/certchain.c new file mode 100644 index 000000000..6323c725e --- /dev/null +++ b/sm/certchain.c @@ -0,0 +1,793 @@ +/* certchain.c - certificate chain validation + * Copyright (C) 2001, 2002, 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 +#include + +#include "gpgsm.h" +#include +#include + +#include "keydb.h" +#include "i18n.h" + +static int +unknown_criticals (KsbaCert cert) +{ + static const char *known[] = { + "2.5.29.15", /* keyUsage */ + "2.5.29.19", /* basic Constraints */ + "2.5.29.32", /* certificatePolicies */ + NULL + }; + int rc = 0, i, idx, crit; + const char *oid; + KsbaError err; + + for (idx=0; !(err=ksba_cert_get_extension (cert, idx, + &oid, &crit, NULL, NULL));idx++) + { + if (!crit) + continue; + for (i=0; known[i] && strcmp (known[i],oid); i++) + ; + if (!known[i]) + { + log_error (_("critical certificate extension %s is not supported\n"), + oid); + rc = gpg_error (GPG_ERR_UNSUPPORTED_CERT); + } + } + if (err && err != -1) + rc = map_ksba_err (err); + + return rc; +} + +static int +allowed_ca (KsbaCert cert, int *chainlen) +{ + KsbaError err; + int flag; + + err = ksba_cert_is_ca (cert, &flag, chainlen); + if (err) + return map_ksba_err (err); + if (!flag) + { + log_error (_("issuer certificate is not marked as a CA\n")); + return gpg_error (GPG_ERR_BAD_CA_CERT); + } + return 0; +} + + +static int +check_cert_policy (KsbaCert cert) +{ + KsbaError err; + char *policies; + FILE *fp; + int any_critical; + + err = ksba_cert_get_cert_policies (cert, &policies); + if (err == KSBA_No_Data) + return 0; /* no policy given */ + if (err) + return map_ksba_err (err); + + /* STRING is a line delimited list of certifiate policies as stored + in the certificate. The line itself is colon delimited where the + first field is the OID of the policy and the second field either + N or C for normal or critical extension */ + + if (opt.verbose > 1) + log_info ("certificate's policy list: %s\n", policies); + + /* The check is very minimal but won't give false positives */ + any_critical = !!strstr (policies, ":C"); + + if (!opt.policy_file) + { + xfree (policies); + if (any_critical) + { + log_error ("critical marked policy without configured policies\n"); + return gpg_error (GPG_ERR_NO_POLICY_MATCH); + } + return 0; + } + + fp = fopen (opt.policy_file, "r"); + if (!fp) + { + log_error ("failed to open `%s': %s\n", + opt.policy_file, strerror (errno)); + xfree (policies); + return gpg_error (GPG_ERR_NO_POLICY_MATCH); + } + + for (;;) + { + int c; + char *p, line[256]; + char *haystack, *allowed; + + /* read line */ + do + { + if (!fgets (line, DIM(line)-1, fp) ) + { + gpg_error_t tmperr; + + xfree (policies); + if (feof (fp)) + { + fclose (fp); + /* with no critical policies this is only a warning */ + if (!any_critical) + { + log_info (_("note: certificate policy not allowed\n")); + return 0; + } + log_error (_("certificate policy not allowed\n")); + return gpg_error (GPG_ERR_NO_POLICY_MATCH); + } + tmperr = gpg_error (gpg_err_code_from_errno (errno)); + fclose (fp); + return tmperr; + } + + if (!*line || line[strlen(line)-1] != '\n') + { + /* eat until end of line */ + while ( (c=getc (fp)) != EOF && c != '\n') + ; + fclose (fp); + xfree (policies); + return gpg_error (*line? GPG_ERR_LINE_TOO_LONG + : GPG_ERR_INCOMPLETE_LINE); + } + + /* Allow for empty lines and spaces */ + for (p=line; spacep (p); p++) + ; + } + while (!*p || *p == '\n' || *p == '#'); + + /* parse line */ + for (allowed=line; spacep (allowed); allowed++) + ; + p = strpbrk (allowed, " :\n"); + if (!*p || p == allowed) + { + fclose (fp); + xfree (policies); + return gpg_error (GPG_ERR_CONFIGURATION); + } + *p = 0; /* strip the rest of the line */ + /* See whether we find ALLOWED (which is an OID) in POLICIES */ + for (haystack=policies; (p=strstr (haystack, allowed)); haystack = p+1) + { + if ( !(p == policies || p[-1] == '\n') ) + continue; /* does not match the begin of a line */ + if (p[strlen (allowed)] != ':') + continue; /* the length does not match */ + /* Yep - it does match so return okay */ + fclose (fp); + xfree (policies); + return 0; + } + } +} + + +static void +find_up_store_certs_cb (void *cb_value, KsbaCert cert) +{ + if (keydb_store_cert (cert, 1, NULL)) + log_error ("error storing issuer certificate as ephemeral\n"); + ++*(int*)cb_value; +} + + +static int +find_up (KEYDB_HANDLE kh, KsbaCert cert, const char *issuer) +{ + KsbaName authid; + KsbaSexp authidno; + int rc = -1; + + if (!ksba_cert_get_auth_key_id (cert, NULL, &authid, &authidno)) + { + const char *s = ksba_name_enum (authid, 0); + if (s && *authidno) + { + rc = keydb_search_issuer_sn (kh, s, authidno); + if (rc) + keydb_search_reset (kh); + if (rc == -1) + { /* And try the ephemeral DB. */ + int old = keydb_set_ephemeral (kh, 1); + if (!old) + { + rc = keydb_search_issuer_sn (kh, s, authidno); + if (rc) + keydb_search_reset (kh); + } + keydb_set_ephemeral (kh, old); + } + } + /* print a note so that the user does not feel too helpless when + an issuer certificate was found and gpgsm prints BAD + signature becuase it is not the correct one. */ + if (rc == -1) + { + log_info ("issuer certificate (#"); + gpgsm_dump_serial (authidno); + log_printf ("/"); + gpgsm_dump_string (s); + log_printf (") not found\n"); + } + else if (rc) + log_error ("failed to find authorityKeyIdentifier: rc=%d\n", rc); + ksba_name_release (authid); + xfree (authidno); + /* Fixme: don't know how to do dirmngr lookup with serial+issuer. */ + } + + if (rc) /* not found via authorithyKeyIdentifier, try regular issuer name */ + rc = keydb_search_subject (kh, issuer); + if (rc == -1) + { + /* Not found, lets see whether we have one in the ephemeral key DB. */ + int old = keydb_set_ephemeral (kh, 1); + if (!old) + { + keydb_search_reset (kh); + rc = keydb_search_subject (kh, issuer); + } + keydb_set_ephemeral (kh, old); + } + + if (rc == -1 && opt.auto_issuer_key_retrieve) + { + STRLIST names = NULL; + int count = 0; + char *pattern; + const char *s; + + if (opt.verbose) + log_info (_("looking up issuer at external location\n")); + /* dirmngr is confused about unknown attributes so has a quick + and ugly hack we locate the CN and use this and the + following. Fixme: we should have far better parsing in the + dirmngr. */ + s = strstr (issuer, "CN="); + if (!s || s == issuer || s[-1] != ',') + s = issuer; + + pattern = xtrymalloc (strlen (s)+2); + if (!pattern) + return OUT_OF_CORE (errno); + strcpy (stpcpy (pattern, "/"), s); + add_to_strlist (&names, pattern); + xfree (pattern); + rc = gpgsm_dirmngr_lookup (NULL, names, find_up_store_certs_cb, &count); + free_strlist (names); + if (opt.verbose) + log_info (_("number of issuers matching: %d\n"), count); + if (rc) + { + log_error ("external key lookup failed: %s\n", gpg_strerror (rc)); + rc = -1; + } + else if (!count) + rc = -1; + else + { + int old; + /* The issuers are currently stored in the ephemeral key + DB, so we temporary switch to ephemeral mode. */ + old = keydb_set_ephemeral (kh, 1); + keydb_search_reset (kh); + rc = keydb_search_subject (kh, issuer); + keydb_set_ephemeral (kh, old); + } + } + return rc; +} + + +/* Return the next certificate up in the chain starting at START. + Returns -1 when there are no more certificates. */ +int +gpgsm_walk_cert_chain (KsbaCert start, KsbaCert *r_next) +{ + int rc = 0; + char *issuer = NULL; + char *subject = NULL; + KEYDB_HANDLE kh = keydb_new (0); + + *r_next = NULL; + if (!kh) + { + log_error (_("failed to allocated keyDB handle\n")); + rc = gpg_error (GPG_ERR_GENERAL); + goto leave; + } + + issuer = ksba_cert_get_issuer (start, 0); + subject = ksba_cert_get_subject (start, 0); + if (!issuer) + { + log_error ("no issuer found in certificate\n"); + rc = gpg_error (GPG_ERR_BAD_CERT); + goto leave; + } + if (!subject) + { + log_error ("no subject found in certificate\n"); + rc = gpg_error (GPG_ERR_BAD_CERT); + goto leave; + } + + if (!strcmp (issuer, subject)) + { + rc = -1; /* we are at the root */ + goto leave; + } + + rc = find_up (kh, start, issuer); + if (rc) + { + /* it is quite common not to have a certificate, so better don't + print an error here */ + if (rc != -1 && opt.verbose > 1) + log_error ("failed to find issuer's certificate: rc=%d\n", rc); + rc = gpg_error (GPG_ERR_MISSING_CERT); + goto leave; + } + + rc = keydb_get_cert (kh, r_next); + if (rc) + { + log_error ("failed to get cert: rc=%d\n", rc); + rc = gpg_error (GPG_ERR_GENERAL); + } + + leave: + xfree (issuer); + xfree (subject); + keydb_release (kh); + return rc; +} + + +/* Check whether the CERT is a root certificate. Returns True if this + is the case. */ +int +gpgsm_is_root_cert (KsbaCert cert) +{ + char *issuer; + char *subject; + int yes; + + issuer = ksba_cert_get_issuer (cert, 0); + subject = ksba_cert_get_subject (cert, 0); + yes = (issuer && subject && !strcmp (issuer, subject)); + xfree (issuer); + xfree (subject); + return yes; +} + + +/* Validate a chain and optionally return the nearest expiration time + in R_EXPTIME */ +int +gpgsm_validate_chain (CTRL ctrl, KsbaCert cert, time_t *r_exptime) +{ + int rc = 0, depth = 0, maxdepth; + char *issuer = NULL; + char *subject = NULL; + KEYDB_HANDLE kh = keydb_new (0); + KsbaCert subject_cert = NULL, issuer_cert = NULL; + time_t current_time = gnupg_get_time (); + time_t exptime = 0; + int any_expired = 0; + int any_revoked = 0; + int any_no_crl = 0; + int any_crl_too_old = 0; + int any_no_policy_match = 0; + + if (r_exptime) + *r_exptime = 0; + + if (opt.no_chain_validation) + { + log_info ("WARNING: bypassing certificate chain validation\n"); + return 0; + } + + if (!kh) + { + log_error (_("failed to allocated keyDB handle\n")); + rc = gpg_error (GPG_ERR_GENERAL); + goto leave; + } + + if (DBG_X509) + gpgsm_dump_cert ("subject", cert); + + subject_cert = cert; + maxdepth = 50; + + for (;;) + { + xfree (issuer); + xfree (subject); + issuer = ksba_cert_get_issuer (subject_cert, 0); + subject = ksba_cert_get_subject (subject_cert, 0); + + if (!issuer) + { + log_error ("no issuer found in certificate\n"); + rc = gpg_error (GPG_ERR_BAD_CERT); + goto leave; + } + + { + time_t not_before, not_after; + + not_before = ksba_cert_get_validity (subject_cert, 0); + not_after = ksba_cert_get_validity (subject_cert, 1); + if (not_before == (time_t)(-1) || not_after == (time_t)(-1)) + { + log_error ("certificate with invalid validity\n"); + rc = gpg_error (GPG_ERR_BAD_CERT); + goto leave; + } + + if (not_after) + { + if (!exptime) + exptime = not_after; + else if (not_after < exptime) + exptime = not_after; + } + + if (not_before && current_time < not_before) + { + log_error ("certificate too young; valid from "); + gpgsm_dump_time (not_before); + log_printf ("\n"); + rc = gpg_error (GPG_ERR_CERT_TOO_YOUNG); + goto leave; + } + if (not_after && current_time > not_after) + { + log_error ("certificate has expired at "); + gpgsm_dump_time (not_after); + log_printf ("\n"); + any_expired = 1; + } + } + + rc = unknown_criticals (subject_cert); + if (rc) + goto leave; + + if (!opt.no_policy_check) + { + rc = check_cert_policy (subject_cert); + if (gpg_err_code (rc) == GPG_ERR_NO_POLICY_MATCH) + { + any_no_policy_match = 1; + rc = 1; + } + else if (rc) + goto leave; + } + + if (!opt.no_crl_check) + { + rc = gpgsm_dirmngr_isvalid (subject_cert); + if (rc) + { + switch (rc) + { + case GPG_ERR_CERT_REVOKED: + log_error (_("the certificate has been revoked\n")); + any_revoked = 1; + break; + case GPG_ERR_NO_CRL_KNOWN: + log_error (_("no CRL found for certificate\n")); + any_no_crl = 1; + break; + case GPG_ERR_CRL_TOO_OLD: + log_error (_("the available CRL is too old\n")); + log_info (_("please make sure that the " + "\"dirmngr\" is properly installed\n")); + any_crl_too_old = 1; + break; + default: + log_error (_("checking the CRL failed: %s\n"), + gpg_strerror (rc)); + goto leave; + } + rc = 0; + } + } + + if (subject && !strcmp (issuer, subject)) + { + if (gpgsm_check_cert_sig (subject_cert, subject_cert) ) + { + log_error ("selfsigned certificate has a BAD signatures\n"); + rc = gpg_error (depth? GPG_ERR_BAD_CERT_CHAIN + : GPG_ERR_BAD_CERT); + goto leave; + } + rc = allowed_ca (subject_cert, NULL); + if (rc) + goto leave; + + rc = gpgsm_agent_istrusted (subject_cert); + if (!rc) + ; + else if (gpg_err_code (rc) == GPG_ERR_NOT_TRUSTED) + { + int rc2; + + char *fpr = gpgsm_get_fingerprint_string (subject_cert, + GCRY_MD_SHA1); + log_info (_("root certificate is not marked trusted\n")); + log_info (_("fingerprint=%s\n"), fpr? fpr : "?"); + xfree (fpr); + rc2 = gpgsm_agent_marktrusted (subject_cert); + if (!rc2) + { + log_info (_("root certificate has now" + " been marked as trusted\n")); + rc = 0; + } + else + { + gpgsm_dump_cert ("issuer", subject_cert); + log_info ("after checking the fingerprint, you may want " + "to enter it manually into " + "\"~/.gnupg-test/trustlist.txt\"\n"); + } + } + else + { + log_error (_("checking the trust list failed: %s\n"), + gpg_strerror (rc)); + } + + break; /* okay, a self-signed certicate is an end-point */ + } + + depth++; + if (depth > maxdepth) + { + log_error (_("certificate chain too long\n")); + rc = gpg_error (GPG_ERR_BAD_CERT_CHAIN); + goto leave; + } + + /* find the next cert up the tree */ + keydb_search_reset (kh); + rc = find_up (kh, subject_cert, issuer); + if (rc) + { + if (rc == -1) + { + log_info ("issuer certificate (#/"); + gpgsm_dump_string (issuer); + log_printf (") not found\n"); + } + else + log_error ("failed to find issuer's certificate: rc=%d\n", rc); + rc = gpg_error (GPG_ERR_MISSING_CERT); + goto leave; + } + + ksba_cert_release (issuer_cert); issuer_cert = NULL; + rc = keydb_get_cert (kh, &issuer_cert); + if (rc) + { + log_error ("failed to get cert: rc=%d\n", rc); + rc = gpg_error (GPG_ERR_GENERAL); + goto leave; + } + + if (DBG_X509) + { + log_debug ("got issuer's certificate:\n"); + gpgsm_dump_cert ("issuer", issuer_cert); + } + + if (gpgsm_check_cert_sig (issuer_cert, subject_cert) ) + { + log_error ("certificate has a BAD signatures\n"); + rc = gpg_error (GPG_ERR_BAD_CERT_CHAIN); + goto leave; + } + + { + int chainlen; + rc = allowed_ca (issuer_cert, &chainlen); + if (rc) + goto leave; + if (chainlen >= 0 && (depth - 1) > chainlen) + { + log_error (_("certificate chain longer than allowed by CA (%d)\n"), + chainlen); + rc = gpg_error (GPG_ERR_BAD_CERT_CHAIN); + goto leave; + } + } + + rc = gpgsm_cert_use_cert_p (issuer_cert); + if (rc) + { + char numbuf[50]; + sprintf (numbuf, "%d", rc); + gpgsm_status2 (ctrl, STATUS_ERROR, "certcert.issuer.keyusage", + numbuf, NULL); + rc = 0; + } + + if (opt.verbose) + log_info ("certificate is good\n"); + + keydb_search_reset (kh); + subject_cert = issuer_cert; + issuer_cert = NULL; + } + + if (opt.no_policy_check) + log_info ("policies not checked due to --disable-policy-checks option\n"); + if (opt.no_crl_check) + log_info ("CRLs not checked due to --disable-crl-checks option\n"); + + if (!rc) + { /* If we encountered an error somewhere during the checks, set + the error code to the most critical one */ + if (any_revoked) + rc = gpg_error (GPG_ERR_CERT_REVOKED); + else if (any_no_crl) + rc = gpg_error (GPG_ERR_NO_CRL_KNOWN); + else if (any_crl_too_old) + rc = gpg_error (GPG_ERR_CRL_TOO_OLD); + else if (any_no_policy_match) + rc = gpg_error (GPG_ERR_NO_POLICY_MATCH); + else if (any_expired) + rc = gpg_error (GPG_ERR_CERT_EXPIRED); + } + + leave: + if (r_exptime) + *r_exptime = exptime; + xfree (issuer); + keydb_release (kh); + ksba_cert_release (issuer_cert); + if (subject_cert != cert) + ksba_cert_release (subject_cert); + return rc; +} + + +/* Check that the given certificate is valid but DO NOT check any + constraints. We assume that the issuers certificate is already in + the DB and that this one is valid; which it should be because it + has been checked using this function. */ +int +gpgsm_basic_cert_check (KsbaCert cert) +{ + int rc = 0; + char *issuer = NULL; + char *subject = NULL; + KEYDB_HANDLE kh = keydb_new (0); + KsbaCert issuer_cert = NULL; + + if (opt.no_chain_validation) + { + log_info ("WARNING: bypassing basic certificate checks\n"); + return 0; + } + + if (!kh) + { + log_error (_("failed to allocated keyDB handle\n")); + rc = gpg_error (GPG_ERR_GENERAL); + goto leave; + } + + issuer = ksba_cert_get_issuer (cert, 0); + subject = ksba_cert_get_subject (cert, 0); + if (!issuer) + { + log_error ("no issuer found in certificate\n"); + rc = gpg_error (GPG_ERR_BAD_CERT); + goto leave; + } + + if (subject && !strcmp (issuer, subject)) + { + if (gpgsm_check_cert_sig (cert, cert) ) + { + log_error ("selfsigned certificate has a BAD signatures\n"); + rc = gpg_error (GPG_ERR_BAD_CERT); + goto leave; + } + } + else + { + /* find the next cert up the tree */ + keydb_search_reset (kh); + rc = find_up (kh, cert, issuer); + if (rc) + { + if (rc == -1) + { + log_info ("issuer certificate (#/"); + gpgsm_dump_string (issuer); + log_printf (") not found\n"); + } + else + log_error ("failed to find issuer's certificate: rc=%d\n", rc); + rc = gpg_error (GPG_ERR_MISSING_CERT); + goto leave; + } + + ksba_cert_release (issuer_cert); issuer_cert = NULL; + rc = keydb_get_cert (kh, &issuer_cert); + if (rc) + { + log_error ("failed to get cert: rc=%d\n", rc); + rc = gpg_error (GPG_ERR_GENERAL); + goto leave; + } + + if (gpgsm_check_cert_sig (issuer_cert, cert) ) + { + log_error ("certificate has a BAD signatures\n"); + rc = gpg_error (GPG_ERR_BAD_CERT); + goto leave; + } + if (opt.verbose) + log_info ("certificate is good\n"); + } + + leave: + xfree (issuer); + keydb_release (kh); + ksba_cert_release (issuer_cert); + return rc; +} + diff --git a/sm/certcheck.c b/sm/certcheck.c new file mode 100644 index 000000000..35509c67e --- /dev/null +++ b/sm/certcheck.c @@ -0,0 +1,300 @@ +/* certcheck.c - check one certificate + * Copyright (C) 2001, 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 +#include + +#include "gpgsm.h" +#include +#include + +#include "keydb.h" +#include "i18n.h" + + +static int +do_encode_md (gcry_md_hd_t md, int algo, unsigned int nbits, + gcry_mpi_t *r_val) +{ + int nframe = (nbits+7) / 8; + byte *frame; + int i, n; + byte asn[100]; + size_t asnlen; + size_t len; + + asnlen = DIM(asn); + if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen)) + { + log_error ("No object identifier for algo %d\n", algo); + return gpg_error (GPG_ERR_INTERNAL); + } + + len = gcry_md_get_algo_dlen (algo); + + if ( len + asnlen + 4 > nframe ) + { + log_error ("can't encode a %d bit MD into a %d bits frame\n", + (int)(len*8), (int)nbits); + return gpg_error (GPG_ERR_INTERNAL); + } + + /* We encode the MD in this way: + * + * 0 A PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes) + * + * PAD consists of FF bytes. + */ + frame = xtrymalloc (nframe); + if (!frame) + return OUT_OF_CORE (errno); + n = 0; + frame[n++] = 0; + frame[n++] = 1; /* block type */ + i = nframe - len - asnlen -3 ; + assert ( i > 1 ); + memset ( frame+n, 0xff, i ); n += i; + frame[n++] = 0; + memcpy ( frame+n, asn, asnlen ); n += asnlen; + memcpy ( frame+n, gcry_md_read(md, algo), len ); n += len; + assert ( n == nframe ); + if (DBG_X509) + { + int j; + log_debug ("encoded hash:"); + for (j=0; j < nframe; j++) + log_printf (" %02X", frame[j]); + log_printf ("\n"); + } + + gcry_mpi_scan (r_val, GCRYMPI_FMT_USG, frame, n, &nframe); + xfree (frame); + return 0; +} + + +/* + Check the signature on CERT using the ISSUER-CERT. This function + does only test the cryptographic signature and nothing else. It is + assumed that the ISSUER_CERT is valid. */ +int +gpgsm_check_cert_sig (KsbaCert issuer_cert, KsbaCert cert) +{ + const char *algoid; + gcry_md_hd_t md; + int rc, algo; + gcry_mpi_t frame; + KsbaSexp p; + size_t n; + gcry_sexp_t s_sig, s_hash, s_pkey; + + algo = gcry_md_map_name ( (algoid=ksba_cert_get_digest_algo (cert))); + if (!algo) + { + log_error ("unknown hash algorithm `%s'\n", algoid? algoid:"?"); + return gpg_error (GPG_ERR_GENERAL); + } + rc = gcry_md_open (&md, algo, 0); + if (rc) + { + log_error ("md_open failed: %s\n", gpg_strerror (rc)); + return rc; + } + if (DBG_HASHING) + gcry_md_start_debug (md, "hash.cert"); + + rc = ksba_cert_hash (cert, 1, HASH_FNC, md); + if (rc) + { + log_error ("ksba_cert_hash failed: %s\n", ksba_strerror (rc)); + gcry_md_close (md); + return map_ksba_err (rc); + } + gcry_md_final (md); + + p = ksba_cert_get_sig_val (cert); + n = gcry_sexp_canon_len (p, 0, NULL, NULL); + if (!n) + { + log_error ("libksba did not return a proper S-Exp\n"); + gcry_md_close (md); + ksba_free (p); + return gpg_error (GPG_ERR_BUG); + } + if (DBG_X509) + { + int j; + log_debug ("signature value:"); + for (j=0; j < n; j++) + log_printf (" %02X", p[j]); + log_printf ("\n"); + } + + rc = gcry_sexp_sscan ( &s_sig, NULL, p, n); + ksba_free (p); + if (rc) + { + log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); + gcry_md_close (md); + return rc; + } + + p = ksba_cert_get_public_key (issuer_cert); + n = gcry_sexp_canon_len (p, 0, NULL, NULL); + if (!n) + { + log_error ("libksba did not return a proper S-Exp\n"); + gcry_md_close (md); + ksba_free (p); + gcry_sexp_release (s_sig); + return gpg_error (GPG_ERR_BUG); + } + rc = gcry_sexp_sscan ( &s_pkey, NULL, p, n); + ksba_free (p); + if (rc) + { + log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); + gcry_md_close (md); + gcry_sexp_release (s_sig); + return rc; + } + + rc = do_encode_md (md, algo, gcry_pk_get_nbits (s_pkey), &frame); + if (rc) + { + gcry_md_close (md); + gcry_sexp_release (s_sig); + gcry_sexp_release (s_pkey); + return rc; + } + + /* put hash into the S-Exp s_hash */ + if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) ) + BUG (); + gcry_mpi_release (frame); + + + rc = gcry_pk_verify (s_sig, s_hash, s_pkey); + if (DBG_CRYPTO) + log_debug ("gcry_pk_verify: %s\n", gpg_strerror (rc)); + gcry_md_close (md); + gcry_sexp_release (s_sig); + gcry_sexp_release (s_hash); + gcry_sexp_release (s_pkey); + return rc; +} + + + +int +gpgsm_check_cms_signature (KsbaCert cert, KsbaConstSexp sigval, + gcry_md_hd_t md, int algo) +{ + int rc; + KsbaSexp p; + gcry_mpi_t frame; + gcry_sexp_t s_sig, s_hash, s_pkey; + size_t n; + + n = gcry_sexp_canon_len (sigval, 0, NULL, NULL); + if (!n) + { + log_error ("libksba did not return a proper S-Exp\n"); + return gpg_error (GPG_ERR_BUG); + } + rc = gcry_sexp_sscan (&s_sig, NULL, sigval, n); + if (rc) + { + log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); + return rc; + } + + p = ksba_cert_get_public_key (cert); + n = gcry_sexp_canon_len (p, 0, NULL, NULL); + if (!n) + { + log_error ("libksba did not return a proper S-Exp\n"); + ksba_free (p); + gcry_sexp_release (s_sig); + return gpg_error (GPG_ERR_BUG); + } + if (DBG_X509) + log_printhex ("public key: ", p, n); + + rc = gcry_sexp_sscan ( &s_pkey, NULL, p, n); + ksba_free (p); + if (rc) + { + log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); + gcry_sexp_release (s_sig); + return rc; + } + + + rc = do_encode_md (md, algo, gcry_pk_get_nbits (s_pkey), &frame); + if (rc) + { + gcry_sexp_release (s_sig); + gcry_sexp_release (s_pkey); + return rc; + } + /* put hash into the S-Exp s_hash */ + if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) ) + BUG (); + gcry_mpi_release (frame); + + rc = gcry_pk_verify (s_sig, s_hash, s_pkey); + if (DBG_CRYPTO) + log_debug ("gcry_pk_verify: %s\n", gpg_strerror (rc)); + gcry_sexp_release (s_sig); + gcry_sexp_release (s_hash); + gcry_sexp_release (s_pkey); + return rc; +} + + + +int +gpgsm_create_cms_signature (KsbaCert cert, gcry_md_hd_t md, int mdalgo, + char **r_sigval) +{ + int rc; + char *grip; + size_t siglen; + + grip = gpgsm_get_keygrip_hexstring (cert); + if (!grip) + return gpg_error (GPG_ERR_BAD_CERT); + + rc = gpgsm_agent_pksign (grip, gcry_md_read(md, mdalgo), + gcry_md_get_algo_dlen (mdalgo), mdalgo, + r_sigval, &siglen); + xfree (grip); + return rc; +} + + + diff --git a/sm/certdump.c b/sm/certdump.c new file mode 100644 index 000000000..703e07186 --- /dev/null +++ b/sm/certdump.c @@ -0,0 +1,457 @@ +/* certdump.c - Dump a certificate for debugging + * Copyright (C) 2001 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 +#include + +#include "gpgsm.h" +#include +#include + +#include "keydb.h" +#include "i18n.h" + +struct dn_array_s { + char *key; + char *value; +}; + + +/* print the first element of an S-Expression */ +void +gpgsm_print_serial (FILE *fp, KsbaConstSexp p) +{ + unsigned long n; + KsbaConstSexp endp; + + if (!p) + fputs (_("none"), fp); + else if (*p != '(') + fputs ("[Internal error - not an S-expression]", fp); + else + { + p++; + n = strtoul (p, (char**)&endp, 10); + p = endp; + if (*p!=':') + fputs ("[Internal Error - invalid S-expression]", fp); + else + { + for (p++; n; n--, p++) + fprintf (fp, "%02X", *p); + } + } +} + + +void +gpgsm_dump_serial (KsbaConstSexp p) +{ + unsigned long n; + KsbaConstSexp endp; + + if (!p) + log_printf ("none"); + else if (*p != '(') + log_printf ("ERROR - not an S-expression"); + else + { + p++; + n = strtoul (p, (char**)&endp, 10); + p = endp; + if (*p!=':') + log_printf ("ERROR - invalid S-expression"); + else + { + for (p++; n; n--, p++) + log_printf ("%02X", *p); + } + } +} + +void +gpgsm_print_time (FILE *fp, time_t t) +{ + if (!t) + fputs (_("none"), fp); + else if ( t == (time_t)(-1) ) + fputs ("[Error - Invalid time]", fp); + else + { + struct tm *tp; + + tp = gmtime (&t); + fprintf (fp, "%04d-%02d-%02d %02d:%02d:%02d Z", + 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday, + tp->tm_hour, tp->tm_min, tp->tm_sec); + assert (!tp->tm_isdst); + } +} + +void +gpgsm_dump_time (time_t t) +{ + + if (!t) + log_printf (_("[none]")); + else if ( t == (time_t)(-1) ) + log_printf (_("[error]")); + else + { + struct tm *tp; + + tp = gmtime (&t); + log_printf ("%04d-%02d-%02d %02d:%02d:%02d", + 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday, + tp->tm_hour, tp->tm_min, tp->tm_sec); + assert (!tp->tm_isdst); + } +} + + + + +void +gpgsm_dump_string (const char *string) +{ + + if (!string) + log_printf ("[error]"); + else + { + const unsigned char *s; + + for (s=string; *s; s++) + { + if (*s < ' ' || (*s >= 0x7f && *s <= 0xa0)) + break; + } + if (!*s && *string != '[') + log_printf ("%s", string); + else + { + log_printf ( "[ "); + log_printhex (NULL, string, strlen (string)); + log_printf ( " ]"); + } + } +} + + +void +gpgsm_dump_cert (const char *text, KsbaCert cert) +{ + KsbaSexp sexp; + unsigned char *p; + char *dn; + time_t t; + + log_debug ("BEGIN Certificate `%s':\n", text? text:""); + if (cert) + { + sexp = ksba_cert_get_serial (cert); + log_debug (" serial: "); + gpgsm_dump_serial (sexp); + ksba_free (sexp); + log_printf ("\n"); + + t = ksba_cert_get_validity (cert, 0); + log_debug (" notBefore: "); + gpgsm_dump_time (t); + log_printf ("\n"); + t = ksba_cert_get_validity (cert, 1); + log_debug (" notAfter: "); + gpgsm_dump_time (t); + log_printf ("\n"); + + dn = ksba_cert_get_issuer (cert, 0); + log_debug (" issuer: "); + gpgsm_dump_string (dn); + ksba_free (dn); + log_printf ("\n"); + + dn = ksba_cert_get_subject (cert, 0); + log_debug (" subject: "); + gpgsm_dump_string (dn); + ksba_free (dn); + log_printf ("\n"); + + log_debug (" hash algo: %s\n", ksba_cert_get_digest_algo (cert)); + + p = gpgsm_get_fingerprint_string (cert, 0); + log_debug (" SHA1 Fingerprint: %s\n", p); + xfree (p); + } + log_debug ("END Certificate\n"); +} + + + +/* helper for the rfc2253 string parser */ +static const unsigned char * +parse_dn_part (struct dn_array_s *array, const unsigned char *string) +{ + const unsigned char *s, *s1; + size_t n; + unsigned char *p; + + /* parse attributeType */ + for (s = string+1; *s && *s != '='; s++) + ; + if (!*s) + return NULL; /* error */ + n = s - string; + if (!n) + return NULL; /* empty key */ + array->key = p = xtrymalloc (n+1); + if (!array->key) + return NULL; + memcpy (p, string, n); + p[n] = 0; + trim_trailing_spaces (p); + if ( !strcmp (p, "1.2.840.113549.1.9.1") ) + strcpy (p, "EMail"); + string = s + 1; + + if (*string == '#') + { /* hexstring */ + string++; + for (s=string; hexdigitp (s); s++) + s++; + n = s - string; + if (!n || (n & 1)) + return NULL; /* empty or odd number of digits */ + n /= 2; + array->value = p = xtrymalloc (n+1); + if (!p) + return NULL; + for (s1=string; n; s1 += 2, n--) + *p++ = xtoi_2 (s1); + *p = 0; + } + else + { /* regular v3 quoted string */ + for (n=0, s=string; *s; s++) + { + if (*s == '\\') + { /* pair */ + s++; + if (*s == ',' || *s == '=' || *s == '+' + || *s == '<' || *s == '>' || *s == '#' || *s == ';' + || *s == '\\' || *s == '\"' || *s == ' ') + n++; + else if (hexdigitp (s) && hexdigitp (s+1)) + { + s++; + n++; + } + else + return NULL; /* invalid escape sequence */ + } + else if (*s == '\"') + return NULL; /* invalid encoding */ + else if (*s == ',' || *s == '=' || *s == '+' + || *s == '<' || *s == '>' || *s == '#' || *s == ';' ) + break; + else + n++; + } + + array->value = p = xtrymalloc (n+1); + if (!p) + return NULL; + for (s=string; n; s++, n--) + { + if (*s == '\\') + { + s++; + if (hexdigitp (s)) + { + *p++ = xtoi_2 (s); + s++; + } + else + *p++ = *s; + } + else + *p++ = *s; + } + *p = 0; + } + return s; +} + + +/* Parse a DN and return an array-ized one. This is not a validating + parser and it does not support any old-stylish syntax; KSBA is + expected to return only rfc2253 compatible strings. */ +static struct dn_array_s * +parse_dn (const unsigned char *string) +{ + struct dn_array_s *array; + size_t arrayidx, arraysize; + int i; + + arraysize = 7; /* C,ST,L,O,OU,CN,email */ + arrayidx = 0; + array = xtrymalloc ((arraysize+1) * sizeof *array); + if (!array) + return NULL; + while (*string) + { + while (*string == ' ') + string++; + if (!*string) + break; /* ready */ + if (arrayidx >= arraysize) + { + struct dn_array_s *a2; + + arraysize += 5; + a2 = xtryrealloc (array, (arraysize+1) * sizeof *array); + if (!a2) + goto failure; + array = a2; + } + array[arrayidx].key = NULL; + array[arrayidx].value = NULL; + string = parse_dn_part (array+arrayidx, string); + arrayidx++; + if (!string) + goto failure; + while (*string == ' ') + string++; + if (*string && *string != ',' && *string != ';' && *string != '+') + goto failure; /* invalid delimiter */ + if (*string) + string++; + } + array[arrayidx].key = NULL; + array[arrayidx].value = NULL; + return array; + + failure: + for (i=0; i < arrayidx; i++) + { + xfree (array[i].key); + xfree (array[i].value); + } + xfree (array); + return NULL; +} + + +static void +print_dn_part (FILE *fp, struct dn_array_s *dn, const char *key) +{ + int any = 0; + + for (; dn->key; dn++) + { + if (!strcmp (dn->key, key) && dn->value && *dn->value) + { + putc ('/', fp); + if (any) + fputs (" + ", fp); + else + fprintf (fp, "%s=", key); + print_sanitized_utf8_string (fp, dn->value, '/'); + any = 1; + } + } +} + +/* Print all parts of a DN in a "standard" sequence. We first print + all the known parts, followed by the uncommon ones */ +static void +print_dn_parts (FILE *fp, struct dn_array_s *dn) +{ + const char *stdpart[] = { + "CN", "OU", "O", "STREET", "L", "ST", "C", "EMail", NULL + }; + int i; + + for (i=0; stdpart[i]; i++) + print_dn_part (fp, dn, stdpart[i]); + + /* now print the rest without any specific ordering */ + for (; dn->key; dn++) + { + for (i=0; stdpart[i]; i++) + { + if (!strcmp (dn->key, stdpart[i])) + break; + } + if (!stdpart[i]) + print_dn_part (fp, dn, dn->key); + } +} + + + +void +gpgsm_print_name (FILE *fp, const char *name) +{ + const unsigned char *s; + int i; + + s = name; + if (!s) + { + fputs (_("[Error - No name]"), fp); + } + else if (*s == '<') + { + const unsigned char *s2 = strchr (s+1, '>'); + if (s2) + print_sanitized_utf8_buffer (fp, s + 1, s2 - s - 1, 0); + } + else if (*s == '(') + fputs (_("[Error - unknown encoding]"), fp); + else if (!((*s >= '0' && *s < '9') + || (*s >= 'A' && *s <= 'Z') + || (*s >= 'a' && *s <= 'z'))) + fputs (_("[Error - invalid encoding]"), fp); + else + { + struct dn_array_s *dn = parse_dn (s); + if (!dn) + fputs (_("[Error - invalid DN]"), fp); + else + { + print_dn_parts (fp, dn); + for (i=0; dn[i].key; i++) + { + xfree (dn[i].key); + xfree (dn[i].value); + } + xfree (dn); + } + } +} + + + diff --git a/sm/certlist.c b/sm/certlist.c new file mode 100644 index 000000000..eedc99025 --- /dev/null +++ b/sm/certlist.c @@ -0,0 +1,315 @@ +/* certlist.c - build list of certificates + * Copyright (C) 2001, 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 +#include + +#include "gpgsm.h" +#include +#include + +#include "keydb.h" +#include "i18n.h" + +/* Return 0 if the cert is usable for encryption. A MODE of 0 checks + for signing a MODE of 1 checks for encryption, a MODE of 2 checks + for verification and a MODE of 3 for decryption (just for + debugging) */ +static int +cert_usage_p (KsbaCert cert, int mode) +{ + KsbaError err; + unsigned int use; + + err = ksba_cert_get_key_usage (cert, &use); + if (err == KSBA_No_Data) + { + if (opt.verbose && mode < 2) + log_info (mode? + _("no key usage specified - accepted for encryption\n"): + _("no key usage specified - accepted for signing\n")); + return 0; + } + if (err) + { + log_error (_("error getting key usage information: %s\n"), + ksba_strerror (err)); + return map_ksba_err (err); + } + + if (mode == 4) + { + if ((use & (KSBA_KEYUSAGE_KEY_CERT_SIGN))) + return 0; + log_info ( _("certificate should have not been used certification\n")); + return gpg_error (GPG_ERR_WRONG_KEY_USAGE); + } + + if ((use & ((mode&1)? + (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT): + (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) + ) + return 0; + log_info (mode==3? _("certificate should have not been used for encryption\n"): + mode==2? _("certificate should have not been used for signing\n"): + mode==1? _("certificate is not usable for encryption\n"): + _("certificate is not usable for signing\n")); + return gpg_error (GPG_ERR_WRONG_KEY_USAGE); +} + + +/* Return 0 if the cert is usable for signing */ +int +gpgsm_cert_use_sign_p (KsbaCert cert) +{ + return cert_usage_p (cert, 0); +} + + +/* Return 0 if the cert is usable for encryption */ +int +gpgsm_cert_use_encrypt_p (KsbaCert cert) +{ + return cert_usage_p (cert, 1); +} + +int +gpgsm_cert_use_verify_p (KsbaCert cert) +{ + return cert_usage_p (cert, 2); +} + +int +gpgsm_cert_use_decrypt_p (KsbaCert cert) +{ + return cert_usage_p (cert, 3); +} + +int +gpgsm_cert_use_cert_p (KsbaCert cert) +{ + return cert_usage_p (cert, 4); +} + + +static int +same_subject_issuer (const char *subject, const char *issuer, KsbaCert cert) +{ + char *subject2 = ksba_cert_get_subject (cert, 0); + char *issuer2 = ksba_cert_get_subject (cert, 0); + int tmp; + + tmp = (subject && subject2 + && !strcmp (subject, subject2) + && issuer && issuer2 + && !strcmp (issuer, issuer2)); + xfree (subject2); + xfree (issuer2); + return tmp; +} + + + +/* Add a certificate to a list of certificate and make sure that it is + a valid certificate. With SECRET set to true a secret key must be + avaibale for the certificate. */ +int +gpgsm_add_to_certlist (CTRL ctrl, const char *name, int secret, + CERTLIST *listaddr) +{ + int rc; + KEYDB_SEARCH_DESC desc; + KEYDB_HANDLE kh = NULL; + KsbaCert cert = NULL; + + rc = keydb_classify_name (name, &desc); + if (!rc) + { + kh = keydb_new (0); + if (!kh) + rc = gpg_error (GPG_ERR_ENOMEM); + else + { + int wrong_usage = 0; + char *subject = NULL; + char *issuer = NULL; + + get_next: + rc = keydb_search (kh, &desc, 1); + if (!rc) + rc = keydb_get_cert (kh, &cert); + if (!rc) + { + rc = secret? gpgsm_cert_use_sign_p (cert) + : gpgsm_cert_use_encrypt_p (cert); + if (gpg_err_code (rc) == GPG_ERR_WRONG_KEY_USAGE) + { + /* There might be another certificate with the + correct usage, so we try again */ + if (!wrong_usage) + { /* save the first match */ + wrong_usage = rc; + subject = ksba_cert_get_subject (cert, 0); + issuer = ksba_cert_get_subject (cert, 0); + ksba_cert_release (cert); + cert = NULL; + goto get_next; + } + else if (same_subject_issuer (subject, issuer, cert)) + { + wrong_usage = rc; + ksba_cert_release (cert); + cert = NULL; + goto get_next; + } + else + wrong_usage = rc; + + } + } + /* we want the error code from the first match in this case */ + if (rc && wrong_usage) + rc = wrong_usage; + + if (!rc) + { + next_ambigious: + rc = keydb_search (kh, &desc, 1); + if (rc == -1) + rc = 0; + else if (!rc) + { + KsbaCert cert2 = NULL; + + /* We have to ignore ambigious names as long as + there only fault is a bad key usage */ + if (!keydb_get_cert (kh, &cert2)) + { + int tmp = (same_subject_issuer (subject, issuer, cert2) + && ((gpg_err_code ( + secret? gpgsm_cert_use_sign_p (cert2) + : gpgsm_cert_use_encrypt_p (cert2) + ) + ) == GPG_ERR_WRONG_KEY_USAGE)); + ksba_cert_release (cert2); + if (tmp) + goto next_ambigious; + } + rc = gpg_error (GPG_ERR_AMBIGUOUS_NAME); + } + } + xfree (subject); + xfree (issuer); + + if (!rc && secret) + { + char *p; + + rc = gpg_error (GPG_ERR_NO_SECKEY); + p = gpgsm_get_keygrip_hexstring (cert); + if (p) + { + if (!gpgsm_agent_havekey (p)) + rc = 0; + xfree (p); + } + } + if (!rc) + rc = gpgsm_validate_chain (ctrl, cert, NULL); + if (!rc) + { + CERTLIST cl = xtrycalloc (1, sizeof *cl); + if (!cl) + rc = OUT_OF_CORE (errno); + else + { + cl->cert = cert; cert = NULL; + cl->next = *listaddr; + *listaddr = cl; + } + } + } + } + + keydb_release (kh); + ksba_cert_release (cert); + return rc == -1? gpg_error (GPG_ERR_NO_PUBKEY): rc; +} + +void +gpgsm_release_certlist (CERTLIST list) +{ + while (list) + { + CERTLIST cl = list->next; + ksba_cert_release (list->cert); + xfree (list); + list = cl; + } +} + + +/* Like gpgsm_add_to_certlist, but look only for one certificate. No + chain validation is done */ +int +gpgsm_find_cert (const char *name, KsbaCert *r_cert) +{ + int rc; + KEYDB_SEARCH_DESC desc; + KEYDB_HANDLE kh = NULL; + + *r_cert = NULL; + rc = keydb_classify_name (name, &desc); + if (!rc) + { + kh = keydb_new (0); + if (!kh) + rc = gpg_error (GPG_ERR_ENOMEM); + else + { + rc = keydb_search (kh, &desc, 1); + if (!rc) + rc = keydb_get_cert (kh, r_cert); + if (!rc) + { + rc = keydb_search (kh, &desc, 1); + if (rc == -1) + rc = 0; + else + { + if (!rc) + rc = gpg_error (GPG_ERR_AMBIGUOUS_NAME); + ksba_cert_release (*r_cert); + *r_cert = NULL; + } + } + } + } + + keydb_release (kh); + return rc == -1? gpg_error (GPG_ERR_NO_PUBKEY): rc; +} + diff --git a/sm/certreqgen.c b/sm/certreqgen.c new file mode 100644 index 000000000..0dd4fdde9 --- /dev/null +++ b/sm/certreqgen.c @@ -0,0 +1,699 @@ +/* certreqgen.c - Generate a key and a certification request + * Copyright (C) 2002, 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 + */ + +/* +The format of the native parameter file is follows: + o Text only, line length is limited to about 1000 chars. + o You must use UTF-8 encoding to specify non-ascii characters. + o Empty lines are ignored. + o Leading and trailing spaces are ignored. + o A hash sign as the first non white space character is a comment line. + o Control statements are indicated by a leading percent sign, the + arguments are separated by white space from the keyword. + o Parameters are specified by a keyword, followed by a colon. Arguments + are separated by white space. + o The first parameter must be "Key-Type", control statements + may be placed anywhere. + o Key generation takes place when either the end of the parameter file + is reached, the next "Key-Type" parameter is encountered or at the + controlstatement "%commit" + o Control statements: + %echo + Print . + %dry-run + Suppress actual key generation (useful for syntax checking). + %commit + Perform the key generation. Note that an implicit commit is done + at the next "Key-Type" parameter. + %certfile + Do not write the certificate to the keyDB but to . + This must be given before the first + commit to take place, duplicate specification of the same filename + is ignored, the last filename before a commit is used. + The filename is used until a new filename is used (at commit points) + and all keys are written to that file. If a new filename is given, + this file is created (and overwrites an existing one). + Both control statements must be given. + o The order of the parameters does not matter except for "Key-Type" + which must be the first parameter. The parameters are only for the + generated keyblock and parameters from previous key generations are not + used. Some syntactically checks may be performed. + The currently defined parameters are: + Key-Type: + Starts a new parameter block by giving the type of the + primary key. The algorithm must be capable of signing. + This is a required parameter. For now the only supported + algorithm is "rsa". + Key-Length: + Length of the key in bits. Default is 1024. + Key-Usage: + Space or comma delimited list of key usage, allowed values are + "encrypt" and "sign". This is used to generate the KeyUsage extension. + Please make sure that the algorithm is capable of this usage. Default + is to allow encrypt and sign. + Name-DN: subject name + This is the DN name of the subject in rfc2253 format. + Name-Email: + The ist the email address + +Here is an example: +$ cat >foo < +#include +#include +#include +#include +#include +#include +#include + +#include "gpgsm.h" +#include +#include + +#include "keydb.h" +#include "i18n.h" + + +enum para_name { + pKEYTYPE, + pKEYLENGTH, + pKEYUSAGE, + pNAMEDN, + pNAMEEMAIL +}; + +struct para_data_s { + struct para_data_s *next; + int lnr; + enum para_name key; + union { + unsigned int usage; + char value[1]; + } u; +}; + +struct reqgen_ctrl_s { + int lnr; + int dryrun; + KsbaWriter writer; +}; + + +static int proc_parameters (struct para_data_s *para, + struct reqgen_ctrl_s *outctrl); +static int create_request (struct para_data_s *para, + KsbaConstSexp public, + struct reqgen_ctrl_s *outctrl); + + + +static void +release_parameter_list (struct para_data_s *r) +{ + struct para_data_s *r2; + + for (; r ; r = r2) + { + r2 = r->next; + xfree(r); + } +} + +static struct para_data_s * +get_parameter (struct para_data_s *para, enum para_name key) +{ + struct para_data_s *r; + + for (r = para; r && r->key != key; r = r->next) + ; + return r; +} + +static const char * +get_parameter_value (struct para_data_s *para, enum para_name key) +{ + struct para_data_s *r = get_parameter (para, key); + return (r && *r->u.value)? r->u.value : NULL; +} + +static int +get_parameter_algo (struct para_data_s *para, enum para_name key) +{ + struct para_data_s *r = get_parameter (para, key); + if (!r) + return -1; + if (digitp (r->u.value)) + return atoi( r->u.value ); + return gcry_pk_map_name (r->u.value); +} + +/* parse the usage parameter. Returns 0 on success. Note that we + only care about sign and encrypt and don't (yet) allow all the + other X.509 usage to be specified; instead we will use a fixed + mapping to the X.509 usage flags */ +static int +parse_parameter_usage (struct para_data_s *para, enum para_name key) +{ + struct para_data_s *r = get_parameter (para, key); + char *p, *pn; + unsigned int use; + + if (!r) + return 0; /* none (this is an optional parameter)*/ + + use = 0; + pn = r->u.value; + while ( (p = strsep (&pn, " \t,")) ) + { + if (!*p) + ; + else if ( !ascii_strcasecmp (p, "sign") ) + use |= GCRY_PK_USAGE_SIGN; + else if ( !ascii_strcasecmp (p, "encrypt") ) + use |= GCRY_PK_USAGE_ENCR; + else + { + log_error ("line %d: invalid usage list\n", r->lnr); + return -1; /* error */ + } + } + r->u.usage = use; + return 0; +} + + +static unsigned int +get_parameter_uint (struct para_data_s *para, enum para_name key) +{ + struct para_data_s *r = get_parameter (para, key); + + if (!r) + return 0; + + return (unsigned int)strtoul (r->u.value, NULL, 10); +} + + + +/* Read the certificate generation parameters from FP and generate + (all) certificate requests. */ +static int +read_parameters (FILE *fp, KsbaWriter writer) +{ + static struct { + const char *name; + enum para_name key; + } keywords[] = { + { "Key-Type", pKEYTYPE}, + { "Key-Length", pKEYLENGTH }, + { "Key-Usage", pKEYUSAGE }, + { "Name-DN", pNAMEDN }, + { "Name-Email", pNAMEEMAIL }, + { NULL, 0 } + }; + char line[1024], *p; + const char *err = NULL; + struct para_data_s *para, *r; + int i, rc = 0, any = 0; + struct reqgen_ctrl_s outctrl; + + memset (&outctrl, 0, sizeof (outctrl)); + outctrl.writer = writer; + + err = NULL; + para = NULL; + while (fgets (line, DIM(line)-1, fp) ) + { + char *keyword, *value; + + outctrl.lnr++; + if (*line && line[strlen(line)-1] != '\n') + { + err = "line too long"; + break; + } + for (p=line; spacep (p); p++) + ; + if (!*p || *p == '#') + continue; + + keyword = p; + if (*keyword == '%') + { + for (; !spacep (p); p++) + ; + if (*p) + *p++ = 0; + for (; spacep (p); p++) + ; + value = p; + trim_trailing_spaces (value); + + if (!ascii_strcasecmp (keyword, "%echo")) + log_info ("%s\n", value); + else if (!ascii_strcasecmp (keyword, "%dry-run")) + outctrl.dryrun = 1; + else if (!ascii_strcasecmp( keyword, "%commit")) + { + rc = proc_parameters (para, &outctrl); + if (rc) + goto leave; + any = 1; + release_parameter_list (para); + para = NULL; + } + else + log_info ("skipping control `%s' (%s)\n", keyword, value); + + continue; + } + + + if (!(p = strchr (p, ':')) || p == keyword) + { + err = "missing colon"; + break; + } + if (*p) + *p++ = 0; + for (; spacep (p); p++) + ; + if (!*p) + { + err = "missing argument"; + break; + } + value = p; + trim_trailing_spaces (value); + + for (i=0; (keywords[i].name + && ascii_strcasecmp (keywords[i].name, keyword)); i++) + ; + if (!keywords[i].name) + { + err = "unknown keyword"; + break; + } + if (keywords[i].key != pKEYTYPE && !para) + { + err = "parameter block does not start with \"Key-Type\""; + break; + } + + if (keywords[i].key == pKEYTYPE && para) + { + rc = proc_parameters (para, &outctrl); + if (rc) + goto leave; + any = 1; + release_parameter_list (para); + para = NULL; + } + else + { + for (r = para; r && r->key != keywords[i].key; r = r->next) + ; + if (r) + { + err = "duplicate keyword"; + break; + } + } + + r = xtrycalloc (1, sizeof *r + strlen( value )); + if (!r) + { + err = "out of core"; + break; + } + r->lnr = outctrl.lnr; + r->key = keywords[i].key; + strcpy (r->u.value, value); + r->next = para; + para = r; + } + + if (err) + { + log_error ("line %d: %s\n", outctrl.lnr, err); + rc = gpg_error (GPG_ERR_GENERAL); + } + else if (ferror(fp)) + { + log_error ("line %d: read error: %s\n", outctrl.lnr, strerror(errno) ); + rc = gpg_error (GPG_ERR_GENERAL); + } + else if (para) + { + rc = proc_parameters (para, &outctrl); + if (rc) + goto leave; + any = 1; + } + + if (!rc && !any) + rc = gpg_error (GPG_ERR_NO_DATA); + + leave: + release_parameter_list (para); + return rc; +} + +/* check whether there are invalid characters in the email address S */ +static int +has_invalid_email_chars (const char *s) +{ + int at_seen=0; + static char valid_chars[] = "01234567890_-." + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + for (; *s; s++) + { + if (*s & 0x80) + return 1; + if (*s == '@') + at_seen++; + else if (!at_seen && !( !!strchr (valid_chars, *s) || *s == '+')) + return 1; + else if (at_seen && !strchr (valid_chars, *s)) + return 1; + } + return at_seen != 1; +} + + +/* Check that all required parameters are given and perform the action */ +static int +proc_parameters (struct para_data_s *para, struct reqgen_ctrl_s *outctrl) +{ + struct para_data_s *r; + const char *s; + int i; + unsigned int nbits; + char numbuf[20]; + unsigned char keyparms[100]; + int rc; + KsbaSexp public; + + /* check that we have all required parameters */ + assert (get_parameter (para, pKEYTYPE)); + + /* We can only use RSA for now. There is a with pkcs-10 on how to + use ElGamal becuase it is expected that a PK algorithm can always + be used for signing. */ + i = get_parameter_algo (para, pKEYTYPE); + if (i < 1 || i != GCRY_PK_RSA ) + { + r = get_parameter (para, pKEYTYPE); + log_error ("line %d: invalid algorithm\n", r->lnr); + return gpg_error (GPG_ERR_INV_PARAMETER); + } + + /* check the keylength */ + if (!get_parameter (para, pKEYLENGTH)) + nbits = 1024; + else + nbits = get_parameter_uint (para, pKEYLENGTH); + if (nbits < 512 || nbits > 4096) + { + r = get_parameter (para, pKEYTYPE); + log_error ("line %d: invalid key length %u (valid are 512 to 4096)\n", + r->lnr, nbits); + return gpg_error (GPG_ERR_INV_PARAMETER); + } + + /* check the usage */ + if (parse_parameter_usage (para, pKEYUSAGE)) + return gpg_error (GPG_ERR_INV_PARAMETER); + + /* check that there is a subject name and that this DN fits our + requirements */ + if (!(s=get_parameter_value (para, pNAMEDN))) + { + r = get_parameter (para, pKEYTYPE); + log_error ("line %d: no subject name given\n", r->lnr); + return gpg_error (GPG_ERR_INV_PARAMETER); + } + /* fixme check s */ + + /* check that the optional email address is okay */ + if ((s=get_parameter_value (para, pNAMEEMAIL))) + { + if (has_invalid_email_chars (s) + || *s == '@' + || s[strlen(s)-1] == '@' + || s[strlen(s)-1] == '.' + || strstr(s, "..")) + { + r = get_parameter (para, pKEYTYPE); + log_error ("line %d: not a valid email address\n", r->lnr); + return gpg_error (GPG_ERR_INV_PARAMETER); + } + } + + sprintf (numbuf, "%u", nbits); + snprintf (keyparms, DIM (keyparms)-1, + "(6:genkey(3:rsa(5:nbits%d:%s)))", strlen (numbuf), numbuf); + rc = gpgsm_agent_genkey (keyparms, &public); + if (rc) + { + r = get_parameter (para, pKEYTYPE); + log_error ("line %d: key generation failed: %s\n", + r->lnr, gpg_strerror (rc)); + return rc; + } + + rc = create_request (para, public, outctrl); + xfree (public); + + return rc; +} + + +/* Parameters are checked, the key pair has been created. Now + generate the request and write it out */ +static int +create_request (struct para_data_s *para, KsbaConstSexp public, + struct reqgen_ctrl_s *outctrl) +{ + KsbaCertreq cr; + KsbaError err; + gcry_md_hd_t md; + KsbaStopReason stopreason; + int rc = 0; + const char *s; + + cr = ksba_certreq_new (); + if (!cr) + return gpg_error (GPG_ERR_ENOMEM); + + rc = gcry_md_open (&md, GCRY_MD_SHA1, 0); + if (rc) + { + log_error ("md_open failed: %s\n", gpg_strerror (rc)); + goto leave; + } + if (DBG_HASHING) + gcry_md_start_debug (md, "cr.cri"); + + ksba_certreq_set_hash_function (cr, HASH_FNC, md); + ksba_certreq_set_writer (cr, outctrl->writer); + + err = ksba_certreq_add_subject (cr, get_parameter_value (para, pNAMEDN)); + if (err) + { + log_error ("error setting the subject's name: %s\n", + ksba_strerror (err)); + rc = map_ksba_err (err); + goto leave; + } + + s = get_parameter_value (para, pNAMEEMAIL); + if (s) + { + char *buf; + + buf = xtrymalloc (strlen (s) + 3); + if (!buf) + { + rc = OUT_OF_CORE (errno); + goto leave; + } + *buf = '<'; + strcpy (buf+1, s); + strcat (buf+1, ">"); + err = ksba_certreq_add_subject (cr, buf); + xfree (buf); + if (err) + { + log_error ("error setting the subject's alternate name: %s\n", + ksba_strerror (err)); + rc = map_ksba_err (err); + goto leave; + } + } + + + err = ksba_certreq_set_public_key (cr, public); + if (err) + { + log_error ("error setting the public key: %s\n", + ksba_strerror (err)); + rc = map_ksba_err (err); + goto leave; + } + + do + { + err = ksba_certreq_build (cr, &stopreason); + if (err) + { + log_error ("ksba_certreq_build failed: %s\n", ksba_strerror (err)); + rc = map_ksba_err (err); + goto leave; + } + if (stopreason == KSBA_SR_NEED_SIG) + { + gcry_sexp_t s_pkey; + size_t n; + unsigned char grip[20], hexgrip[41]; + char *sigval; + size_t siglen; + + n = gcry_sexp_canon_len (public, 0, NULL, NULL); + if (!n) + { + log_error ("libksba did not return a proper S-Exp\n"); + err = gpg_error (GPG_ERR_BUG); + goto leave; + } + rc = gcry_sexp_sscan (&s_pkey, NULL, public, n); + if (rc) + { + log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); + goto leave; + } + if ( !gcry_pk_get_keygrip (s_pkey, grip) ) + { + rc = gpg_error (GPG_ERR_GENERAL); + log_error ("can't figure out the keygrip\n"); + gcry_sexp_release (s_pkey); + goto leave; + } + gcry_sexp_release (s_pkey); + for (n=0; n < 20; n++) + sprintf (hexgrip+n*2, "%02X", grip[n]); + + rc = gpgsm_agent_pksign (hexgrip, + gcry_md_read(md, GCRY_MD_SHA1), + gcry_md_get_algo_dlen (GCRY_MD_SHA1), + GCRY_MD_SHA1, + &sigval, &siglen); + if (rc) + { + log_error ("signing failed: %s\n", gpg_strerror (rc)); + goto leave; + } + + err = ksba_certreq_set_sig_val (cr, sigval); + xfree (sigval); + if (err) + { + log_error ("failed to store the sig_val: %s\n", + ksba_strerror (err)); + rc = map_ksba_err (err); + goto leave; + } + } + } + while (stopreason != KSBA_SR_READY); + + + leave: + gcry_md_close (md); + ksba_certreq_release (cr); + return rc; +} + + + +/* Create a new key by reading the parameters from in_fd. Multiple + keys may be created */ +int +gpgsm_genkey (CTRL ctrl, int in_fd, FILE *out_fp) +{ + int rc; + FILE *in_fp; + Base64Context b64writer = NULL; + KsbaWriter writer; + + in_fp = fdopen (dup (in_fd), "rb"); + if (!in_fp) + { + gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); + log_error ("fdopen() failed: %s\n", strerror (errno)); + return tmperr; + } + + ctrl->pem_name = "NEW CERTIFICATE REQUEST"; + rc = gpgsm_create_writer (&b64writer, ctrl, out_fp, &writer); + if (rc) + { + log_error ("can't create writer: %s\n", gpg_strerror (rc)); + goto leave; + } + + rc = read_parameters (in_fp, writer); + if (rc) + { + log_error ("error creating certificate request: %s\n", + gpg_strerror (rc)); + goto leave; + } + + rc = gpgsm_finish_writer (b64writer); + if (rc) + { + log_error ("write failed: %s\n", gpg_strerror (rc)); + goto leave; + } + + gpgsm_status (ctrl, STATUS_KEY_CREATED, "P"); + log_info ("certificate request created\n"); + + leave: + gpgsm_destroy_writer (b64writer); + fclose (in_fp); + return rc; +} + diff --git a/sm/decrypt.c b/sm/decrypt.c new file mode 100644 index 000000000..17483aa49 --- /dev/null +++ b/sm/decrypt.c @@ -0,0 +1,506 @@ +/* decrypt.c - Decrypt a message + * Copyright (C) 2001, 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 +#include + +#include "gpgsm.h" +#include +#include + +#include "keydb.h" +#include "i18n.h" + +struct decrypt_filter_parm_s { + int algo; + int mode; + int blklen; + gcry_cipher_hd_t hd; + char iv[16]; + size_t ivlen; + int any_data; /* dod we push anything through the filter at all? */ + unsigned char lastblock[16]; /* to strip the padding we have to + keep this one */ + char helpblock[16]; /* needed because there is no block buffering in + libgcrypt (yet) */ + int helpblocklen; +}; + + + +/* decrypt the session key and fill in the parm structure. The + algo and the IV is expected to be already in PARM. */ +static int +prepare_decryption (const char *hexkeygrip, KsbaConstSexp enc_val, + struct decrypt_filter_parm_s *parm) +{ + char *seskey = NULL; + size_t n, seskeylen; + int rc; + + rc = gpgsm_agent_pkdecrypt (hexkeygrip, enc_val, + &seskey, &seskeylen); + if (rc) + { + log_error ("error decrypting session key: %s\n", gpg_strerror (rc)); + goto leave; + } + + if (DBG_CRYPTO) + log_printhex ("pkcs1 encoded session key:", seskey, seskeylen); + + n=0; + if (seskeylen == 24) + { + /* Smells like a 3-des key. This might happen because a SC has + already done the unpacking. fixme! */ + } + else + { + if (n + 7 > seskeylen ) + { + rc = gpg_error (GPG_ERR_INV_SESSION_KEY); + goto leave; + } + + /* FIXME: Actually the leading zero is required but due to the way + we encode the output in libgcrypt as an MPI we are not able to + encode that leading zero. However, when using a Smartcard we are + doing it the rightway and therefore we have to skip the zero. This + should be fixed in gpg-agent of course. */ + if (!seskey[n]) + n++; + + if (seskey[n] != 2 ) /* wrong block type version */ + { + rc = gpg_error (GPG_ERR_INV_SESSION_KEY); + goto leave; + } + + for (n++; n < seskeylen && seskey[n]; n++) /* skip the random bytes */ + ; + n++; /* and the zero byte */ + if (n >= seskeylen ) + { + rc = gpg_error (GPG_ERR_INV_SESSION_KEY); + goto leave; + } + } + + if (DBG_CRYPTO) + log_printhex ("session key:", seskey+n, seskeylen-n); + + rc = gcry_cipher_open (&parm->hd, parm->algo, parm->mode, 0); + if (rc) + { + log_error ("error creating decryptor: %s\n", gpg_strerror (rc)); + goto leave; + } + + rc = gcry_cipher_setkey (parm->hd, seskey+n, seskeylen-n); + if (gpg_err_code (rc) == GPG_ERR_WEAK_KEY) + { + log_info (_("WARNING: message was encrypted with " + "a weak key in the symmetric cipher.\n")); + rc = 0; + } + if (rc) + { + log_error("key setup failed: %s\n", gpg_strerror(rc) ); + goto leave; + } + + gcry_cipher_setiv (parm->hd, parm->iv, parm->ivlen); + + leave: + xfree (seskey); + return rc; +} + + +/* This function is called by the KSBA writer just before the actual + write is done. The function must take INLEN bytes from INBUF, + decrypt it and store it inoutbuf which has a maximum size of + maxoutlen. The valid bytes in outbuf should be return in outlen. + Due to different buffer sizes or different length of input and + output, it may happen that fewer bytes are process or fewer bytes + are written. */ +static KsbaError +decrypt_filter (void *arg, + const void *inbuf, size_t inlen, size_t *inused, + void *outbuf, size_t maxoutlen, size_t *outlen) +{ + struct decrypt_filter_parm_s *parm = arg; + int blklen = parm->blklen; + size_t orig_inlen = inlen; + + /* fixme: Should we issue an error when we have not seen one full block? */ + if (!inlen) + return KSBA_Bug; + + if (maxoutlen < 2*parm->blklen) + return KSBA_Bug; + /* make some space becuase we will later need an extra block at the end */ + maxoutlen -= blklen; + + if (parm->helpblocklen) + { + int i, j; + + for (i=parm->helpblocklen,j=0; i < blklen && j < inlen; i++, j++) + parm->helpblock[i] = ((const char*)inbuf)[j]; + inlen -= j; + if (blklen > maxoutlen) + return KSBA_Bug; + if (i < blklen) + { + parm->helpblocklen = i; + *outlen = 0; + } + else + { + parm->helpblocklen = 0; + if (parm->any_data) + { + memcpy (outbuf, parm->lastblock, blklen); + *outlen =blklen; + } + else + *outlen = 0; + gcry_cipher_decrypt (parm->hd, parm->lastblock, blklen, + parm->helpblock, blklen); + parm->any_data = 1; + } + *inused = orig_inlen - inlen; + return 0; + } + + + if (inlen > maxoutlen) + inlen = maxoutlen; + if (inlen % blklen) + { /* store the remainder away */ + parm->helpblocklen = inlen%blklen; + inlen = inlen/blklen*blklen; + memcpy (parm->helpblock, (const char*)inbuf+inlen, parm->helpblocklen); + } + + *inused = inlen + parm->helpblocklen; + if (inlen) + { + assert (inlen >= blklen); + if (parm->any_data) + { + gcry_cipher_decrypt (parm->hd, (char*)outbuf+blklen, inlen, + inbuf, inlen); + memcpy (outbuf, parm->lastblock, blklen); + memcpy (parm->lastblock,(char*)outbuf+inlen, blklen); + *outlen = inlen; + } + else + { + gcry_cipher_decrypt (parm->hd, outbuf, inlen, inbuf, inlen); + memcpy (parm->lastblock, (char*)outbuf+inlen-blklen, blklen); + *outlen = inlen - blklen; + parm->any_data = 1; + } + } + else + *outlen = 0; + return 0; +} + + + +/* Perform a decrypt operation. */ +int +gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *out_fp) +{ + int rc; + KsbaError err; + Base64Context b64reader = NULL; + Base64Context b64writer = NULL; + KsbaReader reader; + KsbaWriter writer; + KsbaCMS cms = NULL; + KsbaStopReason stopreason; + KEYDB_HANDLE kh; + int recp; + FILE *in_fp = NULL; + struct decrypt_filter_parm_s dfparm; + + memset (&dfparm, 0, sizeof dfparm); + + kh = keydb_new (0); + if (!kh) + { + log_error (_("failed to allocated keyDB handle\n")); + rc = gpg_error (GPG_ERR_GENERAL); + goto leave; + } + + + in_fp = fdopen ( dup (in_fd), "rb"); + if (!in_fp) + { + rc = gpg_error (gpg_err_code_from_errno (errno)); + log_error ("fdopen() failed: %s\n", strerror (errno)); + goto leave; + } + + rc = gpgsm_create_reader (&b64reader, ctrl, in_fp, &reader); + if (rc) + { + log_error ("can't create reader: %s\n", gpg_strerror (rc)); + goto leave; + } + + rc = gpgsm_create_writer (&b64writer, ctrl, out_fp, &writer); + if (rc) + { + log_error ("can't create writer: %s\n", gpg_strerror (rc)); + goto leave; + } + + cms = ksba_cms_new (); + if (!cms) + { + rc = gpg_error (GPG_ERR_ENOMEM); + goto leave; + } + + err = ksba_cms_set_reader_writer (cms, reader, writer); + if (err) + { + log_debug ("ksba_cms_set_reader_writer failed: %s\n", + ksba_strerror (err)); + rc = map_ksba_err (err); + goto leave; + } + + /* parser loop */ + do + { + err = ksba_cms_parse (cms, &stopreason); + if (err) + { + log_debug ("ksba_cms_parse failed: %s\n", ksba_strerror (err)); + rc = map_ksba_err (err); + goto leave; + } + + if (stopreason == KSBA_SR_BEGIN_DATA + || stopreason == KSBA_SR_DETACHED_DATA) + { + int algo, mode; + const char *algoid; + int any_key = 0; + + algoid = ksba_cms_get_content_oid (cms, 2/* encryption algo*/); + algo = gcry_cipher_map_name (algoid); + mode = gcry_cipher_mode_from_oid (algoid); + if (!algo || !mode) + { + rc = gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + log_error ("unsupported algorithm `%s'\n", algoid? algoid:"?"); + if (algoid && !strcmp (algoid, "1.2.840.113549.3.2")) + log_info (_("(this is the RC2 algorithm)\n")); + else if (!algoid) + log_info (_("(this does not seem to be an encrypted" + " message)\n")); + { + char numbuf[50]; + sprintf (numbuf, "%d", rc); + gpgsm_status2 (ctrl, STATUS_ERROR, "decrypt.algorithm", + numbuf, algoid?algoid:"?", NULL); + } + + goto leave; + } + dfparm.algo = algo; + dfparm.mode = mode; + dfparm.blklen = gcry_cipher_get_algo_blklen (algo); + if (dfparm.blklen > sizeof (dfparm.helpblock)) + return gpg_error (GPG_ERR_BUG); + + rc = ksba_cms_get_content_enc_iv (cms, + dfparm.iv, + sizeof (dfparm.iv), + &dfparm.ivlen); + if (rc) + { + log_error ("error getting IV: %s\n", ksba_strerror (err)); + rc = map_ksba_err (err); + goto leave; + } + + for (recp=0; !any_key; recp++) + { + char *issuer; + KsbaSexp serial; + KsbaSexp enc_val; + char *hexkeygrip = NULL; + + err = ksba_cms_get_issuer_serial (cms, recp, &issuer, &serial); + if (err == -1 && recp) + break; /* no more recipients */ + if (err) + log_error ("recp %d - error getting info: %s\n", + recp, ksba_strerror (err)); + else + { + KsbaCert cert = NULL; + + log_debug ("recp %d - issuer: `%s'\n", + recp, issuer? issuer:"[NONE]"); + log_debug ("recp %d - serial: ", recp); + gpgsm_dump_serial (serial); + log_printf ("\n"); + + keydb_search_reset (kh); + rc = keydb_search_issuer_sn (kh, issuer, serial); + if (rc) + { + log_error ("failed to find the certificate: %s\n", + gpg_strerror(rc)); + goto oops; + } + + rc = keydb_get_cert (kh, &cert); + if (rc) + { + log_error ("failed to get cert: %s\n", gpg_strerror (rc)); + goto oops; + } + /* Just in case there is a problem with the own + certificate we print this message - should never + happen of course */ + rc = gpgsm_cert_use_decrypt_p (cert); + if (rc) + { + char numbuf[50]; + sprintf (numbuf, "%d", rc); + gpgsm_status2 (ctrl, STATUS_ERROR, "decrypt.keyusage", + numbuf, NULL); + rc = 0; + } + + hexkeygrip = gpgsm_get_keygrip_hexstring (cert); + + oops: + xfree (issuer); + xfree (serial); + ksba_cert_release (cert); + } + + if (!hexkeygrip) + ; + else if (!(enc_val = ksba_cms_get_enc_val (cms, recp))) + log_error ("recp %d - error getting encrypted session key\n", + recp); + else + { + rc = prepare_decryption (hexkeygrip, enc_val, &dfparm); + xfree (enc_val); + if (rc) + { + log_debug ("decrypting session key failed: %s\n", + gpg_strerror (rc)); + } + else + { /* setup the bulk decrypter */ + any_key = 1; + ksba_writer_set_filter (writer, + decrypt_filter, + &dfparm); + } + } + } + if (!any_key) + { + rc = gpg_error (GPG_ERR_NO_SECKEY); + goto leave; + } + } + else if (stopreason == KSBA_SR_END_DATA) + { + ksba_writer_set_filter (writer, NULL, NULL); + if (dfparm.any_data) + { /* write the last block with padding removed */ + int i, npadding = dfparm.lastblock[dfparm.blklen-1]; + if (!npadding || npadding > dfparm.blklen) + { + log_error ("invalid padding with value %d\n", npadding); + rc = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + rc = ksba_writer_write (writer, + dfparm.lastblock, + dfparm.blklen - npadding); + if (rc) + { + rc = map_ksba_err (rc); + goto leave; + } + for (i=dfparm.blklen - npadding; i < dfparm.blklen; i++) + { + if (dfparm.lastblock[i] != npadding) + { + log_error ("inconsistent padding\n"); + rc = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + } + } + } + + } + while (stopreason != KSBA_SR_READY); + + rc = gpgsm_finish_writer (b64writer); + if (rc) + { + log_error ("write failed: %s\n", gpg_strerror (rc)); + goto leave; + } + gpgsm_status (ctrl, STATUS_DECRYPTION_OKAY, NULL); + + + leave: + if (rc) + gpgsm_status (ctrl, STATUS_DECRYPTION_FAILED, NULL); + ksba_cms_release (cms); + gpgsm_destroy_reader (b64reader); + gpgsm_destroy_writer (b64writer); + keydb_release (kh); + if (in_fp) + fclose (in_fp); + if (dfparm.hd) + gcry_cipher_close (dfparm.hd); + return rc; +} + + diff --git a/sm/delete.c b/sm/delete.c new file mode 100644 index 000000000..53eff864c --- /dev/null +++ b/sm/delete.c @@ -0,0 +1,165 @@ +/* delete.c + * Copyright (C) 2002 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 +#include + +#include "gpgsm.h" +#include +#include + +#include "keydb.h" +#include "i18n.h" + + +/* Delete a certificate or an secret key from a key database. */ +static int +delete_one (CTRL ctrl, const char *username) +{ + int rc = 0; + KEYDB_SEARCH_DESC desc; + KEYDB_HANDLE kh = NULL; + KsbaCert cert = NULL; + int duplicates = 0; + + rc = keydb_classify_name (username, &desc); + if (rc) + { + log_error (_("certificate `%s' not found: %s\n"), + username, gpg_strerror (rc)); + gpgsm_status2 (ctrl, STATUS_DELETE_PROBLEM, "1", NULL); + goto leave; + } + + kh = keydb_new (0); + if (!kh) + { + log_error ("keydb_new failed\n"); + goto leave; + } + + + rc = keydb_search (kh, &desc, 1); + if (!rc) + rc = keydb_get_cert (kh, &cert); + if (!rc) + { + char fpr[20]; + + gpgsm_get_fingerprint (cert, 0, fpr, NULL); + + next_ambigious: + rc = keydb_search (kh, &desc, 1); + if (rc == -1) + rc = 0; + else if (!rc) + { + KsbaCert cert2 = NULL; + char fpr2[20]; + + /* We ignore all duplicated certificates which might have + been inserted due to program bugs. */ + if (!keydb_get_cert (kh, &cert2)) + { + gpgsm_get_fingerprint (cert2, 0, fpr2, NULL); + ksba_cert_release (cert2); + if (!memcmp (fpr, fpr2, 20)) + { + duplicates++; + goto next_ambigious; + } + } + rc = gpg_error (GPG_ERR_AMBIGUOUS_NAME); + } + } + if (rc) + { + if (rc == -1) + rc = gpg_error (GPG_ERR_NO_PUBKEY); + log_error (_("certificate `%s' not found: %s\n"), + username, gpg_strerror (rc)); + gpgsm_status2 (ctrl, STATUS_DELETE_PROBLEM, "3", NULL); + goto leave; + } + + /* we need to search again to get back to the right position. */ + do + { + keydb_search_reset (kh); + rc = keydb_search (kh, &desc, 1); + if (rc) + { + log_error ("problem re-searching certificate: %s\n", + gpg_strerror (rc)); + goto leave; + } + + rc = keydb_delete (kh); + if (rc) + goto leave; + if (opt.verbose) + { + if (duplicates) + log_info (_("duplicated certificate `%s' deleted\n"), username); + else + log_info (_("certificate `%s' deleted\n"), username); + } + } + while (duplicates--); + + leave: + keydb_release (kh); + ksba_cert_release (cert); + return rc; +} + + + +/* Delete the certificates specified by NAMES. */ +int +gpgsm_delete (CTRL ctrl, STRLIST names) +{ + int rc; + + if (!names) + { + log_error ("nothing to delete\n"); + return gpg_error (GPG_ERR_NO_DATA); + } + + for (; names; names=names->next ) + { + rc = delete_one (ctrl, names->d); + if (rc) + { + log_error (_("deleting certificate \"%s\" failed: %s\n"), + names->d, gpg_strerror (rc) ); + return rc; + } + } + + return 0; +} diff --git a/sm/encrypt.c b/sm/encrypt.c new file mode 100644 index 000000000..725a81b70 --- /dev/null +++ b/sm/encrypt.c @@ -0,0 +1,550 @@ +/* encrypt.c - Encrypt a message + * Copyright (C) 2001, 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 +#include + +#include "gpgsm.h" +#include +#include + +#include "keydb.h" +#include "i18n.h" + + +struct dek_s { + const char *algoid; + int algo; + gcry_cipher_hd_t chd; + char key[32]; + int keylen; + char iv[32]; + int ivlen; +}; +typedef struct dek_s *DEK; + +struct encrypt_cb_parm_s { + FILE *fp; + DEK dek; + int eof_seen; + int ready; + int readerror; + int bufsize; + unsigned char *buffer; + int buflen; +}; + + + + + +/* initialize the data encryptionkey (session key) */ +static int +init_dek (DEK dek) +{ + int rc=0, mode, i; + + dek->algo = gcry_cipher_map_name (dek->algoid); + mode = gcry_cipher_mode_from_oid (dek->algoid); + if (!dek->algo || !mode) + { + log_error ("unsupported algorithm `%s'\n", dek->algoid); + return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + } + + dek->keylen = gcry_cipher_get_algo_keylen (dek->algo); + if (!dek->keylen || dek->keylen > sizeof (dek->key)) + return gpg_error (GPG_ERR_BUG); + + dek->ivlen = gcry_cipher_get_algo_blklen (dek->algo); + if (!dek->ivlen || dek->ivlen > sizeof (dek->iv)) + return gpg_error (GPG_ERR_BUG); + + if (dek->keylen < 100/8) + { /* make sure we don't use weak keys */ + log_error ("key length of `%s' too small\n", dek->algoid); + return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + } + + rc = gcry_cipher_open (&dek->chd, dek->algo, mode, GCRY_CIPHER_SECURE); + if (rc) + { + log_error ("failed to create cipher context: %s\n", gpg_strerror (rc)); + return rc; + } + + for (i=0; i < 8; i++) + { + gcry_randomize (dek->key, dek->keylen, GCRY_STRONG_RANDOM ); + rc = gcry_cipher_setkey (dek->chd, dek->key, dek->keylen); + if (gpg_err_code (rc) != GPG_ERR_WEAK_KEY) + break; + log_info(_("weak key created - retrying\n") ); + } + if (rc) + { + log_error ("failed to set the key: %s\n", gpg_strerror (rc)); + gcry_cipher_close (dek->chd); + dek->chd = NULL; + return rc; + } + + gcry_randomize (dek->iv, dek->ivlen, GCRY_STRONG_RANDOM); + rc = gcry_cipher_setiv (dek->chd, dek->iv, dek->ivlen); + if (rc) + { + log_error ("failed to set the IV: %s\n", gpg_strerror (rc)); + gcry_cipher_close (dek->chd); + dek->chd = NULL; + return rc; + } + + return 0; +} + + +/* Encode the session key. NBITS is the number of bits which should be + used for packing the session key. returns: An mpi with the session + key (caller must free) */ +static gcry_mpi_t +encode_session_key (DEK dek, unsigned int nbits) +{ + int nframe = (nbits+7) / 8; + byte *p; + byte *frame; + int i,n; + gcry_mpi_t a; + + if (dek->keylen + 7 > nframe || !nframe) + log_bug ("can't encode a %d bit key in a %d bits frame\n", + dek->keylen*8, nbits ); + + /* We encode the session key in this way: + * + * 0 2 RND(n bytes) 0 KEY(k bytes) + * + * (But how can we store the leading 0 - the external representaion + * of MPIs doesn't allow leading zeroes =:-) + * + * RND are non-zero random bytes. + * KEY is the encryption key (session key) + */ + + frame = gcry_xmalloc_secure (nframe); + n = 0; + frame[n++] = 0; + frame[n++] = 2; + i = nframe - 3 - dek->keylen; + assert (i > 0); + p = gcry_random_bytes_secure (i, GCRY_STRONG_RANDOM); + /* replace zero bytes by new values */ + for (;;) + { + int j, k; + byte *pp; + + /* count the zero bytes */ + for(j=k=0; j < i; j++ ) + { + if( !p[j] ) + k++; + } + if( !k ) + break; /* okay: no zero bytes */ + + k += k/128; /* better get some more */ + pp = gcry_random_bytes_secure (k, GCRY_STRONG_RANDOM); + for (j=0; j < i && k; j++) + { + if( !p[j] ) + p[j] = pp[--k]; + } + xfree (pp); + } + memcpy (frame+n, p, i); + xfree (p); + + n += i; + frame[n++] = 0; + memcpy (frame+n, dek->key, dek->keylen); + n += dek->keylen; + assert (n == nframe); + if (gcry_mpi_scan (&a, GCRYMPI_FMT_USG, frame, n, &nframe) ) + BUG (); + gcry_free(frame); + + return a; +} + + + +/* encrypt the DEK under the key contained in CERT and return it as a + canonical S-Exp in encval */ +static int +encrypt_dek (const DEK dek, KsbaCert cert, char **encval) +{ + gcry_sexp_t s_ciph, s_data, s_pkey; + int rc; + KsbaSexp buf; + size_t len; + + *encval = NULL; + + /* get the key from the cert */ + buf = ksba_cert_get_public_key (cert); + if (!buf) + { + log_error ("no public key for recipient\n"); + return gpg_error (GPG_ERR_NO_PUBKEY); + } + len = gcry_sexp_canon_len (buf, 0, NULL, NULL); + if (!len) + { + log_error ("libksba did not return a proper S-Exp\n"); + return gpg_error (GPG_ERR_BUG); + } + rc = gcry_sexp_sscan (&s_pkey, NULL, buf, len); + xfree (buf); buf = NULL; + if (rc) + { + log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); + return rc; + } + + /* put the encoded cleartext into a simple list */ + { + /* fixme: actually the pkcs-1 encoding should go into libgcrypt */ + gcry_mpi_t data = encode_session_key (dek, gcry_pk_get_nbits (s_pkey)); + if (!data) + { + gcry_mpi_release (data); + return gpg_error (GPG_ERR_GENERAL); + } + if (gcry_sexp_build (&s_data, NULL, "%m", data)) + BUG (); + gcry_mpi_release (data); + } + + /* pass it to libgcrypt */ + rc = gcry_pk_encrypt (&s_ciph, s_data, s_pkey); + gcry_sexp_release (s_data); + gcry_sexp_release (s_pkey); + + /* reformat it */ + len = gcry_sexp_sprint (s_ciph, GCRYSEXP_FMT_CANON, NULL, 0); + assert (len); + buf = xtrymalloc (len); + if (!buf) + { + gpg_error_t tmperr = OUT_OF_CORE (errno); + gcry_sexp_release (s_ciph); + return tmperr; + } + len = gcry_sexp_sprint (s_ciph, GCRYSEXP_FMT_CANON, buf, len); + assert (len); + + *encval = buf; + return 0; +} + + + +/* do the actual encryption */ +static int +encrypt_cb (void *cb_value, char *buffer, size_t count, size_t *nread) +{ + struct encrypt_cb_parm_s *parm = cb_value; + int blklen = parm->dek->ivlen; + unsigned char *p; + size_t n; + + *nread = 0; + if (!buffer) + return -1; /* not supported */ + + if (parm->ready) + return -1; + + if (count < blklen) + BUG (); + + if (!parm->eof_seen) + { /* fillup the buffer */ + p = parm->buffer; + for (n=parm->buflen; n < parm->bufsize; n++) + { + int c = getc (parm->fp); + if (c == EOF) + { + if (ferror (parm->fp)) + { + parm->readerror = errno; + return -1; + } + parm->eof_seen = 1; + break; + } + p[n] = c; + } + parm->buflen = n; + } + + n = parm->buflen < count? parm->buflen : count; + n = n/blklen * blklen; + if (n) + { /* encrypt the stuff */ + gcry_cipher_encrypt (parm->dek->chd, buffer, n, parm->buffer, n); + *nread = n; + /* Who cares about cycles, take the easy way and shift the buffer */ + parm->buflen -= n; + memmove (parm->buffer, parm->buffer+n, parm->buflen); + } + else if (parm->eof_seen) + { /* no complete block but eof: add padding */ + /* fixme: we should try to do this also in the above code path */ + int i, npad = blklen - (parm->buflen % blklen); + p = parm->buffer; + for (n=parm->buflen, i=0; n < parm->bufsize && i < npad; n++, i++) + p[n] = npad; + gcry_cipher_encrypt (parm->dek->chd, buffer, n, parm->buffer, n); + *nread = n; + parm->ready = 1; + } + + return 0; +} + + + + +/* Perform an encrypt operation. + + Encrypt the data received on DATA-FD and write it to OUT_FP. The + recipients are take from the certificate given in recplist; if this + is NULL it will be encrypted for a default recipient */ +int +gpgsm_encrypt (CTRL ctrl, CERTLIST recplist, int data_fd, FILE *out_fp) +{ + int rc = 0; + Base64Context b64writer = NULL; + KsbaError err; + KsbaWriter writer; + KsbaReader reader = NULL; + KsbaCMS cms = NULL; + KsbaStopReason stopreason; + KEYDB_HANDLE kh = NULL; + struct encrypt_cb_parm_s encparm; + DEK dek = NULL; + int recpno; + FILE *data_fp = NULL; + CERTLIST cl; + + memset (&encparm, 0, sizeof encparm); + + if (!recplist) + { + log_error(_("no valid recipients given\n")); + gpgsm_status (ctrl, STATUS_NO_RECP, "0"); + rc = gpg_error (GPG_ERR_NO_PUBKEY); + goto leave; + } + + kh = keydb_new (0); + if (!kh) + { + log_error (_("failed to allocated keyDB handle\n")); + rc = gpg_error (GPG_ERR_GENERAL); + goto leave; + } + + data_fp = fdopen ( dup (data_fd), "rb"); + if (!data_fp) + { + rc = gpg_error (gpg_err_code_from_errno (errno)); + log_error ("fdopen() failed: %s\n", strerror (errno)); + goto leave; + } + + reader = ksba_reader_new (); + if (!reader) + rc = KSBA_Out_Of_Core; + if (!rc) + rc = ksba_reader_set_cb (reader, encrypt_cb, &encparm); + if (rc) + { + rc = map_ksba_err (rc); + goto leave; + } + encparm.fp = data_fp; + + ctrl->pem_name = "ENCRYPTED MESSAGE"; + rc = gpgsm_create_writer (&b64writer, ctrl, out_fp, &writer); + if (rc) + { + log_error ("can't create writer: %s\n", gpg_strerror (rc)); + goto leave; + } + + cms = ksba_cms_new (); + if (!cms) + { + rc = gpg_error (GPG_ERR_ENOMEM); + goto leave; + } + + err = ksba_cms_set_reader_writer (cms, reader, writer); + if (err) + { + log_debug ("ksba_cms_set_reader_writer failed: %s\n", + ksba_strerror (err)); + rc = map_ksba_err (err); + goto leave; + } + + /* We are going to create enveloped data with uninterpreted data as + inner content */ + err = ksba_cms_set_content_type (cms, 0, KSBA_CT_ENVELOPED_DATA); + if (!err) + err = ksba_cms_set_content_type (cms, 1, KSBA_CT_DATA); + if (err) + { + log_debug ("ksba_cms_set_content_type failed: %s\n", + ksba_strerror (err)); + rc = map_ksba_err (err); + goto leave; + } + + /* create a session key */ + dek = xtrycalloc (1, sizeof *dek); /* hmmm: should we put it into secmem?*/ + if (!dek) + rc = OUT_OF_CORE (errno); + else + { + dek->algoid = opt.def_cipher_algoid; + rc = init_dek (dek); + } + if (rc) + { + log_error ("failed to create the session key: %s\n", + gpg_strerror (rc)); + goto leave; + } + + err = ksba_cms_set_content_enc_algo (cms, dek->algoid, dek->iv, dek->ivlen); + if (err) + { + log_error ("ksba_cms_set_content_enc_algo failed: %s\n", + ksba_strerror (err)); + rc = map_ksba_err (err); + goto leave; + } + + encparm.dek = dek; + /* Use a ~8k (AES) or ~4k (3DES) buffer */ + encparm.bufsize = 500 * dek->ivlen; + encparm.buffer = xtrymalloc (encparm.bufsize); + if (!encparm.buffer) + { + rc = OUT_OF_CORE (errno); + goto leave; + } + + /* gather certificates of recipients, encrypt the session key for + each and store them in the CMS object */ + for (recpno = 0, cl = recplist; cl; recpno++, cl = cl->next) + { + char *encval; + + rc = encrypt_dek (dek, cl->cert, &encval); + if (rc) + { + log_error ("encryption failed for recipient no. %d: %s\n", + recpno, gpg_strerror (rc)); + goto leave; + } + + err = ksba_cms_add_recipient (cms, cl->cert); + if (err) + { + log_error ("ksba_cms_add_recipient failed: %s\n", + ksba_strerror (err)); + rc = map_ksba_err (err); + xfree (encval); + goto leave; + } + + err = ksba_cms_set_enc_val (cms, recpno, encval); + xfree (encval); + if (err) + { + log_error ("ksba_cms_set_enc_val failed: %s\n", + ksba_strerror (err)); + rc = map_ksba_err (err); + goto leave; + } + } + + /* main control loop for encryption */ + recpno = 0; + do + { + err = ksba_cms_build (cms, &stopreason); + if (err) + { + log_debug ("ksba_cms_build failed: %s\n", ksba_strerror (err)); + rc = map_ksba_err (err); + goto leave; + } + } + while (stopreason != KSBA_SR_READY); + + if (encparm.readerror) + { + log_error ("error reading input: %s\n", strerror (encparm.readerror)); + rc = gpg_error (gpg_err_code_from_errno (encparm.readerror)); + goto leave; + } + + + rc = gpgsm_finish_writer (b64writer); + if (rc) + { + log_error ("write failed: %s\n", gpg_strerror (rc)); + goto leave; + } + log_info ("encrypted data created\n"); + + leave: + ksba_cms_release (cms); + gpgsm_destroy_writer (b64writer); + ksba_reader_release (reader); + keydb_release (kh); + xfree (dek); + if (data_fp) + fclose (data_fp); + xfree (encparm.buffer); + return rc; +} diff --git a/sm/export.c b/sm/export.c new file mode 100644 index 000000000..93a55debc --- /dev/null +++ b/sm/export.c @@ -0,0 +1,249 @@ +/* export.c + * Copyright (C) 2002, 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 +#include + +#include "gpgsm.h" +#include +#include + +#include "keydb.h" + +static void print_short_info (KsbaCert cert, FILE *fp); + + + +/* Export all certificates or just those given in NAMES. */ +void +gpgsm_export (CTRL ctrl, STRLIST names, FILE *fp) +{ + KEYDB_HANDLE hd; + KEYDB_SEARCH_DESC *desc = NULL; + int ndesc; + Base64Context b64writer = NULL; + KsbaWriter writer; + STRLIST sl; + KsbaCert cert = NULL; + int rc=0; + int count = 0; + int i; + + hd = keydb_new (0); + if (!hd) + { + log_error ("keydb_new failed\n"); + goto leave; + } + + if (!names) + ndesc = 1; + else + { + for (sl=names, ndesc=0; sl; sl = sl->next, ndesc++) + ; + } + + desc = xtrycalloc (ndesc, sizeof *desc); + if (!ndesc) + { + log_error ("allocating memory for export failed: %s\n", + gpg_strerror (OUT_OF_CORE (errno))); + goto leave; + } + + if (!names) + desc[0].mode = KEYDB_SEARCH_MODE_FIRST; + else + { + for (ndesc=0, sl=names; sl; sl = sl->next) + { + rc = keydb_classify_name (sl->d, desc+ndesc); + if (rc) + { + log_error ("key `%s' not found: %s\n", + sl->d, gpg_strerror (rc)); + rc = 0; + } + else + ndesc++; + } + } + + /* If all specifications are done by fingerprint, we switch to + ephemeral mode so that _all_ currently available and matching + certificates are exported. + + fixme: we should in this case keep a list of certificates to + avoid accidential export of duplicate certificates. */ + if (names && ndesc) + { + for (i=0; (i < ndesc + && (desc[i].mode == KEYDB_SEARCH_MODE_FPR + || desc[i].mode == KEYDB_SEARCH_MODE_FPR20 + || desc[i].mode == KEYDB_SEARCH_MODE_FPR16)); i++) + ; + if (i == ndesc) + keydb_set_ephemeral (hd, 1); + } + + while (!(rc = keydb_search (hd, desc, ndesc))) + { + const unsigned char *image; + size_t imagelen; + + if (!names) + desc[0].mode = KEYDB_SEARCH_MODE_NEXT; + + rc = keydb_get_cert (hd, &cert); + if (rc) + { + log_error ("keydb_get_cert failed: %s\n", gpg_strerror (rc)); + goto leave; + } + + image = ksba_cert_get_image (cert, &imagelen); + if (!image) + { + log_error ("ksba_cert_get_image failed\n"); + goto leave; + } + + if (ctrl->create_pem) + { + if (count) + putc ('\n', fp); + print_short_info (cert, fp); + putc ('\n', fp); + } + count++; + + if (!b64writer) + { + ctrl->pem_name = "CERTIFICATE"; + rc = gpgsm_create_writer (&b64writer, ctrl, fp, &writer); + if (rc) + { + log_error ("can't create writer: %s\n", gpg_strerror (rc)); + goto leave; + } + } + + rc = ksba_writer_write (writer, image, imagelen); + if (rc) + { + log_error ("write error: %s\n", ksba_strerror (rc)); + goto leave; + } + + if (ctrl->create_pem) + { + /* We want one certificate per PEM block */ + rc = gpgsm_finish_writer (b64writer); + if (rc) + { + log_error ("write failed: %s\n", gpg_strerror (rc)); + goto leave; + } + gpgsm_destroy_writer (b64writer); + b64writer = NULL; + } + + ksba_cert_release (cert); + cert = NULL; + } + if (rc && rc != -1) + log_error ("keydb_search failed: %s\n", gpg_strerror (rc)); + else if (b64writer) + { + rc = gpgsm_finish_writer (b64writer); + if (rc) + { + log_error ("write failed: %s\n", gpg_strerror (rc)); + goto leave; + } + } + + leave: + gpgsm_destroy_writer (b64writer); + ksba_cert_release (cert); + xfree (desc); + keydb_release (hd); +} + + +/* Print some info about the certifciate CERT to FP */ +static void +print_short_info (KsbaCert cert, FILE *fp) +{ + char *p; + KsbaSexp sexp; + int idx; + + for (idx=0; (p = ksba_cert_get_issuer (cert, idx)); idx++) + { + fputs (!idx? "Issuer ...: " + : "\n aka ...: ", fp); + gpgsm_print_name (fp, p); + xfree (p); + } + putc ('\n', fp); + + fputs ("Serial ...: ", fp); + sexp = ksba_cert_get_serial (cert); + if (sexp) + { + int len; + const unsigned char *s = sexp; + + if (*s == '(') + { + s++; + for (len=0; *s && *s != ':' && digitp (s); s++) + len = len*10 + atoi_1 (s); + if (*s == ':') + for (s++; len; len--, s++) + fprintf (fp, "%02X", *s); + } + xfree (sexp); + } + putc ('\n', fp); + + for (idx=0; (p = ksba_cert_get_subject (cert, idx)); idx++) + { + fputs (!idx? "Subject ..: " + : "\n aka ..: ", fp); + gpgsm_print_name (fp, p); + xfree (p); + } + putc ('\n', fp); +} + + + + + + diff --git a/sm/fingerprint.c b/sm/fingerprint.c new file mode 100644 index 000000000..028c08aab --- /dev/null +++ b/sm/fingerprint.c @@ -0,0 +1,271 @@ +/* fingerprint.c - Get the fingerprint + * Copyright (C) 2001 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 +#include + + +#include "gpgsm.h" +#include +#include + +/* Return the fingerprint of the certificate (we can't put this into + libksba because we need libgcrypt support). The caller must + provide an array of sufficient length or NULL so that the function + allocates the array. If r_len is not NULL, the length of the + digest is returned; well, this can also be done by using + gcry_md_get_algo_dlen(). If algo is 0, a SHA-1 will be used. + + If there is a problem , the function does never return NULL but a + digest of all 0xff. + */ +char * +gpgsm_get_fingerprint (KsbaCert cert, int algo, char *array, int *r_len) +{ + gcry_md_hd_t md; + int rc, len; + + if (!algo) + algo = GCRY_MD_SHA1; + + len = gcry_md_get_algo_dlen (algo); + assert (len); + if (!array) + array = xmalloc (len); + + if (r_len) + *r_len = len; + + rc = gcry_md_open (&md, algo, 0); + if (rc) + { + log_error ("md_open failed: %s\n", gpg_strerror (rc)); + memset (array, 0xff, len); /* better return an invalid fpr than NULL */ + return array; + } + + rc = ksba_cert_hash (cert, 0, HASH_FNC, md); + if (rc) + { + log_error ("ksba_cert_hash failed: %s\n", ksba_strerror (rc)); + gcry_md_close (md); + memset (array, 0xff, len); /* better return an invalid fpr than NULL */ + return array; + } + gcry_md_final (md); + memcpy (array, gcry_md_read(md, algo), len ); + return array; +} + + +/* Return an allocated buffer with the formatted fingerprint */ +char * +gpgsm_get_fingerprint_string (KsbaCert cert, int algo) +{ + unsigned char digest[MAX_DIGEST_LEN]; + char *buf; + int len, i; + + if (!algo) + algo = GCRY_MD_SHA1; + + len = gcry_md_get_algo_dlen (algo); + assert (len <= MAX_DIGEST_LEN ); + gpgsm_get_fingerprint (cert, algo, digest, NULL); + buf = xmalloc (len*3+1); + *buf = 0; + for (i=0; i < len; i++ ) + sprintf (buf+strlen(buf), i? ":%02X":"%02X", digest[i]); + return buf; +} + +/* Return an allocated buffer with the formatted fingerprint as one + large hexnumber */ +char * +gpgsm_get_fingerprint_hexstring (KsbaCert cert, int algo) +{ + unsigned char digest[MAX_DIGEST_LEN]; + char *buf; + int len, i; + + if (!algo) + algo = GCRY_MD_SHA1; + + len = gcry_md_get_algo_dlen (algo); + assert (len <= MAX_DIGEST_LEN ); + gpgsm_get_fingerprint (cert, algo, digest, NULL); + buf = xmalloc (len*3+1); + *buf = 0; + for (i=0; i < len; i++ ) + sprintf (buf+strlen(buf), "%02X", digest[i]); + return buf; +} + +/* Return a certificate ID. These are the last 4 bytes of the SHA-1 + fingerprint. */ +unsigned long +gpgsm_get_short_fingerprint (KsbaCert cert) +{ + unsigned char digest[20]; + + gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL); + return ((digest[16]<<24)|(digest[17]<<16)|(digest[18]<< 8)|digest[19]); +} + + +/* Return the so called KEYGRIP which is the SHA-1 hash of the public + key parameters expressed as an canoncial encoded S-Exp. array must + be 20 bytes long. returns the array or a newly allocated one if the + passed one was NULL */ +char * +gpgsm_get_keygrip (KsbaCert cert, char *array) +{ + gcry_sexp_t s_pkey; + int rc; + KsbaSexp p; + size_t n; + + p = ksba_cert_get_public_key (cert); + if (!p) + return NULL; /* oops */ + + if (DBG_X509) + log_debug ("get_keygrip for public key: %s\n", p); + n = gcry_sexp_canon_len (p, 0, NULL, NULL); + if (!n) + { + log_error ("libksba did not return a proper S-Exp\n"); + return NULL; + } + rc = gcry_sexp_sscan ( &s_pkey, NULL, p, n); + xfree (p); + if (rc) + { + log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); + return NULL; + } + array = gcry_pk_get_keygrip (s_pkey, array); + gcry_sexp_release (s_pkey); + if (!array) + { + rc = gpg_error (GPG_ERR_GENERAL); + log_error ("can't calculate keygrip\n"); + return NULL; + } + if (DBG_X509) + log_printhex ("keygrip=", array, 20); + + return array; +} + +/* Return an allocated buffer with the keygrip of CERT in from of an + hexstring. NULL is returned in case of error */ +char * +gpgsm_get_keygrip_hexstring (KsbaCert cert) +{ + unsigned char grip[20]; + char *buf, *p; + int i; + + gpgsm_get_keygrip (cert, grip); + buf = p = xmalloc (20*2+1); + for (i=0; i < 20; i++, p += 2 ) + sprintf (p, "%02X", grip[i]); + return buf; +} + + + +/* For certain purposes we need a certificate id which has an upper + limit of the size. We use the hash of the issuer name and the + serial number for this. In most cases the serial number is not + that large and the resulting string can be passed on an assuan + command line. Everything is hexencoded with the serialnumber + delimted from the has by a dot. + + The caller must free the string. +*/ +char * +gpgsm_get_certid (KsbaCert cert) +{ + KsbaSexp serial; + unsigned char *p; + char *endp; + unsigned char hash[20]; + unsigned long n; + char *certid; + int i; + + p = ksba_cert_get_issuer (cert, 0); + if (!p) + return NULL; /* Ooops: No issuer */ + gcry_md_hash_buffer (GCRY_MD_SHA1, hash, p, strlen (p)); + xfree (p); + + serial = ksba_cert_get_serial (cert); + if (!serial) + return NULL; /* oops: no serial number */ + p = serial; + if (*p != '(') + { + log_error ("Ooops: invalid serial number\n"); + xfree (serial); + return NULL; + } + p++; + n = strtoul (p, &endp, 10); + p = endp; + if (*p != ':') + { + log_error ("Ooops: invalid serial number (no colon)\n"); + xfree (serial); + return NULL; + } + p++; + + certid = xtrymalloc ( 40 + 1 + n*2 + 1); + if (!certid) + { + xfree (serial); + return NULL; /* out of core */ + } + + for (i=0, endp = certid; i < 20; i++, endp += 2 ) + sprintf (endp, "%02X", hash[i]); + *endp++ = '.'; + for (i=0; i < n; i++, endp += 2) + sprintf (endp, "%02X", p[i]); + *endp = 0; + + xfree (serial); + return certid; +} + + + + + + diff --git a/sm/gpgsm.c b/sm/gpgsm.c new file mode 100644 index 000000000..c392886ba --- /dev/null +++ b/sm/gpgsm.c @@ -0,0 +1,1458 @@ +/* gpgsm.c - GnuPG for S/MIME + * Copyright (C) 2001, 2002, 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 +#include + +#include "gpgsm.h" +#include +#include /* malloc hooks */ + +#include "../kbx/keybox.h" /* malloc hooks */ +#include "i18n.h" +#include "keydb.h" +#include "sysutils.h" + +enum cmd_and_opt_values { + aNull = 0, + oArmor = 'a', + aDetachedSign = 'b', + aSym = 'c', + aDecrypt = 'd', + aEncr = 'e', + oInteractive = 'i', + oKOption = 'k', + oDryRun = 'n', + oOutput = 'o', + oQuiet = 'q', + oRecipient = 'r', + aSign = 's', + oTextmodeShort= 't', + oUser = 'u', + oVerbose = 'v', + oCompress = 'z', + oNotation = 'N', + oBatch = 500, + aClearsign, + aStore, + aKeygen, + aSignEncr, + aSignKey, + aLSignKey, + aListPackets, + aEditKey, + aDeleteKey, + aImport, + aVerify, + aVerifyFiles, + aListKeys, + aListExternalKeys, + aListSigs, + aListSecretKeys, + aSendKeys, + aRecvKeys, + aExport, + aCheckKeys, /* nyi */ + aServer, + aLearnCard, + aCallDirmngr, + aCallProtectTool, + aPasswd, + + oOptions, + oDebug, + oDebugAll, + oDebugWait, + oDebugNoChainValidation, + oLogFile, + + oEnableSpecialFilenames, + + oAgentProgram, + oDisplay, + oTTYname, + oTTYtype, + oLCctype, + oLCmessages, + + oDirmngrProgram, + oFakedSystemTime, + + + oAssumeArmor, + oAssumeBase64, + oAssumeBinary, + + oBase64, + oNoArmor, + + oDisableCRLChecks, + oEnableCRLChecks, + + oIncludeCerts, + oPolicyFile, + oDisablePolicyChecks, + oEnablePolicyChecks, + oAutoIssuerKeyRetrieve, + + + oTextmode, + oFingerprint, + oWithFingerprint, + oAnswerYes, + oAnswerNo, + oKeyring, + oSecretKeyring, + oDefaultKey, + oDefRecipient, + oDefRecipientSelf, + oNoDefRecipient, + oStatusFD, + oNoComment, + oNoVersion, + oEmitVersion, + oCompletesNeeded, + oMarginalsNeeded, + oMaxCertDepth, + oLoadExtension, + oRFC1991, + oOpenPGP, + oCipherAlgo, + oDigestAlgo, + oCompressAlgo, + oCommandFD, + oNoVerbose, + oTrustDBName, + oNoSecmemWarn, + oNoDefKeyring, + oNoGreeting, + oNoTTY, + oNoOptions, + oNoBatch, + oHomedir, + oWithColons, + oWithKeyData, + oSkipVerify, + oCompressKeys, + oCompressSigs, + oAlwaysTrust, + oRunAsShmCP, + oSetFilename, + oSetPolicyURL, + oUseEmbeddedFilename, + oComment, + oDefaultComment, + oThrowKeyid, + oForceV3Sigs, + oForceMDC, + oS2KMode, + oS2KDigest, + oS2KCipher, + oCharset, + oNotDashEscaped, + oEscapeFrom, + oLockOnce, + oLockMultiple, + oLockNever, + oKeyServer, + oEncryptTo, + oNoEncryptTo, + oLoggerFD, + oUtf8Strings, + oNoUtf8Strings, + oDisableCipherAlgo, + oDisablePubkeyAlgo, + oAllowNonSelfsignedUID, + oAllowFreeformUID, + oNoLiteral, + oSetFilesize, + oHonorHttpProxy, + oFastListMode, + oListOnly, + oIgnoreTimeConflict, + oNoRandomSeedFile, + oNoAutoKeyRetrieve, + oUseAgent, + oMergeOnly, + oTryAllSecrets, + oTrustedKey, + oEmuMDEncodeBug, + aDummy + }; + + +static ARGPARSE_OPTS opts[] = { + + { 300, NULL, 0, N_("@Commands:\n ") }, + + { aSign, "sign", 256, N_("|[file]|make a signature")}, + { aClearsign, "clearsign", 256, N_("|[file]|make a clear text signature") }, + { aDetachedSign, "detach-sign", 256, N_("make a detached signature")}, + { aEncr, "encrypt", 256, N_("encrypt data")}, + { aSym, "symmetric", 256, N_("encryption only with symmetric cipher")}, + { aDecrypt, "decrypt", 256, N_("decrypt data (default)")}, + { aVerify, "verify" , 256, N_("verify a signature")}, + { aVerifyFiles, "verify-files" , 256, "@" }, + { aListKeys, "list-keys", 256, N_("list keys")}, + { aListExternalKeys, "list-external-keys", 256, N_("list external keys")}, + { aListSecretKeys, "list-secret-keys", 256, N_("list secret keys")}, + { aListSigs, "list-sigs", 256, N_("list certificate chain")}, + { aListSigs, "check-sigs",256, "@"}, + { oFingerprint, "fingerprint", 256, N_("list keys and fingerprints")}, + { aKeygen, "gen-key", 256, N_("generate a new key pair")}, + { aDeleteKey, "delete-key",256, N_("remove key from the public keyring")}, + { aSendKeys, "send-keys" , 256, N_("export keys to a key server") }, + { aRecvKeys, "recv-keys" , 256, N_("import keys from a key server") }, + { aImport, "import", 256 , N_("import certificates")}, + { aExport, "export", 256 , N_("export certificates")}, + { aLearnCard, "learn-card", 256 ,N_("register a smartcard")}, + { aServer, "server", 256, N_("run in server mode")}, + { aCallDirmngr, "call-dirmngr", 256, N_("pass a command to the dirmngr")}, + { aCallProtectTool, "call-protect-tool", 256, + N_("invoke gpg-protect-tool")}, + { aPasswd, "passwd", 256, N_("change a passphrase")}, + + { 301, NULL, 0, N_("@\nOptions:\n ") }, + + { oArmor, "armor", 0, N_("create ascii armored output")}, + { oArmor, "armour", 0, "@" }, + { oBase64, "base64", 0, N_("create base-64 encoded output")}, + + { oAssumeArmor, "assume-armor", 0, N_("assume input is in PEM format")}, + { oAssumeBase64, "assume-base64", 0, + N_("assume input is in base-64 format")}, + { oAssumeBinary, "assume-binary", 0, + N_("assume input is in binary format")}, + + { oRecipient, "recipient", 2, N_("|NAME|encrypt for NAME")}, + + + { oDisableCRLChecks, "disable-crl-checks", 0, N_("never consult a CRL")}, + { oEnableCRLChecks, "enable-crl-checks", 0, "@"}, + + { oIncludeCerts, "include-certs", 1, + N_("|N|number of certificates to include") }, + + { oPolicyFile, "policy-file", 2, + N_("|FILE|take policy information from FILE") }, + + { oDisablePolicyChecks, "disable-policy-checks", 0, + N_("do not check certificate policies")}, + { oEnablePolicyChecks, "enable-policy-checks", 0, "@"}, + + { oAutoIssuerKeyRetrieve, "auto-issuer-key-retrieve", 0, + N_("fetch missing issuer certificates")}, + +#if 0 + { oDefRecipient, "default-recipient" ,2, + N_("|NAME|use NAME as default recipient")}, + { oDefRecipientSelf, "default-recipient-self" ,0, + N_("use the default key as default recipient")}, + { oNoDefRecipient, "no-default-recipient", 0, "@" }, + { oEncryptTo, "encrypt-to", 2, "@" }, + { oNoEncryptTo, "no-encrypt-to", 0, "@" }, + +#endif + { oUser, "local-user",2, N_("use this user-id to sign or decrypt")}, + +#if 0 + { oCompress, NULL, 1, N_("|N|set compress level N (0 disables)") }, + { oTextmodeShort, NULL, 0, "@"}, + { oTextmode, "textmode", 0, N_("use canonical text mode")}, +#endif + + { oOutput, "output", 2, N_("use as output file")}, + { oVerbose, "verbose", 0, N_("verbose") }, + { oQuiet, "quiet", 0, N_("be somewhat more quiet") }, + { oNoTTY, "no-tty", 0, N_("don't use the terminal at all") }, + { oLogFile, "log-file" ,2, N_("use a log file for the server")}, +#if 0 + { oForceV3Sigs, "force-v3-sigs", 0, N_("force v3 signatures") }, + { oForceMDC, "force-mdc", 0, N_("always use a MDC for encryption") }, +#endif + { oDryRun, "dry-run", 0, N_("do not make any changes") }, + /*{ oInteractive, "interactive", 0, N_("prompt before overwriting") }, */ + /*{ oUseAgent, "use-agent",0, N_("use the gpg-agent")},*/ + { oBatch, "batch", 0, N_("batch mode: never ask")}, + { oAnswerYes, "yes", 0, N_("assume yes on most questions")}, + { oAnswerNo, "no", 0, N_("assume no on most questions")}, + + { oKeyring, "keyring" ,2, N_("add this keyring to the list of keyrings")}, + { oSecretKeyring, "secret-keyring" ,2, N_("add this secret keyring to the list")}, + { oDefaultKey, "default-key" ,2, N_("|NAME|use NAME as default secret key")}, + { oKeyServer, "keyserver",2, N_("|HOST|use this keyserver to lookup keys")}, + { oCharset, "charset" , 2, N_("|NAME|set terminal charset to NAME") }, + { oOptions, "options" , 2, N_("read options from file")}, + + { oDebug, "debug" ,4|16, "@"}, + { oDebugAll, "debug-all" ,0, "@"}, + { oDebugWait, "debug-wait" ,1, "@"}, + { oDebugNoChainValidation, "debug-no-chain-validation" ,0, "@"}, + { oStatusFD, "status-fd" ,1, N_("|FD|write status info to this FD") }, + { aDummy, "no-comment", 0, "@"}, + { aDummy, "completes-needed", 1, "@"}, + { aDummy, "marginals-needed", 1, "@"}, + { oMaxCertDepth, "max-cert-depth", 1, "@" }, + { aDummy, "trusted-key", 2, "@"}, + { oLoadExtension, "load-extension" ,2, + N_("|FILE|load extension module FILE")}, + { aDummy, "rfc1991", 0, "@"}, + { aDummy, "openpgp", 0, "@"}, + { aDummy, "s2k-mode", 1, "@"}, + { aDummy, "s2k-digest-algo",2, "@"}, + { aDummy, "s2k-cipher-algo",2, "@"}, + { oCipherAlgo, "cipher-algo", 2 , N_("|NAME|use cipher algorithm NAME")}, + { oDigestAlgo, "digest-algo", 2 , + N_("|NAME|use message digest algorithm NAME")}, +#if 0 + { oCompressAlgo, "compress-algo", 1 , N_("|N|use compress algorithm N")}, +#endif + { aDummy, "throw-keyid", 0, "@"}, + { aDummy, "notation-data", 2, "@"}, + + { 302, NULL, 0, N_( + "@\n(See the man page for a complete listing of all commands and options)\n" + )}, + + { 303, NULL, 0, N_("@\nExamples:\n\n" + " -se -r Bob [file] sign and encrypt for user Bob\n" + " --clearsign [file] make a clear text signature\n" + " --detach-sign [file] make a detached signature\n" + " --list-keys [names] show keys\n" + " --fingerprint [names] show fingerprints\n" ) }, + + /* hidden options */ + { oNoVerbose, "no-verbose", 0, "@"}, + + { oEnableSpecialFilenames, "enable-special-filenames", 0, "@" }, + + + { oTrustDBName, "trustdb-name", 2, "@" }, + { oNoSecmemWarn, "no-secmem-warning", 0, "@" }, + { oNoArmor, "no-armor", 0, "@"}, + { oNoArmor, "no-armour", 0, "@"}, + { oNoDefKeyring, "no-default-keyring", 0, "@" }, + { oNoGreeting, "no-greeting", 0, "@" }, + { oNoOptions, "no-options", 0, "@" }, /* shortcut for --options /dev/null */ + { oHomedir, "homedir", 2, "@" }, /* defaults to "~/.gnupg" */ + { oAgentProgram, "agent-program", 2 , "@" }, + { oDisplay, "display", 2, "@" }, + { oTTYname, "ttyname", 2, "@" }, + { oTTYtype, "ttytype", 2, "@" }, + { oLCctype, "lc-ctype", 2, "@" }, + { oLCmessages, "lc-messages", 2, "@" }, + { oDirmngrProgram, "dirmngr-program", 2 , "@" }, + { oFakedSystemTime, "faked-system-time", 4, "@" }, /* (epoch time) */ + + + { oNoBatch, "no-batch", 0, "@" }, + { oWithColons, "with-colons", 0, "@"}, + { oWithKeyData,"with-key-data", 0, "@"}, + { aListKeys, "list-key", 0, "@" }, /* alias */ + { aListSigs, "list-sig", 0, "@" }, /* alias */ + { aListSigs, "check-sig",0, "@" }, /* alias */ + { oSkipVerify, "skip-verify",0, "@" }, + { oCompressKeys, "compress-keys",0, "@"}, + { oCompressSigs, "compress-sigs",0, "@"}, + { oAlwaysTrust, "always-trust", 0, "@"}, + { oNoVersion, "no-version", 0, "@"}, + { oLockOnce, "lock-once", 0, "@" }, + { oLockMultiple, "lock-multiple", 0, "@" }, + { oLockNever, "lock-never", 0, "@" }, + { oLoggerFD, "logger-fd",1, "@" }, + { oWithFingerprint, "with-fingerprint", 0, "@" }, + { oDisableCipherAlgo, "disable-cipher-algo", 2, "@" }, + { oDisablePubkeyAlgo, "disable-pubkey-algo", 2, "@" }, + { oHonorHttpProxy,"honor-http-proxy", 0, "@" }, + { oListOnly, "list-only", 0, "@"}, + { oIgnoreTimeConflict, "ignore-time-conflict", 0, "@" }, + { oNoRandomSeedFile, "no-random-seed-file", 0, "@" }, +{0} }; + + + +int gpgsm_errors_seen = 0; + +/* It is possible that we are currentlu running under setuid permissions */ +static int maybe_setuid = 1; + +/* Option --enable-special-filenames */ +static int allow_special_filenames; + + +static char *build_list (const char *text, + const char *(*mapf)(int), int (*chkf)(int)); +static void set_cmd (enum cmd_and_opt_values *ret_cmd, + enum cmd_and_opt_values new_cmd ); + +static void emergency_cleanup (void); +static int check_special_filename (const char *fname); +static int open_read (const char *filename); +static FILE *open_fwrite (const char *filename); +static void run_protect_tool (int argc, char **argv); + + +static int +our_pk_test_algo (int algo) +{ + return 1; +} + +static int +our_cipher_test_algo (int algo) +{ + return 1; +} + +static int +our_md_test_algo (int algo) +{ + return 1; +} + +static const char * +my_strusage( int level ) +{ + static char *digests, *pubkeys, *ciphers; + const char *p; + + switch (level) + { + case 11: p = "gpgsm (GnuPG)"; + break; + case 13: p = VERSION; break; + case 17: p = PRINTABLE_OS_NAME; break; + case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n"); + break; + case 1: + case 40: p = _("Usage: gpgsm [options] [files] (-h for help)"); + break; + case 41: + p = _("Syntax: gpgsm [options] [files]\n" + "sign, check, encrypt or decrypt using the S/MIME protocol\n" + "default operation depends on the input data\n"); + break; + + case 31: p = "\nHome: "; break; + case 32: p = opt.homedir; break; + case 33: p = _("\nSupported algorithms:\n"); break; + case 34: + if (!ciphers) + ciphers = build_list ("Cipher: ", gcry_cipher_algo_name, + our_cipher_test_algo ); + p = ciphers; + break; + case 35: + if (!pubkeys) + pubkeys = build_list ("Pubkey: ", gcry_pk_algo_name, + our_pk_test_algo ); + p = pubkeys; + break; + case 36: + if (!digests) + digests = build_list("Hash: ", gcry_md_algo_name, our_md_test_algo ); + p = digests; + break; + + default: p = NULL; break; + } + return p; +} + + +static char * +build_list (const char *text, const char * (*mapf)(int), int (*chkf)(int)) +{ + int i; + size_t n=strlen(text)+2; + char *list, *p; + + if (maybe_setuid) { + gcry_control (GCRYCTL_DROP_PRIVS); /* drop setuid */ + } + + for (i=1; i < 110; i++ ) + if (!chkf(i)) + n += strlen(mapf(i)) + 2; + list = xmalloc (21 + n); + *list = 0; + for (p=NULL, i=1; i < 110; i++) + { + if (!chkf(i)) + { + if( !p ) + p = stpcpy (list, text ); + else + p = stpcpy (p, ", "); + p = stpcpy (p, mapf(i) ); + } + } + if (p) + p = stpcpy(p, "\n" ); + return list; +} + + +static void +i18n_init(void) +{ +#ifdef USE_SIMPLE_GETTEXT + set_gettext_file (PACKAGE); +#else +# ifdef ENABLE_NLS +# ifdef HAVE_LC_MESSAGES + setlocale (LC_TIME, ""); + setlocale (LC_MESSAGES, ""); +# else + setlocale (LC_ALL, "" ); +# endif + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); +# endif +#endif +} + + +static void +wrong_args (const char *text) +{ + fputs (_("usage: gpgsm [options] "), stderr); + fputs (text, stderr); + putc ('\n', stderr); + gpgsm_exit (2); +} + + +static void +set_debug(void) +{ + if (opt.debug & DBG_MPI_VALUE) + gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 2); + if (opt.debug & DBG_CRYPTO_VALUE ) + gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1); +} + + +static void +set_cmd (enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd) +{ + enum cmd_and_opt_values cmd = *ret_cmd; + + if (!cmd || cmd == new_cmd) + cmd = new_cmd; + else if ( cmd == aSign && new_cmd == aEncr ) + cmd = aSignEncr; + else if ( cmd == aEncr && new_cmd == aSign ) + cmd = aSignEncr; + else if ( (cmd == aSign && new_cmd == aClearsign) + || (cmd == aClearsign && new_cmd == aSign) ) + cmd = aClearsign; + else + { + log_error(_("conflicting commands\n")); + gpgsm_exit(2); + } + + *ret_cmd = cmd; +} + + +int +main ( int argc, char **argv) +{ + ARGPARSE_ARGS pargs; + int orig_argc; + char **orig_argv; + const char *fname; + /* char *username;*/ + int may_coredump; + STRLIST sl, remusr= NULL, locusr=NULL; + STRLIST nrings=NULL; + int detached_sig = 0; + FILE *configfp = NULL; + char *configname = NULL; + unsigned configlineno; + int parse_debug = 0; + int no_more_options = 0; + int default_config =1; + int default_keyring = 1; + char *logfile = NULL; + int greeting = 0; + int nogreeting = 0; + int debug_wait = 0; + int use_random_seed = 1; + int with_fpr = 0; + char *def_digest_string = NULL; + enum cmd_and_opt_values cmd = 0; + struct server_control_s ctrl; + CERTLIST recplist = NULL; + CERTLIST signerlist = NULL; + + /* trap_unaligned ();*/ + set_strusage (my_strusage); + gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); + /* We don't need any locking in libgcrypt unless we use any kind of + threading. */ + gcry_control (GCRYCTL_DISABLE_INTERNAL_LOCKING); + + /* Please note that we may running SUID(ROOT), so be very CAREFUL + when adding any stuff between here and the call to secmem_init() + somewhere after the option parsing */ + log_set_prefix ("gpgsm", 1); + /* check that the libraries are suitable. Do it here because the + option parse may need services of the library */ + if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) ) + { + log_fatal( _("libgcrypt is too old (need %s, have %s)\n"), + NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) ); + } + if (!ksba_check_version (NEED_KSBA_VERSION) ) + { + log_fatal( _("libksba is too old (need %s, have %s)\n"), + NEED_KSBA_VERSION, ksba_check_version (NULL) ); + } + + gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); + + may_coredump = disable_core_dumps (); + + gnupg_init_signals (0, emergency_cleanup); + + create_dotlock (NULL); /* register locking cleanup */ + i18n_init(); + + opt.def_cipher_algoid = "1.2.840.113549.3.7"; /*des-EDE3-CBC*/ +#ifdef __MINGW32__ + opt.homedir = read_w32_registry_string ( NULL, + "Software\\GNU\\GnuPG", "HomeDir" ); +#else + opt.homedir = getenv ("GNUPGHOME"); +#endif + if (!opt.homedir || !*opt.homedir ) + opt.homedir = GNUPG_DEFAULT_HOMEDIR; + + /* first check whether we have a config file on the commandline */ + orig_argc = argc; + orig_argv = argv; + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags= 1|(1<<6); /* do not remove the args, ignore version */ + while (arg_parse( &pargs, opts)) + { + if (pargs.r_opt == oDebug || pargs.r_opt == oDebugAll) + parse_debug++; + else if (pargs.r_opt == oOptions) + { /* yes there is one, so we do not try the default one but + read the config file when it is encountered at the + commandline */ + default_config = 0; + } + else if (pargs.r_opt == oNoOptions) + default_config = 0; /* --no-options */ + else if (pargs.r_opt == oHomedir) + opt.homedir = pargs.r.ret_str; + else if (pargs.r_opt == aCallProtectTool) + break; /* This break makes sure that --version and --help are + passed to the protect-tool. */ + } + + + /* initialize the secure memory. */ + gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); + maybe_setuid = 0; + + /* + Now we are now working under our real uid + */ + + ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free ); + assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free); + keybox_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free); + + /* Setup a default control structure for command line mode */ + memset (&ctrl, 0, sizeof ctrl); + gpgsm_init_default_ctrl (&ctrl); + ctrl.no_server = 1; + ctrl.status_fd = -1; /* not status output */ + ctrl.autodetect_encoding = 1; + + /* set the default option file */ + if (default_config ) + configname = make_filename (opt.homedir, "gpgsm.conf", NULL); + /* cet the default policy file */ + opt.policy_file = make_filename (opt.homedir, "policies.txt", NULL); + + argc = orig_argc; + argv = orig_argv; + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags = 1; /* do not remove the args */ + + next_pass: + if (configname) { + configlineno = 0; + configfp = fopen (configname, "r"); + if (!configfp) + { + if (default_config) + { + if (parse_debug) + log_info (_("NOTE: no default option file `%s'\n"), configname); + } + else + { + log_error (_("option file `%s': %s\n"), configname, strerror(errno)); + gpgsm_exit(2); + } + xfree(configname); + configname = NULL; + } + if (parse_debug && configname) + log_info (_("reading options from `%s'\n"), configname); + default_config = 0; + } + + while (!no_more_options + && optfile_parse (configfp, configname, &configlineno, &pargs, opts)) + { + switch (pargs.r_opt) + { + case aServer: + opt.batch = 1; + set_cmd (&cmd, aServer); + break; + case aCallDirmngr: + opt.batch = 1; + set_cmd (&cmd, aCallDirmngr); + break; + + case aCallProtectTool: + opt.batch = 1; + set_cmd (&cmd, aCallProtectTool); + no_more_options = 1; /* Stop parsing. */ + break; + + case aCheckKeys: set_cmd (&cmd, aCheckKeys); break; + case aImport: set_cmd (&cmd, aImport); break; + case aSendKeys: set_cmd (&cmd, aSendKeys); break; + case aRecvKeys: set_cmd (&cmd, aRecvKeys); break; + case aExport: set_cmd (&cmd, aExport); break; + case aListKeys: set_cmd (&cmd, aListKeys); break; + case aListExternalKeys: set_cmd (&cmd, aListExternalKeys); break; + case aListSecretKeys: set_cmd (&cmd, aListSecretKeys); break; + case aListSigs: set_cmd (&cmd, aListSigs); break; + + case aLearnCard: set_cmd (&cmd, aLearnCard); break; + + case aPasswd: set_cmd (&cmd, aPasswd); break; + + case aDeleteKey: + set_cmd (&cmd, aDeleteKey); + /*greeting=1;*/ + break; + + case aDetachedSign: + detached_sig = 1; + set_cmd (&cmd, aSign ); + break; + + case aSym: set_cmd (&cmd, aSym); break; + case aDecrypt: set_cmd (&cmd, aDecrypt); break; + case aEncr: set_cmd (&cmd, aEncr); break; + case aSign: set_cmd (&cmd, aSign ); break; + case aKeygen: set_cmd (&cmd, aKeygen); greeting=1; break; + case aClearsign: set_cmd (&cmd, aClearsign); break; + case aVerify: set_cmd (&cmd, aVerify); break; + + + /* output encoding selection */ + case oArmor: + ctrl.create_pem = 1; + break; + case oBase64: + ctrl.create_pem = 0; + ctrl.create_base64 = 1; + break; + case oNoArmor: + ctrl.create_pem = 0; + ctrl.create_base64 = 0; + break; + + /* Input encoding selection */ + case oAssumeArmor: + ctrl.autodetect_encoding = 0; + ctrl.is_pem = 1; + ctrl.is_base64 = 0; + break; + case oAssumeBase64: + ctrl.autodetect_encoding = 0; + ctrl.is_pem = 0; + ctrl.is_base64 = 1; + break; + case oAssumeBinary: + ctrl.autodetect_encoding = 0; + ctrl.is_pem = 0; + ctrl.is_base64 = 0; + break; + + case oDisableCRLChecks: + opt.no_crl_check = 1; + break; + case oEnableCRLChecks: + opt.no_crl_check = 0; + break; + + case oIncludeCerts: ctrl.include_certs = pargs.r.ret_int; break; + + case oPolicyFile: + xfree (opt.policy_file); + if (*pargs.r.ret_str) + opt.policy_file = xstrdup (pargs.r.ret_str); + else + opt.policy_file = NULL; + break; + + case oDisablePolicyChecks: + opt.no_policy_check = 1; + break; + case oEnablePolicyChecks: + opt.no_policy_check = 0; + break; + + case oAutoIssuerKeyRetrieve: + opt.auto_issuer_key_retrieve = 1; + break; + + case oOutput: opt.outfile = pargs.r.ret_str; break; + + + case oQuiet: opt.quiet = 1; break; + case oNoTTY: /* fixme:tty_no_terminal(1);*/ break; + case oDryRun: opt.dry_run = 1; break; + + case oVerbose: + opt.verbose++; + gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); + break; + case oNoVerbose: + opt.verbose = 0; + gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); + break; + + case oLogFile: logfile = pargs.r.ret_str; break; + + case oBatch: + opt.batch = 1; + greeting = 0; + break; + case oNoBatch: opt.batch = 0; break; + + case oAnswerYes: opt.answer_yes = 1; break; + case oAnswerNo: opt.answer_no = 1; break; + + case oKeyring: append_to_strlist (&nrings, pargs.r.ret_str); break; + + case oDebug: opt.debug |= pargs.r.ret_ulong; break; + case oDebugAll: opt.debug = ~0; break; + case oDebugWait: debug_wait = pargs.r.ret_int; break; + case oDebugNoChainValidation: opt.no_chain_validation = 1; break; + + case oStatusFD: ctrl.status_fd = pargs.r.ret_int; break; + case oLoggerFD: log_set_fd (pargs.r.ret_int ); break; + case oWithFingerprint: + with_fpr=1; /*fall thru*/ + case oFingerprint: + opt.fingerprint++; + break; + + case oOptions: + /* config files may not be nested (silently ignore them) */ + if (!configfp) + { + xfree(configname); + configname = xstrdup (pargs.r.ret_str); + goto next_pass; + } + break; + case oNoOptions: break; /* no-options */ + case oHomedir: opt.homedir = pargs.r.ret_str; break; + case oAgentProgram: opt.agent_program = pargs.r.ret_str; break; + case oDisplay: opt.display = xstrdup (pargs.r.ret_str); break; + case oTTYname: opt.ttyname = xstrdup (pargs.r.ret_str); break; + case oTTYtype: opt.ttytype = xstrdup (pargs.r.ret_str); break; + case oLCctype: opt.lc_ctype = xstrdup (pargs.r.ret_str); break; + case oLCmessages: opt.lc_messages = xstrdup (pargs.r.ret_str); break; + case oDirmngrProgram: opt.dirmngr_program = pargs.r.ret_str; break; + + case oFakedSystemTime: + gnupg_set_time ( (time_t)pargs.r.ret_ulong, 0); + break; + + case oNoDefKeyring: default_keyring = 0; break; + case oNoGreeting: nogreeting = 1; break; + + case oDefaultKey: + /* fixme:opt.def_secret_key = pargs.r.ret_str;*/ + break; + case oDefRecipient: + if (*pargs.r.ret_str) + opt.def_recipient = xstrdup (pargs.r.ret_str); + break; + case oDefRecipientSelf: + xfree (opt.def_recipient); + opt.def_recipient = NULL; + opt.def_recipient_self = 1; + break; + case oNoDefRecipient: + xfree (opt.def_recipient); + opt.def_recipient = NULL; + opt.def_recipient_self = 0; + break; + + case oWithKeyData: opt.with_key_data=1; /* fall thru */ + case oWithColons: ctrl.with_colons = 1; break; + + case oSkipVerify: opt.skip_verify=1; break; + + case oNoEncryptTo: /*fixme: opt.no_encrypt_to = 1;*/ break; + case oEncryptTo: /* store the recipient in the second list */ + sl = add_to_strlist (&remusr, pargs.r.ret_str); + sl->flags = 1; + break; + + case oRecipient: /* store the recipient */ + add_to_strlist ( &remusr, pargs.r.ret_str); + break; + + case oTextmodeShort: /*fixme:opt.textmode = 2;*/ break; + case oTextmode: /*fixme:opt.textmode=1;*/ break; + + case oUser: /* store the local users, the first one is the default */ + if (!opt.local_user) + opt.local_user = pargs.r.ret_str; + add_to_strlist (&locusr, pargs.r.ret_str); + break; + + case oNoSecmemWarn: + gcry_control (GCRYCTL_DISABLE_SECMEM_WARN); + break; + + case oCipherAlgo: + opt.def_cipher_algoid = pargs.r.ret_str; + break; + + case oDisableCipherAlgo: + { + int algo = gcry_cipher_map_name (pargs.r.ret_str); + gcry_cipher_ctl (NULL, GCRYCTL_DISABLE_ALGO, &algo, sizeof algo); + } + break; + case oDisablePubkeyAlgo: + { + int algo = gcry_pk_map_name (pargs.r.ret_str); + gcry_pk_ctl (GCRYCTL_DISABLE_ALGO,&algo, sizeof algo ); + } + break; + + case oIgnoreTimeConflict: opt.ignore_time_conflict = 1; break; + case oNoRandomSeedFile: use_random_seed = 0; break; + + case oEnableSpecialFilenames: allow_special_filenames =1; break; + + + case aDummy: + break; + default: + pargs.err = configfp? 1:2; + break; + } + } + + if (configfp) + { + fclose (configfp); + configfp = NULL; + xfree (configname); + configname = NULL; + goto next_pass; + } + + xfree (configname); + configname = NULL; + + if (log_get_errorcount(0)) + gpgsm_exit(2); + + if (nogreeting) + greeting = 0; + + if (greeting) + { + fprintf(stderr, "%s %s; %s\n", + strusage(11), strusage(13), strusage(14) ); + fprintf(stderr, "%s\n", strusage(15) ); + } +# ifdef IS_DEVELOPMENT_VERSION + if (!opt.batch) + { + log_info ("NOTE: THIS IS A DEVELOPMENT VERSION!\n"); + log_info ("It is only intended for test purposes and should NOT be\n"); + log_info ("used in a production environment or with production keys!\n"); + } +# endif + + if (may_coredump && !opt.quiet) + log_info (_("WARNING: program may create a core file!\n")); + + if (logfile && cmd == aServer) + { + log_set_file (logfile); + log_set_prefix (NULL, 1|2|4); + } + + if (gnupg_faked_time_p ()) + { + log_info (_("WARNING: running with faked system time: ")); + gpgsm_dump_time (gnupg_get_time ()); + log_printf ("\n"); + } + +/*FIXME if (opt.batch) */ +/* tty_batchmode (1); */ + + gcry_control (GCRYCTL_RESUME_SECMEM_WARN); + + set_debug (); + + /* FIXME: should set filenames of libgcrypt explicitly + * gpg_opt_homedir = opt.homedir; */ + + /* must do this after dropping setuid, because the mapping functions + may try to load an module and we may have disabled an algorithm */ + if ( !gcry_cipher_map_name (opt.def_cipher_algoid) + || !gcry_cipher_mode_from_oid (opt.def_cipher_algoid)) + log_error (_("selected cipher algorithm is invalid\n")); + + if (def_digest_string) + { + opt.def_digest_algo = gcry_md_map_name (def_digest_string); + xfree (def_digest_string); + def_digest_string = NULL; + if (our_md_test_algo(opt.def_digest_algo) ) + log_error (_("selected digest algorithm is invalid\n")); + } + + if (log_get_errorcount(0)) + gpgsm_exit(2); + + /* set the random seed file */ + if (use_random_seed) { + char *p = make_filename (opt.homedir, "random_seed", NULL); + gcry_control (GCRYCTL_SET_RANDOM_SEED_FILE, p); + xfree(p); + } + + + if (!cmd && opt.fingerprint && !with_fpr) + set_cmd (&cmd, aListKeys); + + if (!nrings && default_keyring) /* add default keybox */ + keydb_add_resource ("pubring.kbx", 0, 0); + for (sl = nrings; sl; sl = sl->next) + keydb_add_resource (sl->d, 0, 0); + FREE_STRLIST(nrings); + + + for (sl = locusr; sl; sl = sl->next) + { + int rc = gpgsm_add_to_certlist (&ctrl, sl->d, 1, &signerlist); + if (rc) + { + log_error (_("can't sign using `%s': %s\n"), + sl->d, gpg_strerror (rc)); + gpgsm_status2 (&ctrl, STATUS_INV_RECP, + gpg_err_code (rc) == -1? "1": + gpg_err_code (rc) == GPG_ERR_NO_PUBKEY? "1": + gpg_err_code (rc) == GPG_ERR_AMBIGUOUS_NAME? "2": + gpg_err_code (rc) == GPG_ERR_WRONG_KEY_USAGE? "3": + gpg_err_code (rc) == GPG_ERR_CERT_REVOKED? "4": + gpg_err_code (rc) == GPG_ERR_CERT_EXPIRED? "5": + gpg_err_code (rc) == GPG_ERR_NO_CRL_KNOWN? "6": + gpg_err_code (rc) == GPG_ERR_CRL_TOO_OLD? "7": + gpg_err_code (rc) == GPG_ERR_NO_POLICY_MATCH? "8": + gpg_err_code (rc) == GPG_ERR_NO_SECKEY? "9": + "0", + sl->d, NULL); + } + } + for (sl = remusr; sl; sl = sl->next) + { + int rc = gpgsm_add_to_certlist (&ctrl, sl->d, 0, &recplist); + if (rc) + { + log_error (_("can't encrypt to `%s': %s\n"), + sl->d, gpg_strerror (rc)); + gpgsm_status2 (&ctrl, STATUS_INV_RECP, + gpg_err_code (rc) == -1? "1": + gpg_err_code (rc) == GPG_ERR_NO_PUBKEY? "1": + gpg_err_code (rc) == GPG_ERR_AMBIGUOUS_NAME? "2": + gpg_err_code (rc) == GPG_ERR_WRONG_KEY_USAGE? "3": + gpg_err_code (rc) == GPG_ERR_CERT_REVOKED? "4": + gpg_err_code (rc) == GPG_ERR_CERT_EXPIRED? "5": + gpg_err_code (rc) == GPG_ERR_NO_CRL_KNOWN? "6": + gpg_err_code (rc) == GPG_ERR_CRL_TOO_OLD? "7": + gpg_err_code (rc) == GPG_ERR_NO_POLICY_MATCH? "8": + "0", + sl->d, NULL); + } + } + if (log_get_errorcount(0)) + gpgsm_exit(1); /* must stop for invalid recipients */ + + + + fname = argc? *argv : NULL; + + switch (cmd) + { + case aServer: + if (debug_wait) + { + log_debug ("waiting for debugger - my pid is %u .....\n", + (unsigned int)getpid()); + sleep (debug_wait); + log_debug ("... okay\n"); + } + gpgsm_server (); + break; + + case aCallDirmngr: + if (!argc) + wrong_args (_("--call-dirmngr {args}")); + else + if (gpgsm_dirmngr_run_command (&ctrl, *argv, argc-1, argv+1)) + gpgsm_exit (1); + break; + + case aCallProtectTool: + run_protect_tool (argc, argv); + break; + + case aEncr: /* encrypt the given file */ + if (!argc) + gpgsm_encrypt (&ctrl, recplist, 0, stdout); /* from stdin */ + else if (argc == 1) + gpgsm_encrypt (&ctrl, recplist, open_read (*argv), stdout); /* from file */ + else + wrong_args (_("--encrypt [datafile]")); + break; + + case aSign: /* sign the given file */ + /* FIXME: We don't handle --output yet. We should also allow + to concatenate multiple files for signing because that is + what gpg does.*/ + if (!argc) + gpgsm_sign (&ctrl, signerlist, + 0, detached_sig, stdout); /* create from stdin */ + else if (argc == 1) + gpgsm_sign (&ctrl, signerlist, + open_read (*argv), detached_sig, stdout); /* from file */ + else + wrong_args (_("--sign [datafile]")); + break; + + case aSignEncr: /* sign and encrypt the given file */ + log_error ("this command has not yet been implemented\n"); + break; + + case aClearsign: /* make a clearsig */ + log_error ("this command has not yet been implemented\n"); + break; + + case aVerify: + { + FILE *fp = NULL; + + if (argc == 2 && opt.outfile) + log_info ("option --output ignored for a detached signature\n"); + else if (opt.outfile) + fp = open_fwrite (opt.outfile); + + if (!argc) + gpgsm_verify (&ctrl, 0, -1, fp); /* normal signature from stdin */ + else if (argc == 1) + gpgsm_verify (&ctrl, open_read (*argv), -1, fp); /* std signature */ + else if (argc == 2) /* detached signature (sig, detached) */ + gpgsm_verify (&ctrl, open_read (*argv), open_read (argv[1]), NULL); + else + wrong_args (_("--verify [signature [detached_data]]")); + + if (fp && fp != stdout) + fclose (fp); + } + break; + + case aVerifyFiles: + log_error ("this command has not yet been implemented\n"); + break; + + case aDecrypt: + if (!argc) + gpgsm_decrypt (&ctrl, 0, stdout); /* from stdin */ + else if (argc == 1) + gpgsm_decrypt (&ctrl, open_read (*argv), stdout); /* from file */ + else + wrong_args (_("--decrypt [filename]")); + break; + + case aDeleteKey: + for (sl=NULL; argc; argc--, argv++) + add_to_strlist (&sl, *argv); + gpgsm_delete (&ctrl, sl); + free_strlist(sl); + break; + + case aListSigs: + ctrl.with_chain = 1; + case aListKeys: + for (sl=NULL; argc; argc--, argv++) + add_to_strlist (&sl, *argv); + gpgsm_list_keys (&ctrl, sl, stdout, (0 | (1<<6))); + free_strlist(sl); + break; + + case aListExternalKeys: + for (sl=NULL; argc; argc--, argv++) + add_to_strlist (&sl, *argv); + gpgsm_list_keys (&ctrl, sl, stdout, (0 | (1<<7))); + free_strlist(sl); + break; + + case aListSecretKeys: + for (sl=NULL; argc; argc--, argv++) + add_to_strlist (&sl, *argv); + gpgsm_list_keys (&ctrl, sl, stdout, (2 | (1<<6))); + free_strlist(sl); + break; + + case aKeygen: /* generate a key */ + log_error ("this function is not yet available from the commandline\n"); + break; + + case aImport: + gpgsm_import_files (&ctrl, argc, argv, open_read); + break; + + case aExport: + for (sl=NULL; argc; argc--, argv++) + add_to_strlist (&sl, *argv); + gpgsm_export (&ctrl, sl, stdout); + free_strlist(sl); + break; + + + case aSendKeys: + case aRecvKeys: + log_error ("this command has not yet been implemented\n"); + break; + + + case aLearnCard: + if (argc) + wrong_args ("--learn-card"); + else + { + int rc = gpgsm_agent_learn (); + if (rc) + log_error ("error learning card: %s\n", gpg_strerror (rc)); + } + break; + + case aPasswd: + if (argc != 1) + wrong_args ("--passwd "); + else + { + int rc; + KsbaCert cert = NULL; + char *grip = NULL; + + rc = gpgsm_find_cert (*argv, &cert); + if (rc) + ; + else if (!(grip = gpgsm_get_keygrip_hexstring (cert))) + rc = gpg_error (GPG_ERR_BUG); + else + rc = gpgsm_agent_passwd (grip); + if (rc) + log_error ("error changing passphrase: %s\n", gpg_strerror (rc)); + xfree (grip); + ksba_cert_release (cert); + } + break; + + default: + log_error ("invalid command (there is no implicit command)\n"); + break; + } + + /* cleanup */ + gpgsm_release_certlist (recplist); + gpgsm_release_certlist (signerlist); + FREE_STRLIST(remusr); + FREE_STRLIST(locusr); + gpgsm_exit(0); + return 8; /*NEVER REACHED*/ +} + +/* Note: This function is used by signal handlers!. */ +static void +emergency_cleanup (void) +{ + gcry_control (GCRYCTL_TERM_SECMEM ); +} + + +void +gpgsm_exit (int rc) +{ + gcry_control (GCRYCTL_UPDATE_RANDOM_SEED_FILE); + if (opt.debug & DBG_MEMSTAT_VALUE) + { + gcry_control( GCRYCTL_DUMP_MEMORY_STATS ); + gcry_control( GCRYCTL_DUMP_RANDOM_STATS ); + } + if (opt.debug) + gcry_control (GCRYCTL_DUMP_SECMEM_STATS ); + emergency_cleanup (); + rc = rc? rc : log_get_errorcount(0)? 2 : gpgsm_errors_seen? 1 : 0; + exit (rc); +} + + +void +gpgsm_init_default_ctrl (struct server_control_s *ctrl) +{ + ctrl->include_certs = 1; /* only include the signer's cert */ +} + + + +/* Check whether the filename has the form "-&nnnn", where n is a + non-zero number. Returns this number or -1 if it is not the case. */ +static int +check_special_filename (const char *fname) +{ + if (allow_special_filenames + && fname && *fname == '-' && fname[1] == '&' ) { + int i; + + fname += 2; + for (i=0; isdigit (fname[i]); i++ ) + ; + if ( !fname[i] ) + return atoi (fname); + } + return -1; +} + + + +/* Open the FILENAME for read and return the filedescriptor. Stop + with an error message in case of problems. "-" denotes stdin and + if special filenames are allowed the given fd is opened instead. */ +static int +open_read (const char *filename) +{ + int fd; + + if (filename[0] == '-' && !filename[1]) + return 0; /* stdin */ + fd = check_special_filename (filename); + if (fd != -1) + return fd; + fd = open (filename, O_RDONLY); + if (fd == -1) + { + log_error (_("can't open `%s': %s\n"), filename, strerror (errno)); + gpgsm_exit (2); + } + return fd; +} + +/* Open FILENAME for fwrite and return the stream. Stop with an error + message in case of problems. "-" denotes stdout and if special + filenames are allowed the given fd is opened instead. Caller must + close the returned stream unless it is stdout. */ +static FILE * +open_fwrite (const char *filename) +{ + int fd; + FILE *fp; + + if (filename[0] == '-' && !filename[1]) + return stdout; + + fd = check_special_filename (filename); + if (fd != -1) + { + fp = fdopen (dup (fd), "wb"); + if (!fp) + { + log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno)); + gpgsm_exit (2); + } + return fp; + } + fp = fopen (filename, "wb"); + if (!fp) + { + log_error (_("can't open `%s': %s\n"), filename, strerror (errno)); + gpgsm_exit (2); + } + return fp; +} + + +static void +run_protect_tool (int argc, char **argv) +{ + char *pgm = GNUPG_PROTECT_TOOL; + char **av; + int i; + + av = xcalloc (argc+2, sizeof *av); + av[0] = strrchr (pgm, '/'); + if (!av[0]) + av[0] = pgm; + for (i=1; argc; i++, argc--, argv++) + av[i] = *argv; + av[i] = NULL; + execv (pgm, av); + log_error ("error executing `%s': %s\n", pgm, strerror (errno)); + gpgsm_exit (2); +} + diff --git a/sm/gpgsm.h b/sm/gpgsm.h new file mode 100644 index 000000000..f996d578c --- /dev/null +++ b/sm/gpgsm.h @@ -0,0 +1,274 @@ +/* gpgsm.h - Global definitions for GpgSM + * Copyright (C) 2001, 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 GPGSM_H +#define GPGSM_H + +#ifdef GPG_ERR_SOURCE_DEFAULT +#error GPG_ERR_SOURCE_DEFAULT already defined +#endif +#define GPG_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_GPGSM +#include + +#include +#include "../common/util.h" +#include "../common/errors.h" + +#define OUT_OF_CORE(a) (gpg_error (gpg_err_code_from_errno ((a)))) + +#define MAX_DIGEST_LEN 24 + +/* A large struct name "opt" to keep global flags */ +struct { + unsigned int debug; /* debug flags (DBG_foo_VALUE) */ + int verbose; /* verbosity level */ + int quiet; /* be as quiet as possible */ + int batch; /* run in batch mode, i.e w/o any user interaction */ + int answer_yes; /* assume yes on most questions */ + int answer_no; /* assume no on most questions */ + int dry_run; /* don't change any persistent data */ + + const char *homedir; /* configuration directory name */ + const char *agent_program; + char *display; + char *ttyname; + char *ttytype; + char *lc_ctype; + char *lc_messages; + + const char *dirmngr_program; + char *outfile; /* name of output file */ + + int with_key_data;/* include raw key in the column delimted output */ + + int fingerprint; /* list fingerprints in all key listings */ + + int armor; /* force base64 armoring (see also ctrl.with_base64) */ + int no_armor; /* don't try to figure out whether data is base64 armored*/ + + const char *def_cipher_algoid; /* cipher algorithm to use if + nothing else is specified */ + + int def_digest_algo; /* Ditto for hash algorithm */ + int def_compress_algo; /* Ditto for compress algorithm */ + + char *def_recipient; /* userID of the default recipient */ + int def_recipient_self; /* The default recipient is the default key */ + + char *local_user; /* NULL or argument to -u */ + + int always_trust; /* Trust the given keys even if there is no + valid certification chain */ + int skip_verify; /* do not check signatures on data */ + + int lock_once; /* Keep lock once they are set */ + + int ignore_time_conflict; /* Ignore certain time conflicts */ + + int no_crl_check; /* Don't do a CRL check */ + + char *policy_file; /* full pathname of policy file */ + int no_policy_check; /* ignore certificate policies */ + int no_chain_validation; /* Bypass all cert chain validity tests */ + + int auto_issuer_key_retrieve; /* try to retrieve a missing issuer key. */ +} opt; + + +#define DBG_X509_VALUE 1 /* debug x.509 data reading/writing */ +#define DBG_MPI_VALUE 2 /* debug mpi details */ +#define DBG_CRYPTO_VALUE 4 /* debug low level crypto */ +#define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */ +#define DBG_CACHE_VALUE 64 /* debug the caching */ +#define DBG_MEMSTAT_VALUE 128 /* show memory statistics */ +#define DBG_HASHING_VALUE 512 /* debug hashing operations */ +#define DBG_ASSUAN_VALUE 1024 /* debug assuan communication */ + +#define DBG_X509 (opt.debug & DBG_X509_VALUE) +#define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE) +#define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE) +#define DBG_CACHE (opt.debug & DBG_CACHE_VALUE) +#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE) +#define DBG_ASSUAN (opt.debug & DBG_ASSUAN_VALUE) + +struct server_local_s; + +/* Note that the default values for this are set by + gpgsm_init_default_ctrl() */ +struct server_control_s { + int no_server; /* we are not running under server control */ + int status_fd; /* only for non-server mode */ + struct server_local_s *server_local; + int with_colons; /* use column delimited output format */ + int with_chain; /* include the certifying certs in a listing */ + + int autodetect_encoding; /* try to detect the input encoding */ + int is_pem; /* Is in PEM format */ + int is_base64; /* is in plain base-64 format */ + + int create_base64; /* Create base64 encoded output */ + int create_pem; /* create PEM output */ + const char *pem_name; /* PEM name to use */ + + int include_certs; /* -1 to send all certificates in the chain + along with a signature or the number of + certificates up the chain (0 = none, 1 = only + signer) */ +}; +typedef struct server_control_s *CTRL; + +/* data structure used in base64.c */ +typedef struct base64_context_s *Base64Context; + + +struct certlist_s { + struct certlist_s *next; + KsbaCert cert; +}; +typedef struct certlist_s *CERTLIST; + +/*-- gpgsm.c --*/ +void gpgsm_exit (int rc); +void gpgsm_init_default_ctrl (struct server_control_s *ctrl); + +/*-- server.c --*/ +void gpgsm_server (void); +void gpgsm_status (CTRL ctrl, int no, const char *text); +void gpgsm_status2 (CTRL ctrl, int no, ...); +void gpgsm_status_with_err_code (CTRL ctrl, int no, const char *text, + gpg_err_code_t ec); + +/*-- fingerprint --*/ +char *gpgsm_get_fingerprint (KsbaCert cert, int algo, char *array, int *r_len); +char *gpgsm_get_fingerprint_string (KsbaCert cert, int algo); +char *gpgsm_get_fingerprint_hexstring (KsbaCert cert, int algo); +unsigned long gpgsm_get_short_fingerprint (KsbaCert cert); +char *gpgsm_get_keygrip (KsbaCert cert, char *array); +char *gpgsm_get_keygrip_hexstring (KsbaCert cert); +char *gpgsm_get_certid (KsbaCert cert); + + +/*-- base64.c --*/ +int gpgsm_create_reader (Base64Context *ctx, + CTRL ctrl, FILE *fp, KsbaReader *r_reader); +void gpgsm_destroy_reader (Base64Context ctx); +int gpgsm_create_writer (Base64Context *ctx, + CTRL ctrl, FILE *fp, KsbaWriter *r_writer); +int gpgsm_finish_writer (Base64Context ctx); +void gpgsm_destroy_writer (Base64Context ctx); + + +/*-- certdump.c --*/ +void gpgsm_print_serial (FILE *fp, KsbaConstSexp p); +void gpgsm_print_time (FILE *fp, time_t t); +void gpgsm_print_name (FILE *fp, const char *string); + +void gpgsm_dump_cert (const char *text, KsbaCert cert); +void gpgsm_dump_serial (KsbaConstSexp p); +void gpgsm_dump_time (time_t t); +void gpgsm_dump_string (const char *string); + + + +/*-- certcheck.c --*/ +int gpgsm_check_cert_sig (KsbaCert issuer_cert, KsbaCert cert); +int gpgsm_check_cms_signature (KsbaCert cert, KsbaConstSexp sigval, + gcry_md_hd_t md, int hash_algo); +/* fixme: move create functions to another file */ +int gpgsm_create_cms_signature (KsbaCert cert, gcry_md_hd_t md, int mdalgo, + char **r_sigval); + + +/*-- certchain.c --*/ +int gpgsm_walk_cert_chain (KsbaCert start, KsbaCert *r_next); +int gpgsm_is_root_cert (KsbaCert cert); +int gpgsm_validate_chain (CTRL ctrl, KsbaCert cert, time_t *r_exptime); +int gpgsm_basic_cert_check (KsbaCert cert); + +/*-- certlist.c --*/ +int gpgsm_cert_use_sign_p (KsbaCert cert); +int gpgsm_cert_use_encrypt_p (KsbaCert cert); +int gpgsm_cert_use_verify_p (KsbaCert cert); +int gpgsm_cert_use_decrypt_p (KsbaCert cert); +int gpgsm_cert_use_cert_p (KsbaCert cert); +int gpgsm_add_to_certlist (CTRL ctrl, const char *name, int secret, + CERTLIST *listaddr); +void gpgsm_release_certlist (CERTLIST list); +int gpgsm_find_cert (const char *name, KsbaCert *r_cert); + +/*-- keylist.c --*/ +void gpgsm_list_keys (CTRL ctrl, STRLIST names, FILE *fp, unsigned int mode); + +/*-- import.c --*/ +int gpgsm_import (CTRL ctrl, int in_fd); +int gpgsm_import_files (CTRL ctrl, int nfiles, char **files, + int (*of)(const char *fname)); + +/*-- export.c --*/ +void gpgsm_export (CTRL ctrl, STRLIST names, FILE *fp); + +/*-- delete.c --*/ +int gpgsm_delete (CTRL ctrl, STRLIST names); + +/*-- verify.c --*/ +int gpgsm_verify (CTRL ctrl, int in_fd, int data_fd, FILE *out_fp); + +/*-- sign.c --*/ +int gpgsm_get_default_cert (KsbaCert *r_cert); +int gpgsm_sign (CTRL ctrl, CERTLIST signerlist, + int data_fd, int detached, FILE *out_fp); + +/*-- encrypt.c --*/ +int gpgsm_encrypt (CTRL ctrl, CERTLIST recplist, int in_fd, FILE *out_fp); + +/*-- decrypt.c --*/ +int gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *out_fp); + +/*-- certreqgen.c --*/ +int gpgsm_genkey (CTRL ctrl, int in_fd, FILE *out_fp); + +/*-- call-agent.c --*/ +int gpgsm_agent_pksign (const char *keygrip, + unsigned char *digest, + size_t digestlen, + int digestalgo, + char **r_buf, size_t *r_buflen); +int gpgsm_agent_pkdecrypt (const char *keygrip, + KsbaConstSexp ciphertext, + char **r_buf, size_t *r_buflen); +int gpgsm_agent_genkey (KsbaConstSexp keyparms, KsbaSexp *r_pubkey); +int gpgsm_agent_istrusted (KsbaCert cert); +int gpgsm_agent_havekey (const char *hexkeygrip); +int gpgsm_agent_marktrusted (KsbaCert cert); +int gpgsm_agent_learn (void); +int gpgsm_agent_passwd (const char *hexkeygrip); + +/*-- call-dirmngr.c --*/ +int gpgsm_dirmngr_isvalid (KsbaCert cert); +int gpgsm_dirmngr_lookup (CTRL ctrl, STRLIST names, + void (*cb)(void*, KsbaCert), void *cb_value); +int gpgsm_dirmngr_run_command (CTRL ctrl, const char *command, + int argc, char **argv); + + + + + +#endif /*GPGSM_H*/ diff --git a/sm/import.c b/sm/import.c new file mode 100644 index 000000000..17dc3d66c --- /dev/null +++ b/sm/import.c @@ -0,0 +1,349 @@ +/* import.c - Import certificates + * Copyright (C) 2001, 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 +#include + +#include "gpgsm.h" +#include +#include + +#include "keydb.h" +#include "i18n.h" + +struct stats_s { + unsigned long count; + unsigned long imported; + unsigned long unchanged; + unsigned long not_imported; +}; + + + +static void +print_imported_status (CTRL ctrl, KsbaCert cert) +{ + char *fpr; + + fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); + gpgsm_status2 (ctrl, STATUS_IMPORTED, fpr, "[X.509]", NULL); + xfree (fpr); +} + + +/* Print an IMPORT_PROBLEM status. REASON is one of: + 0 := "No specific reason given". + 1 := "Invalid Certificate". + 2 := "Issuer Certificate missing". + 3 := "Certificate Chain too long". + 4 := "Error storing certificate". +*/ +static void +print_import_problem (CTRL ctrl, KsbaCert cert, int reason) +{ + char *fpr = NULL; + char buf[25]; + int i; + + sprintf (buf, "%d", reason); + if (cert) + { + fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); + /* detetect an error (all high) value */ + for (i=0; fpr[i] == 'F'; i++) + ; + if (!fpr[i]) + { + xfree (fpr); + fpr = NULL; + } + } + gpgsm_status2 (ctrl, STATUS_IMPORT_PROBLEM, buf, fpr, NULL); + xfree (fpr); +} + + +void +print_imported_summary (CTRL ctrl, struct stats_s *stats) +{ + char buf[14*25]; + + if (!opt.quiet) + { + log_info (_("total number processed: %lu\n"), stats->count); + if (stats->imported) + { + log_info (_(" imported: %lu"), stats->imported ); + log_printf ("\n"); + } + if (stats->unchanged) + log_info (_(" unchanged: %lu\n"), stats->unchanged); + if (stats->not_imported) + log_info (_(" not imported: %lu\n"), stats->not_imported); + } + + sprintf (buf, "%lu 0 %lu 0 %lu 0 0 0 0 0 0 0 0 %lu", + stats->count, + stats->imported, + stats->unchanged, + stats->not_imported + ); + gpgsm_status (ctrl, STATUS_IMPORT_RES, buf); +} + + + +static void +check_and_store (CTRL ctrl, struct stats_s *stats, KsbaCert cert, int depth) +{ + int rc; + + stats->count++; + if ( depth >= 50 ) + { + log_error (_("certificate chain too long\n")); + stats->not_imported++; + print_import_problem (ctrl, cert, 3); + return; + } + + rc = gpgsm_basic_cert_check (cert); + if (!rc) + { + int existed; + + if (!keydb_store_cert (cert, 0, &existed)) + { + KsbaCert next = NULL; + + if (!existed) + { + print_imported_status (ctrl, cert); + stats->imported++; + } + else + stats->unchanged++; + + if (opt.verbose > 1 && existed) + { + if (depth) + log_info ("issuer certificate already in DB\n"); + else + log_info ("certificate already in DB\n"); + } + else if (opt.verbose && !existed) + { + if (depth) + log_info ("issuer certificate imported\n"); + else + log_info ("certificate imported\n"); + } + /* Now lets walk up the chain and import all certificates up + the chain.*/ + else if (!gpgsm_walk_cert_chain (cert, &next)) + { + check_and_store (ctrl, stats, next, depth+1); + ksba_cert_release (next); + } + } + else + { + log_error (_("error storing certificate\n")); + stats->not_imported++; + print_import_problem (ctrl, cert, 4); + } + } + else + { + log_error (_("basic certificate checks failed - not imported\n")); + stats->not_imported++; + print_import_problem (ctrl, cert, + gpg_err_code (rc) == GPG_ERR_MISSING_CERT? 2 : + gpg_err_code (rc) == GPG_ERR_BAD_CERT? 1 : 0); + } +} + + + + +static int +import_one (CTRL ctrl, struct stats_s *stats, int in_fd) +{ + int rc; + Base64Context b64reader = NULL; + KsbaReader reader; + KsbaCert cert = NULL; + KsbaCMS cms = NULL; + FILE *fp = NULL; + KsbaContentType ct; + + fp = fdopen ( dup (in_fd), "rb"); + if (!fp) + { + rc = gpg_error (gpg_err_code_from_errno (errno)); + log_error ("fdopen() failed: %s\n", strerror (errno)); + goto leave; + } + + rc = gpgsm_create_reader (&b64reader, ctrl, fp, &reader); + if (rc) + { + log_error ("can't create reader: %s\n", gpg_strerror (rc)); + goto leave; + } + + ct = ksba_cms_identify (reader); + if (ct == KSBA_CT_SIGNED_DATA) + { /* This is probably a signed-only message - import the certs */ + KsbaStopReason stopreason; + int i; + + cms = ksba_cms_new (); + if (!cms) + { + rc = gpg_error (GPG_ERR_ENOMEM); + goto leave; + } + + rc = ksba_cms_set_reader_writer (cms, reader, NULL); + if (rc) + { + log_error ("ksba_cms_set_reader_writer failed: %s\n", + ksba_strerror (rc)); + rc = map_ksba_err (rc); + goto leave; + } + + + do + { + rc = ksba_cms_parse (cms, &stopreason); + if (rc) + { + log_error ("ksba_cms_parse failed: %s\n", ksba_strerror (rc)); + rc = map_ksba_err (rc); + goto leave; + } + + if (stopreason == KSBA_SR_BEGIN_DATA) + log_info ("not a certs-only message\n"); + } + while (stopreason != KSBA_SR_READY); + + for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++) + { + check_and_store (ctrl, stats, cert, 0); + ksba_cert_release (cert); + cert = NULL; + } + if (!i) + log_error ("no certificate found\n"); + } + else if (ct == KSBA_CT_NONE) + { /* Failed to identify this message - assume a certificate */ + + cert = ksba_cert_new (); + if (!cert) + { + rc = gpg_error (GPG_ERR_ENOMEM); + goto leave; + } + + rc = ksba_cert_read_der (cert, reader); + if (rc) + { + rc = map_ksba_err (rc); + goto leave; + } + + check_and_store (ctrl, stats, cert, 0); + } + else + { + log_error ("can't extract certificates from input\n"); + rc = gpg_error (GPG_ERR_NO_DATA); + } + + leave: + ksba_cms_release (cms); + ksba_cert_release (cert); + gpgsm_destroy_reader (b64reader); + if (fp) + fclose (fp); + return rc; +} + + +int +gpgsm_import (CTRL ctrl, int in_fd) +{ + int rc; + struct stats_s stats; + + memset (&stats, 0, sizeof stats); + rc = import_one (ctrl, &stats, in_fd); + print_imported_summary (ctrl, &stats); + /* If we never printed an error message do it now so that a command + line invocation will return with an error (log_error keeps a + global errorcount) */ + if (rc && !log_get_errorcount (0)) + log_error (_("error importing certificate: %s\n"), gpg_strerror (rc)); + return rc; +} + + +int +gpgsm_import_files (CTRL ctrl, int nfiles, char **files, + int (*of)(const char *fname)) +{ + int rc = 0; + struct stats_s stats; + + memset (&stats, 0, sizeof stats); + + if (!nfiles) + rc = import_one (ctrl, &stats, 0); + else + { + for (; nfiles && !rc ; nfiles--, files++) + { + int fd = of (*files); + rc = import_one (ctrl, &stats, fd); + close (fd); + if (rc == -1) + rc = 0; + } + } + print_imported_summary (ctrl, &stats); + /* If we never printed an error message do it now so that a command + line invocation will return with an error (log_error keeps a + global errorcount) */ + if (rc && !log_get_errorcount (0)) + log_error (_("error importing certificate: %s\n"), gpg_strerror (rc)); + return rc; +} + + diff --git a/sm/keydb.c b/sm/keydb.c new file mode 100644 index 000000000..fe6556549 --- /dev/null +++ b/sm/keydb.c @@ -0,0 +1,1282 @@ +/* keydb.c - key database dispatcher + * Copyright (C) 2001, 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 +#include +#include + +#include "gpgsm.h" +#include "../kbx/keybox.h" +#include "keydb.h" +#include "i18n.h" + +#define DIRSEP_C '/' + +static int active_handles; + +typedef enum { + KEYDB_RESOURCE_TYPE_NONE = 0, + KEYDB_RESOURCE_TYPE_KEYBOX +} KeydbResourceType; +#define MAX_KEYDB_RESOURCES 20 + +struct resource_item { + KeydbResourceType type; + union { + KEYBOX_HANDLE kr; + } u; + void *token; + int secret; + DOTLOCK lockhandle; +}; + +static struct resource_item all_resources[MAX_KEYDB_RESOURCES]; +static int used_resources; + +struct keydb_handle { + int locked; + int found; + int current; + int is_ephemeral; + int used; /* items in active */ + struct resource_item active[MAX_KEYDB_RESOURCES]; +}; + + +static int lock_all (KEYDB_HANDLE hd); +static void unlock_all (KEYDB_HANDLE hd); + + +/* + * Register a resource (which currently may only be a keybox file). + * The first keybox which is added by this function is + * created if it does not exist. + * Note: this function may be called before secure memory is + * available. + */ +int +keydb_add_resource (const char *url, int force, int secret) +{ + static int any_secret, any_public; + const char *resname = url; + char *filename = NULL; + int rc = 0; + FILE *fp; + KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE; + const char *created_fname = NULL; + + /* Do we have an URL? + gnupg-kbx:filename := this is a plain keybox + filename := See what is is, but create as plain keybox. + */ + if (strlen (resname) > 10) + { + if (!strncmp (resname, "gnupg-kbx:", 10) ) + { + rt = KEYDB_RESOURCE_TYPE_KEYBOX; + resname += 10; + } +#if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__) + else if (strchr (resname, ':')) + { + log_error ("invalid key resource URL `%s'\n", url ); + rc = gpg_error (GPG_ERR_GENERAL); + goto leave; + } +#endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */ + } + + if (*resname != DIRSEP_C ) + { /* do tilde expansion etc */ + if (strchr(resname, DIRSEP_C) ) + filename = make_filename (resname, NULL); + else + filename = make_filename (opt.homedir, resname, NULL); + } + else + filename = xstrdup (resname); + + if (!force) + force = secret? !any_secret : !any_public; + + /* see whether we can determine the filetype */ + if (rt == KEYDB_RESOURCE_TYPE_NONE) + { + FILE *fp2 = fopen( filename, "rb" ); + + if (fp2) { + u32 magic; + + /* FIXME: check for the keybox magic */ + if (fread( &magic, 4, 1, fp2) == 1 ) + { + if (magic == 0x13579ace || magic == 0xce9a5713) + ; /* GDBM magic - no more support */ + else + rt = KEYDB_RESOURCE_TYPE_KEYBOX; + } + else /* maybe empty: assume ring */ + rt = KEYDB_RESOURCE_TYPE_KEYBOX; + fclose (fp2); + } + else /* no file yet: create ring */ + rt = KEYDB_RESOURCE_TYPE_KEYBOX; + } + + switch (rt) + { + case KEYDB_RESOURCE_TYPE_NONE: + log_error ("unknown type of key resource `%s'\n", url ); + rc = gpg_error (GPG_ERR_GENERAL); + goto leave; + + case KEYDB_RESOURCE_TYPE_KEYBOX: + fp = fopen (filename, "rb"); + if (!fp && !force) + { + rc = gpg_error (gpg_err_code_from_errno (errno)); + goto leave; + } + + if (!fp) + { /* no file */ +#if 0 /* no autocreate of the homedirectory yet */ + { + char *last_slash_in_filename; + + last_slash_in_filename = strrchr (filename, DIRSEP_C); + *last_slash_in_filename = 0; + if (access (filename, F_OK)) + { /* on the first time we try to create the default + homedir and in this case the process will be + terminated, so that on the next invocation can + read the options file in on startup */ + try_make_homedir (filename); + rc = gpg_error (GPG_ERR_FILE_OPEN_ERROR); + *last_slash_in_filename = DIRSEP_C; + goto leave; + } + *last_slash_in_filename = DIRSEP_C; + } +#endif + fp = fopen (filename, "w"); + if (!fp) + { + rc = gpg_error (gpg_err_code_from_errno (errno)); + log_error (_("error creating keybox `%s': %s\n"), + filename, strerror(errno)); + goto leave; + } + + if (!opt.quiet) + log_info (_("keybox `%s' created\n"), filename); + created_fname = filename; + } + fclose (fp); + fp = NULL; + /* now register the file */ + { + + void *token = keybox_register_file (filename, secret); + if (!token) + ; /* already registered - ignore it */ + else if (used_resources >= MAX_KEYDB_RESOURCES) + rc = gpg_error (GPG_ERR_RESOURCE_LIMIT); + else + { + all_resources[used_resources].type = rt; + all_resources[used_resources].u.kr = NULL; /* Not used here */ + all_resources[used_resources].token = token; + all_resources[used_resources].secret = secret; + + all_resources[used_resources].lockhandle + = create_dotlock (filename); + if (!all_resources[used_resources].lockhandle) + log_fatal ( _("can't create lock for `%s'\n"), filename); + + used_resources++; + } + } + break; + default: + log_error ("resource type of `%s' not supported\n", url); + rc = gpg_error (GPG_ERR_NOT_SUPPORTED); + goto leave; + } + + /* fixme: check directory permissions and print a warning */ + + leave: + if (rc) + log_error ("keyblock resource `%s': %s\n", filename, gpg_strerror(rc)); + else if (secret) + any_secret = 1; + else + any_public = 1; + xfree (filename); + return rc; +} + + +KEYDB_HANDLE +keydb_new (int secret) +{ + KEYDB_HANDLE hd; + int i, j; + + hd = xcalloc (1, sizeof *hd); + hd->found = -1; + + assert (used_resources <= MAX_KEYDB_RESOURCES); + for (i=j=0; i < used_resources; i++) + { + if (!all_resources[i].secret != !secret) + continue; + switch (all_resources[i].type) + { + case KEYDB_RESOURCE_TYPE_NONE: /* ignore */ + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + hd->active[j].type = all_resources[i].type; + hd->active[j].token = all_resources[i].token; + hd->active[j].secret = all_resources[i].secret; + hd->active[j].lockhandle = all_resources[i].lockhandle; + hd->active[j].u.kr = keybox_new (all_resources[i].token, secret); + if (!hd->active[j].u.kr) + { + xfree (hd); + return NULL; /* fixme: release all previously allocated handles*/ + } + j++; + break; + } + } + hd->used = j; + + active_handles++; + return hd; +} + +void +keydb_release (KEYDB_HANDLE hd) +{ + int i; + + if (!hd) + return; + assert (active_handles > 0); + active_handles--; + + unlock_all (hd); + for (i=0; i < hd->used; i++) + { + switch (hd->active[i].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + keybox_release (hd->active[i].u.kr); + break; + } + } + + xfree (hd); +} + + +/* Return the name of the current resource. This is function first + looks for the last found found, then for the current search + position, and last returns the first available resource. The + returned string is only valid as long as the handle exists. This + function does only return NULL if no handle is specified, in all + other error cases an empty string is returned. */ +const char * +keydb_get_resource_name (KEYDB_HANDLE hd) +{ + int idx; + const char *s = NULL; + + if (!hd) + return NULL; + + if ( hd->found >= 0 && hd->found < hd->used) + idx = hd->found; + else if ( hd->current >= 0 && hd->current < hd->used) + idx = hd->current; + else + idx = 0; + + switch (hd->active[idx].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + s = NULL; + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + s = keybox_get_resource_name (hd->active[idx].u.kr); + break; + } + + return s? s: ""; +} + +/* Switch the handle into ephemeral mode and return the orginal value. */ +int +keydb_set_ephemeral (KEYDB_HANDLE hd, int yes) +{ + int i; + + if (!hd) + return 0; + + yes = !!yes; + if (hd->is_ephemeral != yes) + { + for (i=0; i < hd->used; i++) + { + switch (hd->active[i].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + keybox_set_ephemeral (hd->active[i].u.kr, yes); + break; + } + } + } + + i = hd->is_ephemeral; + hd->is_ephemeral = yes; + return i; +} + + + +static int +lock_all (KEYDB_HANDLE hd) +{ + int i, rc = 0; + + /* Fixme: This locking scheme may lead to deadlock if the resources + are not added in the same sequence by all processes. We are + cuurently only allowing one resource so it is not a problem. */ + for (i=0; i < hd->used; i++) + { + switch (hd->active[i].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + if (hd->active[i].lockhandle) + rc = make_dotlock (hd->active[i].lockhandle, -1); + break; + } + if (rc) + break; + } + + if (rc) + { + /* revert the already set locks */ + for (i--; i >= 0; i--) + { + switch (hd->active[i].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + if (hd->active[i].lockhandle) + release_dotlock (hd->active[i].lockhandle); + break; + } + } + } + else + hd->locked = 1; + + return rc; +} + +static void +unlock_all (KEYDB_HANDLE hd) +{ + int i; + + if (!hd->locked) + return; + + for (i=hd->used-1; i >= 0; i--) + { + switch (hd->active[i].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + if (hd->active[i].lockhandle) + release_dotlock (hd->active[i].lockhandle); + break; + } + } + hd->locked = 0; +} + + +#if 0 +/* + * Return the last found keybox. Caller must free it. + * The returned keyblock has the kbode flag bit 0 set for the node with + * the public key used to locate the keyblock or flag bit 1 set for + * the user ID node. + */ +int +keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb) +{ + int rc = 0; + + if (!hd) + return G10ERR_INV_ARG; + + if ( hd->found < 0 || hd->found >= hd->used) + return -1; /* nothing found */ + + switch (hd->active[hd->found].type) { + case KEYDB_RESOURCE_TYPE_NONE: + rc = G10ERR_GENERAL; /* oops */ + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + rc = keybox_get_keyblock (hd->active[hd->found].u.kr, ret_kb); + break; + } + + return rc; +} + +/* + * update the current keyblock with KB + */ +int +keydb_update_keyblock (KEYDB_HANDLE hd, KBNODE kb) +{ + int rc = 0; + + if (!hd) + return G10ERR_INV_ARG; + + if ( hd->found < 0 || hd->found >= hd->used) + return -1; /* nothing found */ + + if( opt.dry_run ) + return 0; + + rc = lock_all (hd); + if (rc) + return rc; + + switch (hd->active[hd->found].type) { + case KEYDB_RESOURCE_TYPE_NONE: + rc = G10ERR_GENERAL; /* oops */ + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + rc = keybox_update_keyblock (hd->active[hd->found].u.kr, kb); + break; + } + + unlock_all (hd); + return rc; +} + + +/* + * Insert a new KB into one of the resources. + */ +int +keydb_insert_keyblock (KEYDB_HANDLE hd, KBNODE kb) +{ + int rc = -1; + int idx; + + if (!hd) + return G10ERR_INV_ARG; + + if( opt.dry_run ) + return 0; + + if ( hd->found >= 0 && hd->found < hd->used) + idx = hd->found; + else if ( hd->current >= 0 && hd->current < hd->used) + idx = hd->current; + else + return G10ERR_GENERAL; + + rc = lock_all (hd); + if (rc) + return rc; + + switch (hd->active[idx].type) { + case KEYDB_RESOURCE_TYPE_NONE: + rc = G10ERR_GENERAL; /* oops */ + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + rc = keybox_insert_keyblock (hd->active[idx].u.kr, kb); + break; + } + + unlock_all (hd); + return rc; +} + +#endif /*disabled code*/ + + + +/* + Return the last found keybox. Caller must free it. The returned + keyblock has the kbode flag bit 0 set for the node with the public + key used to locate the keyblock or flag bit 1 set for the user ID + node. */ +int +keydb_get_cert (KEYDB_HANDLE hd, KsbaCert *r_cert) +{ + int rc = 0; + + if (!hd) + return gpg_error (GPG_ERR_INV_VALUE); + + if ( hd->found < 0 || hd->found >= hd->used) + return -1; /* nothing found */ + + switch (hd->active[hd->found].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + rc = gpg_error (GPG_ERR_GENERAL); /* oops */ + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + rc = keybox_get_cert (hd->active[hd->found].u.kr, r_cert); + break; + } + + return rc; +} + +/* + * Insert a new Certificate into one of the resources. + */ +int +keydb_insert_cert (KEYDB_HANDLE hd, KsbaCert cert) +{ + int rc = -1; + int idx; + char digest[20]; + + if (!hd) + return gpg_error (GPG_ERR_INV_VALUE); + + if (opt.dry_run) + return 0; + + if ( hd->found >= 0 && hd->found < hd->used) + idx = hd->found; + else if ( hd->current >= 0 && hd->current < hd->used) + idx = hd->current; + else + return gpg_error (GPG_ERR_GENERAL); + + rc = lock_all (hd); + if (rc) + return rc; + + gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL); /* kludge*/ + + switch (hd->active[idx].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + rc = gpg_error (GPG_ERR_GENERAL); + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + rc = keybox_insert_cert (hd->active[idx].u.kr, cert, digest); + break; + } + + unlock_all (hd); + return rc; +} + + + +/* update the current keyblock with KB */ +int +keydb_update_cert (KEYDB_HANDLE hd, KsbaCert cert) +{ + int rc = 0; + char digest[20]; + + if (!hd) + return gpg_error (GPG_ERR_INV_VALUE); + + if ( hd->found < 0 || hd->found >= hd->used) + return -1; /* nothing found */ + + if (opt.dry_run) + return 0; + + rc = lock_all (hd); + if (rc) + return rc; + + gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL); /* kludge*/ + + switch (hd->active[hd->found].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + rc = gpg_error (GPG_ERR_GENERAL); /* oops */ + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + rc = keybox_update_cert (hd->active[hd->found].u.kr, cert, digest); + break; + } + + unlock_all (hd); + return rc; +} + + +/* + * The current keyblock or cert will be deleted. + */ +int +keydb_delete (KEYDB_HANDLE hd) +{ + int rc = -1; + + if (!hd) + return gpg_error (GPG_ERR_INV_VALUE); + + if ( hd->found < 0 || hd->found >= hd->used) + return -1; /* nothing found */ + + if( opt.dry_run ) + return 0; + + rc = lock_all (hd); + if (rc) + return rc; + + switch (hd->active[hd->found].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + rc = gpg_error (GPG_ERR_GENERAL); + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + rc = keybox_delete (hd->active[hd->found].u.kr); + break; + } + + unlock_all (hd); + return rc; +} + + + +/* + * Locate the default writable key resource, so that the next + * operation (which is only relevant for inserts) will be done on this + * resource. + */ +int +keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved) +{ + int rc; + + if (!hd) + return gpg_error (GPG_ERR_INV_VALUE); + + rc = keydb_search_reset (hd); /* this does reset hd->current */ + if (rc) + return rc; + + for ( ; hd->current >= 0 && hd->current < hd->used; hd->current++) + { + switch (hd->active[hd->current].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + BUG(); + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + if (keybox_is_writable (hd->active[hd->current].token)) + return 0; /* found (hd->current is set to it) */ + break; + } + } + + return -1; +} + +/* + * Rebuild the caches of all key resources. + */ +void +keydb_rebuild_caches (void) +{ + int i; + + for (i=0; i < used_resources; i++) + { + if (all_resources[i].secret) + continue; + switch (all_resources[i].type) + { + case KEYDB_RESOURCE_TYPE_NONE: /* ignore */ + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: +/* rc = keybox_rebuild_cache (all_resources[i].token); */ +/* if (rc) */ +/* log_error (_("failed to rebuild keybox cache: %s\n"), */ +/* g10_errstr (rc)); */ + break; + } + } +} + + + +/* + * Start the next search on this handle right at the beginning + */ +int +keydb_search_reset (KEYDB_HANDLE hd) +{ + int i, rc = 0; + + if (!hd) + return gpg_error (GPG_ERR_INV_VALUE); + + hd->current = 0; + hd->found = -1; + /* and reset all resources */ + for (i=0; !rc && i < hd->used; i++) + { + switch (hd->active[i].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + rc = keybox_search_reset (hd->active[i].u.kr); + break; + } + } + return rc; /* fixme: we need to map error codes or share them with + all modules*/ +} + +/* + * Search through all keydb resources, starting at the current position, + * for a keyblock which contains one of the keys described in the DESC array. + */ +int +keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc) +{ + int rc = -1; + + if (!hd) + return gpg_error (GPG_ERR_INV_VALUE); + + while (rc == -1 && hd->current >= 0 && hd->current < hd->used) + { + switch (hd->active[hd->current].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + BUG(); /* we should never see it here */ + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + rc = keybox_search (hd->active[hd->current].u.kr, desc, ndesc); + break; + } + if (rc == -1) /* EOF -> switch to next resource */ + hd->current++; + else if (!rc) + hd->found = hd->current; + } + + return rc; +} + + +int +keydb_search_first (KEYDB_HANDLE hd) +{ + KEYDB_SEARCH_DESC desc; + + memset (&desc, 0, sizeof desc); + desc.mode = KEYDB_SEARCH_MODE_FIRST; + return keydb_search (hd, &desc, 1); +} + +int +keydb_search_next (KEYDB_HANDLE hd) +{ + KEYDB_SEARCH_DESC desc; + + memset (&desc, 0, sizeof desc); + desc.mode = KEYDB_SEARCH_MODE_NEXT; + return keydb_search (hd, &desc, 1); +} + +int +keydb_search_kid (KEYDB_HANDLE hd, u32 *kid) +{ + KEYDB_SEARCH_DESC desc; + + memset (&desc, 0, sizeof desc); + desc.mode = KEYDB_SEARCH_MODE_LONG_KID; +/* desc.u.kid[0] = kid[0]; */ +/* desc.u.kid[1] = kid[1]; */ + return keydb_search (hd, &desc, 1); +} + +int +keydb_search_fpr (KEYDB_HANDLE hd, const byte *fpr) +{ + KEYDB_SEARCH_DESC desc; + + memset (&desc, 0, sizeof desc); + desc.mode = KEYDB_SEARCH_MODE_FPR; + memcpy (desc.u.fpr, fpr, 20); + return keydb_search (hd, &desc, 1); +} + +int +keydb_search_issuer (KEYDB_HANDLE hd, const char *issuer) +{ + KEYDB_SEARCH_DESC desc; + int rc; + + memset (&desc, 0, sizeof desc); + desc.mode = KEYDB_SEARCH_MODE_ISSUER; + desc.u.name = issuer; + rc = keydb_search (hd, &desc, 1); + return rc; +} + +int +keydb_search_issuer_sn (KEYDB_HANDLE hd, + const char *issuer, KsbaConstSexp serial) +{ + KEYDB_SEARCH_DESC desc; + int rc; + const unsigned char *s; + + memset (&desc, 0, sizeof desc); + desc.mode = KEYDB_SEARCH_MODE_ISSUER_SN; + s = serial; + if (*s !='(') + return gpg_error (GPG_ERR_INV_VALUE); + s++; + for (desc.snlen = 0; digitp (s); s++) + desc.snlen = 10*desc.snlen + atoi_1 (s); + if (*s !=':') + return gpg_error (GPG_ERR_INV_VALUE); + desc.sn = s+1; + desc.u.name = issuer; + rc = keydb_search (hd, &desc, 1); + return rc; +} + +int +keydb_search_subject (KEYDB_HANDLE hd, const char *name) +{ + KEYDB_SEARCH_DESC desc; + int rc; + + memset (&desc, 0, sizeof desc); + desc.mode = KEYDB_SEARCH_MODE_SUBJECT; + desc.u.name = name; + rc = keydb_search (hd, &desc, 1); + return rc; +} + + +static int +hextobyte (const unsigned char *s) +{ + int c; + + if( *s >= '0' && *s <= '9' ) + c = 16 * (*s - '0'); + else if ( *s >= 'A' && *s <= 'F' ) + c = 16 * (10 + *s - 'A'); + else if ( *s >= 'a' && *s <= 'f' ) + c = 16 * (10 + *s - 'a'); + else + return -1; + s++; + if ( *s >= '0' && *s <= '9' ) + c += *s - '0'; + else if ( *s >= 'A' && *s <= 'F' ) + c += 10 + *s - 'A'; + else if ( *s >= 'a' && *s <= 'f' ) + c += 10 + *s - 'a'; + else + return -1; + return c; +} + + +static int +classify_user_id (const char *name, + KEYDB_SEARCH_DESC *desc, + int *force_exact ) +{ + const char *s; + int hexprefix = 0; + int hexlength; + int mode = 0; + + /* clear the structure so that the mode field is set to zero unless + * we set it to the correct value right at the end of this function */ + memset (desc, 0, sizeof *desc); + *force_exact = 0; + /* skip leading spaces. Fixme: what about trailing white space? */ + for(s = name; *s && spacep (s); s++ ) + ; + + switch (*s) + { + case 0: /* empty string is an error */ + return 0; + + case '.': /* an email address, compare from end */ + mode = KEYDB_SEARCH_MODE_MAILEND; + s++; + desc->u.name = s; + break; + + case '<': /* an email address */ + mode = KEYDB_SEARCH_MODE_MAIL; + s++; + desc->u.name = s; + break; + + case '@': /* part of an email address */ + mode = KEYDB_SEARCH_MODE_MAILSUB; + s++; + desc->u.name = s; + break; + + case '=': /* exact compare */ + mode = KEYDB_SEARCH_MODE_EXACT; + s++; + desc->u.name = s; + break; + + case '*': /* case insensitive substring search */ + mode = KEYDB_SEARCH_MODE_SUBSTR; + s++; + desc->u.name = s; + break; + + case '+': /* compare individual words */ + mode = KEYDB_SEARCH_MODE_WORDS; + s++; + desc->u.name = s; + break; + + case '/': /* subject's DN */ + s++; + if (!*s || spacep (s)) + return 0; /* no DN or prefixed with a space */ + desc->u.name = s; + mode = KEYDB_SEARCH_MODE_SUBJECT; + break; + + case '#': + { + const char *si; + + s++; + if ( *s == '/') + { /* "#/" indicates an issuer's DN */ + s++; + if (!*s || spacep (s)) + return 0; /* no DN or prefixed with a space */ + desc->u.name = s; + mode = KEYDB_SEARCH_MODE_ISSUER; + } + else + { /* serialnumber + optional issuer ID */ + for (si=s; *si && *si != '/'; si++) + { + if (!strchr("01234567890abcdefABCDEF", *si)) + return 0; /* invalid digit in serial number*/ + } + desc->sn = s; + desc->snlen = -1; + if (!*si) + mode = KEYDB_SEARCH_MODE_SN; + else + { + s = si+1; + if (!*s || spacep (s)) + return 0; /* no DN or prefixed with a space */ + desc->u.name = s; + mode = KEYDB_SEARCH_MODE_ISSUER_SN; + } + } + } + break; + + case ':': /*Unified fingerprint */ + { + const char *se, *si; + int i; + + se = strchr (++s,':'); + if (!se) + return 0; + for (i=0,si=s; si < se; si++, i++ ) + { + if (!strchr("01234567890abcdefABCDEF", *si)) + return 0; /* invalid digit */ + } + if (i != 32 && i != 40) + return 0; /* invalid length of fpr*/ + for (i=0,si=s; si < se; i++, si +=2) + desc->u.fpr[i] = hextobyte(si); + for (; i < 20; i++) + desc->u.fpr[i]= 0; + s = se + 1; + mode = KEYDB_SEARCH_MODE_FPR; + } + break; + + default: + if (s[0] == '0' && s[1] == 'x') + { + hexprefix = 1; + s += 2; + } + + hexlength = strspn(s, "0123456789abcdefABCDEF"); + if (hexlength >= 8 && s[hexlength] =='!') + { + *force_exact = 1; + hexlength++; /* just for the following check */ + } + + /* check if a hexadecimal number is terminated by EOS or blank */ + if (hexlength && s[hexlength] && !spacep (s+hexlength)) + { + if (hexprefix) /* a "0x" prefix without correct */ + return 0; /* termination is an error */ + /* The first chars looked like a hex number, but really is + not */ + hexlength = 0; + } + + if (*force_exact) + hexlength--; /* remove the bang */ + + if (hexlength == 8 + || (!hexprefix && hexlength == 9 && *s == '0')) + { /* short keyid */ + unsigned long kid; + if (hexlength == 9) + s++; + kid = strtoul( s, NULL, 16 ); + desc->u.kid[4] = kid >> 24; + desc->u.kid[5] = kid >> 16; + desc->u.kid[6] = kid >> 8; + desc->u.kid[7] = kid; + mode = KEYDB_SEARCH_MODE_SHORT_KID; + } + else if (hexlength == 16 + || (!hexprefix && hexlength == 17 && *s == '0')) + { /* complete keyid */ + unsigned long kid0, kid1; + char buf[9]; + if (hexlength == 17) + s++; + mem2str(buf, s, 9 ); + kid0 = strtoul (buf, NULL, 16); + kid1 = strtoul (s+8, NULL, 16); + desc->u.kid[0] = kid0 >> 24; + desc->u.kid[1] = kid0 >> 16; + desc->u.kid[2] = kid0 >> 8; + desc->u.kid[3] = kid0; + desc->u.kid[4] = kid1 >> 24; + desc->u.kid[5] = kid1 >> 16; + desc->u.kid[6] = kid1 >> 8; + desc->u.kid[7] = kid1; + mode = KEYDB_SEARCH_MODE_LONG_KID; + } + else if (hexlength == 32 + || (!hexprefix && hexlength == 33 && *s == '0')) + { /* md5 fingerprint */ + int i; + if (hexlength == 33) + s++; + memset(desc->u.fpr+16, 0, 4); + for (i=0; i < 16; i++, s+=2) + { + int c = hextobyte(s); + if (c == -1) + return 0; + desc->u.fpr[i] = c; + } + mode = KEYDB_SEARCH_MODE_FPR16; + } + else if (hexlength == 40 + || (!hexprefix && hexlength == 41 && *s == '0')) + { /* sha1/rmd160 fingerprint */ + int i; + if (hexlength == 41) + s++; + for (i=0; i < 20; i++, s+=2) + { + int c = hextobyte(s); + if (c == -1) + return 0; + desc->u.fpr[i] = c; + } + mode = KEYDB_SEARCH_MODE_FPR20; + } + else if (!hexprefix) + { + /* The fingerprint in an X.509 listing is often delimited by + colons, so we try to single this case out. */ + mode = 0; + hexlength = strspn (s, ":0123456789abcdefABCDEF"); + if (hexlength == 59 && (!s[hexlength] || spacep (s+hexlength))) + { + int i; + + for (i=0; i < 20; i++, s += 3) + { + int c = hextobyte(s); + if (c == -1 || (i < 19 && s[2] != ':')) + break; + desc->u.fpr[i] = c; + } + if (i == 20) + mode = KEYDB_SEARCH_MODE_FPR20; + } + if (!mode) /* default is substring search */ + { + *force_exact = 0; + desc->u.name = s; + mode = KEYDB_SEARCH_MODE_SUBSTR; + } + } + else + { /* hex number with a prefix but a wrong length */ + return 0; + } + } + + desc->mode = mode; + return mode; +} + + +int +keydb_classify_name (const char *name, KEYDB_SEARCH_DESC *desc) +{ + int dummy; + KEYDB_SEARCH_DESC dummy_desc; + + if (!desc) + desc = &dummy_desc; + + if (!classify_user_id (name, desc, &dummy)) + return gpg_error (GPG_ERR_INV_NAME); + return 0; +} + + +/* Store the certificate in the key DB but make sure that it does not + already exists. We do this simply by comparing the fingerprint. + If EXISTED is not NULL it will be set to true if the certificate + was already in the DB. */ +int +keydb_store_cert (KsbaCert cert, int ephemeral, int *existed) +{ + KEYDB_HANDLE kh; + int rc; + unsigned char fpr[20]; + + if (existed) + *existed = 0; + + if (!gpgsm_get_fingerprint (cert, 0, fpr, NULL)) + { + log_error (_("failed to get the fingerprint\n")); + return gpg_error (GPG_ERR_GENERAL); + } + + kh = keydb_new (0); + if (!kh) + { + log_error (_("failed to allocate keyDB handle\n")); + return gpg_error (GPG_ERR_ENOMEM);; + } + + if (ephemeral) + keydb_set_ephemeral (kh, 1); + + rc = keydb_search_fpr (kh, fpr); + if (rc != -1) + { + keydb_release (kh); + if (!rc) + { + if (existed) + *existed = 1; + return 0; /* okay */ + } + log_error (_("problem looking for existing certificate: %s\n"), + gpg_strerror (rc)); + return rc; + } + + rc = keydb_locate_writable (kh, 0); + if (rc) + { + log_error (_("error finding writable keyDB: %s\n"), gpg_strerror (rc)); + keydb_release (kh); + return rc; + } + + rc = keydb_insert_cert (kh, cert); + if (rc) + { + log_error (_("error storing certificate: %s\n"), gpg_strerror (rc)); + keydb_release (kh); + return rc; + } + keydb_release (kh); + return 0; +} + + + diff --git a/sm/keylist.c b/sm/keylist.c new file mode 100644 index 000000000..634bda292 --- /dev/null +++ b/sm/keylist.c @@ -0,0 +1,617 @@ +/* keylist.c + * Copyright (C) 1998, 1999, 2000, 2001, 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 +#include + +#include "gpgsm.h" + +#include +#include + +#include "keydb.h" +#include "i18n.h" + +struct list_external_parm_s { + FILE *fp; + int print_header; + int with_colons; + int with_chain; +}; + + + +static void +print_key_data (KsbaCert cert, FILE *fp) +{ +#if 0 + int n = pk ? pubkey_get_npkey( pk->pubkey_algo ) : 0; + int i; + + for(i=0; i < n; i++ ) + { + fprintf (fp, "pkd:%d:%u:", i, mpi_get_nbits( pk->pkey[i] ) ); + mpi_print(stdout, pk->pkey[i], 1 ); + putchar(':'); + putchar('\n'); + } +#endif +} + +static void +print_capabilities (KsbaCert cert, FILE *fp) +{ + KsbaError err; + unsigned int use; + + err = ksba_cert_get_key_usage (cert, &use); + if (err == KSBA_No_Data) + { + putc ('e', fp); + putc ('s', fp); + putc ('c', fp); + putc ('E', fp); + putc ('S', fp); + putc ('C', fp); + return; + } + if (err) + { + log_error (_("error getting key usage information: %s\n"), + ksba_strerror (err)); + return; + } + + if ((use & (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT))) + putc ('e', fp); + if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) + putc ('s', fp); + if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN)) + putc ('c', fp); + if ((use & (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT))) + putc ('E', fp); + if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) + putc ('S', fp); + if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN)) + putc ('C', fp); +} + + +static void +print_time (time_t t, FILE *fp) +{ + if (!t) + ; + else if ( t == (time_t)(-1) ) + putc ('?', fp); + else + fprintf (fp, "%lu", (unsigned long)t); +} + + +/* return an allocated string with the email address extracted from a + DN */ +static char * +email_kludge (const char *name) +{ + const unsigned char *p; + unsigned char *buf; + int n; + + if (strncmp (name, "1.2.840.113549.1.9.1=#", 22)) + return NULL; + /* This looks pretty much like an email address in the subject's DN + we use this to add an additional user ID entry. This way, + openSSL generated keys get a nicer and usable listing */ + name += 22; + for (n=0, p=name; hexdigitp (p) && hexdigitp (p+1); p +=2, n++) + ; + if (*p != '#' || !n) + return NULL; + buf = xtrymalloc (n+3); + if (!buf) + return NULL; /* oops, out of core */ + *buf = '<'; + for (n=1, p=name; *p != '#'; p +=2, n++) + buf[n] = xtoi_2 (p); + buf[n++] = '>'; + buf[n] = 0; + return buf; +} + + + + +/* List one certificate in colon mode */ +static void +list_cert_colon (KsbaCert cert, FILE *fp, int have_secret) +{ + int idx, trustletter = 0; + char *p; + KsbaSexp sexp; + char *fpr; + + fputs (have_secret? "crs:":"crt:", fp); + trustletter = 0; +#if 0 + if (is_not_valid (cert)) + putc ('i', fp); + else if ( is_revoked (cert) ) + putc ('r', fp); + else if ( has_expired (cert)) + putcr ('e', fp); + else +#endif + { + trustletter = '?'; /*get_validity_info ( pk, NULL );*/ + putc (trustletter, fp); + } + + fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); + fprintf (fp, ":%u:%d:%s:", + /*keylen_of_cert (cert)*/1024, + /* pubkey_algo_of_cert (cert)*/1, + fpr+24); + + /* we assume --fixed-list-mode for gpgsm */ + print_time ( ksba_cert_get_validity (cert, 0), fp); + putc (':', fp); + print_time ( ksba_cert_get_validity (cert, 1), fp); + putc (':', fp); + /* field 8, serial number: */ + if ((sexp = ksba_cert_get_serial (cert))) + { + int len; + const unsigned char *s = sexp; + + if (*s == '(') + { + s++; + for (len=0; *s && *s != ':' && digitp (s); s++) + len = len*10 + atoi_1 (s); + if (*s == ':') + for (s++; len; len--, s++) + fprintf (fp,"%02X", *s); + } + xfree (sexp); + } + putc (':', fp); + /* field 9, ownertrust - not used here */ + putc (':', fp); + /* field 10, old user ID - we use it here for the issuer DN */ + if ((p = ksba_cert_get_issuer (cert,0))) + { + print_sanitized_string (fp, p, ':'); + xfree (p); + } + putc (':', fp); + /* field 11, signature class - not used */ + putc (':', fp); + /* field 12, capabilities: */ + print_capabilities (cert, fp); + putc (':', fp); + putc ('\n', fp); + + /* FPR record */ + fprintf (fp, "fpr:::::::::%s:::", fpr); + xfree (fpr); fpr = NULL; + /* print chaining ID (field 13)*/ + { + KsbaCert next; + + if (!gpgsm_walk_cert_chain (cert, &next)) + { + p = gpgsm_get_fingerprint_hexstring (next, GCRY_MD_SHA1); + fputs (p, fp); + xfree (p); + ksba_cert_release (next); + } + } + putc (':', fp); + putc ('\n', fp); + + + if (opt.with_key_data) + { + if ( (p = gpgsm_get_keygrip_hexstring (cert))) + { + fprintf (fp, "grp:::::::::%s:\n", p); + xfree (p); + } + print_key_data (cert, fp); + } + + for (idx=0; (p = ksba_cert_get_subject (cert,idx)); idx++) + { + fprintf (fp, "uid:%c::::::::", trustletter); + print_sanitized_string (fp, p, ':'); + putc (':', fp); + putc (':', fp); + putc ('\n', fp); + if (!idx) + { + /* It would be better to get the faked email address from + the keydb. But as long as we don't have a way to pass + the meta data back, we just check it the same way as the + code used to create the keybox meta data does */ + char *pp = email_kludge (p); + if (pp) + { + fprintf (fp, "uid:%c::::::::", trustletter); + print_sanitized_string (fp, pp, ':'); + putc (':', fp); + putc (':', fp); + putc ('\n', fp); + xfree (pp); + } + } + xfree (p); + } +} + + +/* List one certificate in standard mode */ +static void +list_cert_std (KsbaCert cert, FILE *fp, int have_secret) +{ + KsbaError kerr; + KsbaSexp sexp; + char *dn; + time_t t; + int idx; + int is_ca, chainlen; + unsigned int kusage; + char *string, *p; + + sexp = ksba_cert_get_serial (cert); + fputs ("Serial number: ", fp); + gpgsm_print_serial (fp, sexp); + ksba_free (sexp); + putc ('\n', fp); + + dn = ksba_cert_get_issuer (cert, 0); + fputs (" Issuer: ", fp); + gpgsm_print_name (fp, dn); + ksba_free (dn); + putc ('\n', fp); + for (idx=1; (dn = ksba_cert_get_issuer (cert, idx)); idx++) + { + fputs (" aka: ", fp); + gpgsm_print_name (fp, dn); + ksba_free (dn); + putc ('\n', fp); + } + + dn = ksba_cert_get_subject (cert, 0); + fputs (" Subject: ", fp); + gpgsm_print_name (fp, dn); + ksba_free (dn); + putc ('\n', fp); + for (idx=1; (dn = ksba_cert_get_subject (cert, idx)); idx++) + { + fputs (" aka: ", fp); + gpgsm_print_name (fp, dn); + ksba_free (dn); + putc ('\n', fp); + } + + t = ksba_cert_get_validity (cert, 0); + fputs (" validity: ", fp); + gpgsm_print_time (fp, t); + fputs (" through ", fp); + t = ksba_cert_get_validity (cert, 1); + gpgsm_print_time (fp, t); + putc ('\n', fp); + + kerr = ksba_cert_get_key_usage (cert, &kusage); + if (kerr != KSBA_No_Data) + { + fputs (" key usage:", fp); + if (kerr) + fprintf (fp, " [error: %s]", ksba_strerror (kerr)); + else + { + if ( (kusage & KSBA_KEYUSAGE_DIGITAL_SIGNATURE)) + fputs (" digitalSignature", fp); + if ( (kusage & KSBA_KEYUSAGE_NON_REPUDIATION)) + fputs (" nonRepudiation", fp); + if ( (kusage & KSBA_KEYUSAGE_KEY_ENCIPHERMENT)) + fputs (" keyEncipherment", fp); + if ( (kusage & KSBA_KEYUSAGE_DATA_ENCIPHERMENT)) + fputs (" dataEncipherment", fp); + if ( (kusage & KSBA_KEYUSAGE_KEY_AGREEMENT)) + fputs (" keyAgreement", fp); + if ( (kusage & KSBA_KEYUSAGE_KEY_CERT_SIGN)) + fputs (" certSign", fp); + if ( (kusage & KSBA_KEYUSAGE_CRL_SIGN)) + fputs (" crlSign", fp); + if ( (kusage & KSBA_KEYUSAGE_ENCIPHER_ONLY)) + fputs (" encipherOnly", fp); + if ( (kusage & KSBA_KEYUSAGE_DECIPHER_ONLY)) + fputs (" decipherOnly", fp); + } + putc ('\n', fp); + } + + kerr = ksba_cert_get_cert_policies (cert, &string); + if (kerr != KSBA_No_Data) + { + fputs (" policies: ", fp); + if (kerr) + fprintf (fp, "[error: %s]", ksba_strerror (kerr)); + else + { + for (p=string; *p; p++) + { + if (*p == '\n') + *p = ','; + } + print_sanitized_string (fp, string, 0); + xfree (string); + } + putc ('\n', fp); + } + + kerr = ksba_cert_is_ca (cert, &is_ca, &chainlen); + if (kerr || is_ca) + { + fputs (" chain length: ", fp); + if (kerr) + fprintf (fp, "[error: %s]", ksba_strerror (kerr)); + else if (chainlen == -1) + fputs ("unlimited", fp); + else + fprintf (fp, "%d", chainlen); + putc ('\n', fp); + } + + + dn = gpgsm_get_fingerprint_string (cert, 0); + fprintf (fp, " fingerprint: %s\n", dn?dn:"error"); + xfree (dn); +} + +/* Same as standard mode mode list all certifying certts too */ +static void +list_cert_chain (KsbaCert cert, FILE *fp) +{ + KsbaCert next = NULL; + + list_cert_std (cert, fp, 0); + ksba_cert_ref (cert); + while (!gpgsm_walk_cert_chain (cert, &next)) + { + ksba_cert_release (cert); + fputs ("Certified by\n", fp); + list_cert_std (next, fp, 0); + cert = next; + } + ksba_cert_release (cert); + putc ('\n', fp); +} + + + +/* List all internal keys or just the key given as NAMES. + */ +static void +list_internal_keys (CTRL ctrl, STRLIST names, FILE *fp, unsigned int mode) +{ + KEYDB_HANDLE hd; + KEYDB_SEARCH_DESC *desc = NULL; + STRLIST sl; + int ndesc; + KsbaCert cert = NULL; + int rc=0; + const char *lastresname, *resname; + int have_secret; + + hd = keydb_new (0); + if (!hd) + { + log_error ("keydb_new failed\n"); + goto leave; + } + + if (!names) + ndesc = 1; + else + { + for (sl=names, ndesc=0; sl; sl = sl->next, ndesc++) + ; + } + + desc = xtrycalloc (ndesc, sizeof *desc); + if (!ndesc) + { + log_error ("out of core\n"); + goto leave; + } + + if (!names) + desc[0].mode = KEYDB_SEARCH_MODE_FIRST; + else + { + for (ndesc=0, sl=names; sl; sl = sl->next) + { + rc = keydb_classify_name (sl->d, desc+ndesc); + if (rc) + { + log_error ("key `%s' not found: %s\n", + sl->d, gpg_strerror (rc)); + rc = 0; + } + else + ndesc++; + } + + } + + /* it would be nice to see which of the given users did actually + match one in the keyring. To implement this we need to have a + found flag for each entry in desc and to set this we must check + all those entries after a match to mark all matched one - + currently we stop at the first match. To do this we need an + extra flag to enable this feature so */ + + lastresname = NULL; + while (!(rc = keydb_search (hd, desc, ndesc))) + { + if (!names) + desc[0].mode = KEYDB_SEARCH_MODE_NEXT; + + rc = keydb_get_cert (hd, &cert); + if (rc) + { + log_error ("keydb_get_cert failed: %s\n", gpg_strerror (rc)); + goto leave; + } + + resname = keydb_get_resource_name (hd); + + if (lastresname != resname ) + { + int i; + + if (ctrl->no_server) + { + fprintf (fp, "%s\n", resname ); + for (i=strlen(resname); i; i-- ) + putchar('-'); + putc ('\n', fp); + lastresname = resname; + } + } + + have_secret = 0; + if (mode) + { + char *p = gpgsm_get_keygrip_hexstring (cert); + if (p) + { + if (!gpgsm_agent_havekey (p)) + have_secret = 1; + xfree (p); + } + } + + if (!mode + || ((mode & 1) && !have_secret) + || ((mode & 2) && have_secret) ) + { + if (ctrl->with_colons) + list_cert_colon (cert, fp, have_secret); + else if (ctrl->with_chain) + list_cert_chain (cert, fp); + else + { + list_cert_std (cert, fp, have_secret); + putc ('\n', fp); + } + } + ksba_cert_release (cert); + cert = NULL; + } + if (rc && rc != -1) + log_error ("keydb_search failed: %s\n", gpg_strerror (rc)); + + leave: + ksba_cert_release (cert); + xfree (desc); + keydb_release (hd); +} + + + +static void +list_external_cb (void *cb_value, KsbaCert cert) +{ + struct list_external_parm_s *parm = cb_value; + + if (keydb_store_cert (cert, 1, NULL)) + log_error ("error storing certificate as ephemeral\n"); + + if (parm->print_header) + { + const char *resname = "[external keys]"; + int i; + + fprintf (parm->fp, "%s\n", resname ); + for (i=strlen(resname); i; i-- ) + putchar('-'); + putc ('\n', parm->fp); + parm->print_header = 0; + } + + if (parm->with_colons) + list_cert_colon (cert, parm->fp, 0); + else if (parm->with_chain) + list_cert_chain (cert, parm->fp); + else + { + list_cert_std (cert, parm->fp, 0); + putc ('\n', parm->fp); + } +} + + +/* List external keys similar to internal one. Note: mode does not + make sense here because it would be unwise to list external secret + keys */ +static void +list_external_keys (CTRL ctrl, STRLIST names, FILE *fp) +{ + int rc; + struct list_external_parm_s parm; + + parm.fp = fp; + parm.print_header = ctrl->no_server; + parm.with_colons = ctrl->with_colons; + parm.with_chain = ctrl->with_chain; + + rc = gpgsm_dirmngr_lookup (ctrl, names, list_external_cb, &parm); + if (rc) + log_error ("listing external keys failed: %s\n", gpg_strerror (rc)); +} + +/* List all keys or just the key given as NAMES. + MODE controls the operation mode: + Bit 0-2: + 0 = list all public keys but don't flag secret ones + 1 = list only public keys + 2 = list only secret keys + 3 = list secret and public keys + Bit 6: list internal keys + Bit 7: list external keys + */ +void +gpgsm_list_keys (CTRL ctrl, STRLIST names, FILE *fp, unsigned int mode) +{ + if ((mode & (1<<6))) + list_internal_keys (ctrl, names, fp, (mode & 3)); + if ((mode & (1<<7))) + list_external_keys (ctrl, names, fp); +} diff --git a/sm/server.c b/sm/server.c new file mode 100644 index 000000000..dda150964 --- /dev/null +++ b/sm/server.c @@ -0,0 +1,1070 @@ +/* server.c - Server mode and main entry point + * Copyright (C) 2001, 2002, 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 +#include + +#include + +#include "gpgsm.h" + +#define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t)) + + +/* The filepointer for status message used in non-server mode */ +static FILE *statusfp; + +/* Data used to assuciate an Assuan context with local server data */ +struct server_local_s { + ASSUAN_CONTEXT assuan_ctx; + int message_fd; + int list_internal; + int list_external; + CERTLIST recplist; + CERTLIST signerlist; +}; + + + +/* note, that it is sufficient to allocate the target string D as + long as the source string S, i.e.: strlen(s)+1; */ +static void +strcpy_escaped_plus (char *d, const unsigned char *s) +{ + while (*s) + { + if (*s == '%' && s[1] && s[2]) + { + s++; + *d++ = xtoi_2 ( s); + s += 2; + } + else if (*s == '+') + *d++ = ' ', s++; + else + *d++ = *s++; + } + *d = 0; +} + + + + +/* Check whether the option NAME appears in LINE */ +static int +has_option (const char *line, const char *name) +{ + const char *s; + int n = strlen (name); + + s = strstr (line, name); + return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n))); +} + + +static void +close_message_fd (CTRL ctrl) +{ + if (ctrl->server_local->message_fd != -1) + { + close (ctrl->server_local->message_fd); + ctrl->server_local->message_fd = -1; + } +} + + +static int +option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value) +{ + CTRL ctrl = assuan_get_pointer (ctx); + + if (!strcmp (key, "include-certs")) + { + int i = *value? atoi (value) : -1; + if (ctrl->include_certs < -2) + return ASSUAN_Parameter_Error; + ctrl->include_certs = i; + } + else if (!strcmp (key, "display")) + { + if (opt.display) + free (opt.display); + opt.display = strdup (value); + if (!opt.display) + return ASSUAN_Out_Of_Core; + } + else if (!strcmp (key, "ttyname")) + { + if (opt.ttyname) + free (opt.ttyname); + opt.ttyname = strdup (value); + if (!opt.ttyname) + return ASSUAN_Out_Of_Core; + } + else if (!strcmp (key, "ttytype")) + { + if (opt.ttytype) + free (opt.ttytype); + opt.ttytype = strdup (value); + if (!opt.ttytype) + return ASSUAN_Out_Of_Core; + } + else if (!strcmp (key, "lc-ctype")) + { + if (opt.lc_ctype) + free (opt.lc_ctype); + opt.lc_ctype = strdup (value); + if (!opt.lc_ctype) + return ASSUAN_Out_Of_Core; + } + else if (!strcmp (key, "lc-messages")) + { + if (opt.lc_messages) + free (opt.lc_messages); + opt.lc_messages = strdup (value); + if (!opt.lc_messages) + return ASSUAN_Out_Of_Core; + } + else if (!strcmp (key, "list-mode")) + { + int i = *value? atoi (value) : 0; + if (!i || i == 1) /* default and mode 1 */ + { + ctrl->server_local->list_internal = 1; + ctrl->server_local->list_external = 0; + } + else if (i == 2) + { + ctrl->server_local->list_internal = 0; + ctrl->server_local->list_external = 1; + } + else if (i == 3) + { + ctrl->server_local->list_internal = 1; + ctrl->server_local->list_external = 1; + } + else + return ASSUAN_Parameter_Error; + } + else + return ASSUAN_Invalid_Option; + + return 0; +} + + + + +static void +reset_notify (ASSUAN_CONTEXT ctx) +{ + CTRL ctrl = assuan_get_pointer (ctx); + + gpgsm_release_certlist (ctrl->server_local->recplist); + gpgsm_release_certlist (ctrl->server_local->signerlist); + ctrl->server_local->recplist = NULL; + ctrl->server_local->signerlist = NULL; + close_message_fd (ctrl); + assuan_close_input_fd (ctx); + assuan_close_output_fd (ctx); +} + + +static void +input_notify (ASSUAN_CONTEXT ctx, const char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + + ctrl->autodetect_encoding = 0; + ctrl->is_pem = 0; + ctrl->is_base64 = 0; + if (strstr (line, "--armor")) + ctrl->is_pem = 1; + else if (strstr (line, "--base64")) + ctrl->is_base64 = 1; + else if (strstr (line, "--binary")) + ; + else + ctrl->autodetect_encoding = 1; +} + +static void +output_notify (ASSUAN_CONTEXT ctx, const char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + + ctrl->create_pem = 0; + ctrl->create_base64 = 0; + if (strstr (line, "--armor")) + ctrl->create_pem = 1; + else if (strstr (line, "--base64")) + ctrl->create_base64 = 1; /* just the raw output */ +} + + + +/* RECIPIENT + + Set the recipient for the encryption. should be the + internal representation of the key; the server may accept any other + way of specification [we will support this]. If this is a valid and + trusted recipient the server does respond with OK, otherwise the + return is an ERR with the reason why the recipient can't be used, + the encryption will then not be done for this recipient. IF the + policy is not to encrypt at all if not all recipients are valid, the + client has to take care of this. All RECIPIENT commands are + cumulative until a RESET or an successful ENCRYPT command. */ +static int +cmd_recipient (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int rc; + + rc = gpgsm_add_to_certlist (ctrl, line, 0, &ctrl->server_local->recplist); + if (rc) + { + gpg_err_code_t r = gpg_err_code (rc); + gpgsm_status2 (ctrl, STATUS_INV_RECP, + r == -1? "1": + r == GPG_ERR_NO_PUBKEY? "1": + r == GPG_ERR_AMBIGUOUS_NAME? "2": + r == GPG_ERR_WRONG_KEY_USAGE? "3": + r == GPG_ERR_CERT_REVOKED? "4": + r == GPG_ERR_CERT_EXPIRED? "5": + r == GPG_ERR_NO_CRL_KNOWN? "6": + r == GPG_ERR_CRL_TOO_OLD? "7": + r == GPG_ERR_NO_POLICY_MATCH? "8": + "0", + line, NULL); + } + + return map_to_assuan_status (rc); +} + +/* SIGNER + + Set the signer's keys for the signature creation. should + be the internal representation of the key; the server may accept any + other way of specification [we will support this]. If this is a + valid and usable signing key the server does respond with OK, + otherwise it returns an ERR with the reason why the key can't be + used, the signing will then not be done for this key. If the policy + is not to sign at all if not all signer keys are valid, the client + has to take care of this. All SIGNER commands are cumulative until + a RESET but they are *not* reset by an SIGN command becuase it can + be expected that set of signers are used for more than one sign + operation. + + Note that this command returns an INV_RECP status which is a bit + strange, but they are very similar. */ +static int +cmd_signer (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int rc; + + rc = gpgsm_add_to_certlist (ctrl, line, 1, &ctrl->server_local->signerlist); + if (rc) + { + gpg_err_code_t r = gpg_err_code (rc); + gpgsm_status2 (ctrl, STATUS_INV_RECP, + r == -1? "1": + r == GPG_ERR_NO_PUBKEY? "1": + r == GPG_ERR_AMBIGUOUS_NAME? "2": + r == GPG_ERR_WRONG_KEY_USAGE? "3": + r == GPG_ERR_CERT_REVOKED? "4": + r == GPG_ERR_CERT_EXPIRED? "5": + r == GPG_ERR_NO_CRL_KNOWN? "6": + r == GPG_ERR_CRL_TOO_OLD? "7": + r == GPG_ERR_NO_POLICY_MATCH? "8": + r == GPG_ERR_NO_SECKEY? "9": + "0", + line, NULL); + } + return map_to_assuan_status (rc); +} + + +/* ENCRYPT + + Do the actual encryption process. Takes the plaintext from the INPUT + command, writes to the ciphertext to the file descriptor set with + the OUTPUT command, take the recipients form all the recipients set + so far. If this command fails the clients should try to delete all + output currently done or otherwise mark it as invalid. GPGSM does + ensure that there won't be any security problem with leftover data + on the output in this case. + + This command should in general not fail, as all necessary checks + have been done while setting the recipients. The input and output + pipes are closed. */ +static int +cmd_encrypt (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int inp_fd, out_fd; + FILE *out_fp; + int rc; + + inp_fd = assuan_get_input_fd (ctx); + if (inp_fd == -1) + return set_error (No_Input, NULL); + out_fd = assuan_get_output_fd (ctx); + if (out_fd == -1) + return set_error (No_Output, NULL); + + out_fp = fdopen ( dup(out_fd), "w"); + if (!out_fp) + return set_error (General_Error, "fdopen() failed"); + rc = gpgsm_encrypt (assuan_get_pointer (ctx), + ctrl->server_local->recplist, + inp_fd, out_fp); + fclose (out_fp); + + gpgsm_release_certlist (ctrl->server_local->recplist); + ctrl->server_local->recplist = NULL; + /* close and reset the fd */ + close_message_fd (ctrl); + assuan_close_input_fd (ctx); + assuan_close_output_fd (ctx); + return map_to_assuan_status (rc); +} + +/* DECRYPT + + This performs the decrypt operation after doing some check on the + internal state. (e.g. that only needed data has been set). Because + it utilizes the GPG-Agent for the session key decryption, there is + no need to ask the client for a protecting passphrase - GpgAgent + does take care of this by requesting this from the user. */ +static int +cmd_decrypt (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int inp_fd, out_fd; + FILE *out_fp; + int rc; + + inp_fd = assuan_get_input_fd (ctx); + if (inp_fd == -1) + return set_error (No_Input, NULL); + out_fd = assuan_get_output_fd (ctx); + if (out_fd == -1) + return set_error (No_Output, NULL); + + out_fp = fdopen ( dup(out_fd), "w"); + if (!out_fp) + return set_error (General_Error, "fdopen() failed"); + rc = gpgsm_decrypt (ctrl, inp_fd, out_fp); + fclose (out_fp); + + /* close and reset the fd */ + close_message_fd (ctrl); + assuan_close_input_fd (ctx); + assuan_close_output_fd (ctx); + + return map_to_assuan_status (rc); +} + + +/* VERIFY + + This does a verify operation on the message send to the input-FD. + The result is written out using status lines. If an output FD was + given, the signed text will be written to that. + + If the signature is a detached one, the server will inquire about + the signed material and the client must provide it. + */ +static int +cmd_verify (ASSUAN_CONTEXT ctx, char *line) +{ + int rc; + CTRL ctrl = assuan_get_pointer (ctx); + int fd = assuan_get_input_fd (ctx); + int out_fd = assuan_get_output_fd (ctx); + FILE *out_fp = NULL; + + if (fd == -1) + return set_error (No_Input, NULL); + + if (out_fd != -1) + { + out_fp = fdopen ( dup(out_fd), "w"); + if (!out_fp) + return set_error (General_Error, "fdopen() failed"); + } + + rc = gpgsm_verify (assuan_get_pointer (ctx), fd, + ctrl->server_local->message_fd, out_fp); + if (out_fp) + fclose (out_fp); + + /* close and reset the fd */ + close_message_fd (ctrl); + assuan_close_input_fd (ctx); + assuan_close_output_fd (ctx); + + return map_to_assuan_status (rc); +} + + +/* SIGN [--detached] + + Sign the data set with the INPUT command and write it to the sink + set by OUTPUT. With "--detached" specified, a detached signature is + created (surprise). */ +static int +cmd_sign (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int inp_fd, out_fd; + FILE *out_fp; + int detached; + int rc; + + inp_fd = assuan_get_input_fd (ctx); + if (inp_fd == -1) + return set_error (No_Input, NULL); + out_fd = assuan_get_output_fd (ctx); + if (out_fd == -1) + return set_error (No_Output, NULL); + + detached = has_option (line, "--detached"); + + out_fp = fdopen ( dup(out_fd), "w"); + if (!out_fp) + return set_error (General_Error, "fdopen() failed"); + + rc = gpgsm_sign (assuan_get_pointer (ctx), ctrl->server_local->signerlist, + inp_fd, detached, out_fp); + fclose (out_fp); + + /* close and reset the fd */ + close_message_fd (ctrl); + assuan_close_input_fd (ctx); + assuan_close_output_fd (ctx); + + return map_to_assuan_status (rc); +} + + +/* IMPORT + + Import the certificates read form the input-fd, return status + message for each imported one. The import checks the validity of + the certificate but not of the entire chain. It is possible to + import expired certificates. */ +static int +cmd_import (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int rc; + int fd = assuan_get_input_fd (ctx); + + if (fd == -1) + return set_error (No_Input, NULL); + + rc = gpgsm_import (assuan_get_pointer (ctx), fd); + + /* close and reset the fd */ + close_message_fd (ctrl); + assuan_close_input_fd (ctx); + assuan_close_output_fd (ctx); + + return map_to_assuan_status (rc); +} + + +static int +cmd_export (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int fd = assuan_get_output_fd (ctx); + FILE *out_fp; + char *p; + STRLIST list, sl; + + if (fd == -1) + return set_error (No_Output, NULL); + + /* break the line down into an STRLIST */ + list = NULL; + for (p=line; *p; line = p) + { + while (*p && *p != ' ') + p++; + if (*p) + *p++ = 0; + if (*line) + { + sl = xtrymalloc (sizeof *sl + strlen (line)); + if (!sl) + { + free_strlist (list); + return ASSUAN_Out_Of_Core; + } + sl->flags = 0; + strcpy_escaped_plus (sl->d, line); + sl->next = list; + list = sl; + } + } + + out_fp = fdopen ( dup(fd), "w"); + if (!out_fp) + { + free_strlist (list); + return set_error (General_Error, "fdopen() failed"); + } + + gpgsm_export (ctrl, list, out_fp); + fclose (out_fp); + free_strlist (list); + /* close and reset the fd */ + close_message_fd (ctrl); + assuan_close_input_fd (ctx); + assuan_close_output_fd (ctx); + return 0; +} + + +static int +cmd_delkeys (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + char *p; + STRLIST list, sl; + int rc; + + /* break the line down into an STRLIST */ + list = NULL; + for (p=line; *p; line = p) + { + while (*p && *p != ' ') + p++; + if (*p) + *p++ = 0; + if (*line) + { + sl = xtrymalloc (sizeof *sl + strlen (line)); + if (!sl) + { + free_strlist (list); + return ASSUAN_Out_Of_Core; + } + sl->flags = 0; + strcpy_escaped_plus (sl->d, line); + sl->next = list; + list = sl; + } + } + + rc = gpgsm_delete (ctrl, list); + free_strlist (list); + + /* close and reset the fd */ + close_message_fd (ctrl); + assuan_close_input_fd (ctx); + assuan_close_output_fd (ctx); + + return map_to_assuan_status (rc); +} + + + +/* MESSAGE FD= + + Set the file descriptor to read a message which is used with + detached signatures */ +static int +cmd_message (ASSUAN_CONTEXT ctx, char *line) +{ + char *endp; + int fd; + CTRL ctrl = assuan_get_pointer (ctx); + + if (strncmp (line, "FD=", 3)) + return set_error (Syntax_Error, "FD= expected"); + line += 3; + if (!digitp (line)) + return set_error (Syntax_Error, "number required"); + fd = strtoul (line, &endp, 10); + if (*endp) + return set_error (Syntax_Error, "garbage found"); + if (fd == -1) + return set_error (No_Input, NULL); + + ctrl->server_local->message_fd = fd; + return 0; +} + + +static int +do_listkeys (ASSUAN_CONTEXT ctx, char *line, int mode) +{ + CTRL ctrl = assuan_get_pointer (ctx); + FILE *fp = assuan_get_data_fp (ctx); + char *p; + STRLIST list, sl; + unsigned int listmode; + + if (!fp) + return set_error (General_Error, "no data stream"); + + /* break the line down into an STRLIST */ + list = NULL; + for (p=line; *p; line = p) + { + while (*p && *p != ' ') + p++; + if (*p) + *p++ = 0; + if (*line) + { + sl = xtrymalloc (sizeof *sl + strlen (line)); + if (!sl) + { + free_strlist (list); + return ASSUAN_Out_Of_Core; + } + sl->flags = 0; + strcpy_escaped_plus (sl->d, line); + sl->next = list; + list = sl; + } + } + + ctrl->with_colons = 1; + listmode = mode; + if (ctrl->server_local->list_internal) + listmode |= (1<<6); + if (ctrl->server_local->list_external) + listmode |= (1<<7); + gpgsm_list_keys (assuan_get_pointer (ctx), list, fp, listmode); + free_strlist (list); + return 0; +} + +static int +cmd_listkeys (ASSUAN_CONTEXT ctx, char *line) +{ + return do_listkeys (ctx, line, 3); +} + +static int +cmd_listsecretkeys (ASSUAN_CONTEXT ctx, char *line) +{ + return do_listkeys (ctx, line, 2); +} + + +/* GENKEY + + Read the parameters in native format from the input fd and write a + certificate request to the output. + */ +static int +cmd_genkey (ASSUAN_CONTEXT ctx, char *line) +{ + CTRL ctrl = assuan_get_pointer (ctx); + int inp_fd, out_fd; + FILE *out_fp; + int rc; + + inp_fd = assuan_get_input_fd (ctx); + if (inp_fd == -1) + return set_error (No_Input, NULL); + out_fd = assuan_get_output_fd (ctx); + if (out_fd == -1) + return set_error (No_Output, NULL); + + out_fp = fdopen ( dup(out_fd), "w"); + if (!out_fp) + return set_error (General_Error, "fdopen() failed"); + rc = gpgsm_genkey (ctrl, inp_fd, out_fp); + fclose (out_fp); + + /* close and reset the fds */ + assuan_close_input_fd (ctx); + assuan_close_output_fd (ctx); + + return map_to_assuan_status (rc); +} + + + + + +/* Tell the assuan library about our commands */ +static int +register_commands (ASSUAN_CONTEXT ctx) +{ + static struct { + const char *name; + int (*handler)(ASSUAN_CONTEXT, char *line); + } table[] = { + { "RECIPIENT", cmd_recipient }, + { "SIGNER", cmd_signer }, + { "ENCRYPT", cmd_encrypt }, + { "DECRYPT", cmd_decrypt }, + { "VERIFY", cmd_verify }, + { "SIGN", cmd_sign }, + { "IMPORT", cmd_import }, + { "EXPORT", cmd_export }, + { "INPUT", NULL }, + { "OUTPUT", NULL }, + { "MESSAGE", cmd_message }, + { "LISTKEYS", cmd_listkeys }, + { "LISTSECRETKEYS",cmd_listsecretkeys }, + { "GENKEY", cmd_genkey }, + { "DELKEYS", cmd_delkeys }, + { NULL } + }; + int i, rc; + + for (i=0; table[i].name; i++) + { + rc = assuan_register_command (ctx, table[i].name, table[i].handler); + if (rc) + return rc; + } + return 0; +} + +/* Startup the server */ +void +gpgsm_server (void) +{ + int rc; + int filedes[2]; + ASSUAN_CONTEXT ctx; + struct server_control_s ctrl; + + memset (&ctrl, 0, sizeof ctrl); + gpgsm_init_default_ctrl (&ctrl); + + /* For now we use a simple pipe based server so that we can work + from scripts. We will later add options to run as a daemon and + wait for requests on a Unix domain socket */ + filedes[0] = 0; + filedes[1] = 1; + rc = assuan_init_pipe_server (&ctx, filedes); + if (rc) + { + log_error ("failed to initialize the server: %s\n", + assuan_strerror(rc)); + gpgsm_exit (2); + } + rc = register_commands (ctx); + if (rc) + { + log_error ("failed to the register commands with Assuan: %s\n", + assuan_strerror(rc)); + gpgsm_exit (2); + } + assuan_set_hello_line (ctx, "GNU Privacy Guard's S/M server ready"); + + assuan_register_reset_notify (ctx, reset_notify); + assuan_register_input_notify (ctx, input_notify); + assuan_register_output_notify (ctx, output_notify); + assuan_register_option_handler (ctx, option_handler); + + assuan_set_pointer (ctx, &ctrl); + ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local); + ctrl.server_local->assuan_ctx = ctx; + ctrl.server_local->message_fd = -1; + ctrl.server_local->list_internal = 1; + ctrl.server_local->list_external = 0; + + if (DBG_ASSUAN) + assuan_set_log_stream (ctx, log_get_stream ()); + + for (;;) + { + rc = assuan_accept (ctx); + if (rc == -1) + { + break; + } + else if (rc) + { + log_info ("Assuan accept problem: %s\n", assuan_strerror (rc)); + break; + } + + rc = assuan_process (ctx); + if (rc) + { + log_info ("Assuan processing failed: %s\n", assuan_strerror (rc)); + continue; + } + } + + gpgsm_release_certlist (ctrl.server_local->recplist); + ctrl.server_local->recplist = NULL; + gpgsm_release_certlist (ctrl.server_local->signerlist); + ctrl.server_local->signerlist = NULL; + + assuan_deinit_server (ctx); +} + + +static const char * +get_status_string ( int no ) +{ + const char *s; + + switch (no) + { + case STATUS_ENTER : s = "ENTER"; break; + case STATUS_LEAVE : s = "LEAVE"; break; + case STATUS_ABORT : s = "ABORT"; break; + case STATUS_GOODSIG: s = "GOODSIG"; break; + case STATUS_SIGEXPIRED: s = "SIGEXPIRED"; break; + case STATUS_KEYREVOKED: s = "KEYREVOKED"; break; + case STATUS_BADSIG : s = "BADSIG"; break; + case STATUS_ERRSIG : s = "ERRSIG"; break; + case STATUS_BADARMOR : s = "BADARMOR"; break; + case STATUS_RSA_OR_IDEA : s= "RSA_OR_IDEA"; break; + case STATUS_TRUST_UNDEFINED: s = "TRUST_UNDEFINED"; break; + case STATUS_TRUST_NEVER : s = "TRUST_NEVER"; break; + case STATUS_TRUST_MARGINAL : s = "TRUST_MARGINAL"; break; + case STATUS_TRUST_FULLY : s = "TRUST_FULLY"; break; + case STATUS_TRUST_ULTIMATE : s = "TRUST_ULTIMATE"; break; + case STATUS_GET_BOOL : s = "GET_BOOL"; break; + case STATUS_GET_LINE : s = "GET_LINE"; break; + case STATUS_GET_HIDDEN : s = "GET_HIDDEN"; break; + case STATUS_GOT_IT : s = "GOT_IT"; break; + case STATUS_SHM_INFO : s = "SHM_INFO"; break; + case STATUS_SHM_GET : s = "SHM_GET"; break; + case STATUS_SHM_GET_BOOL : s = "SHM_GET_BOOL"; break; + case STATUS_SHM_GET_HIDDEN : s = "SHM_GET_HIDDEN"; break; + case STATUS_NEED_PASSPHRASE: s = "NEED_PASSPHRASE"; break; + case STATUS_VALIDSIG : s = "VALIDSIG"; break; + case STATUS_SIG_ID : s = "SIG_ID"; break; + case STATUS_ENC_TO : s = "ENC_TO"; break; + case STATUS_NODATA : s = "NODATA"; break; + case STATUS_BAD_PASSPHRASE : s = "BAD_PASSPHRASE"; break; + case STATUS_NO_PUBKEY : s = "NO_PUBKEY"; break; + case STATUS_NO_SECKEY : s = "NO_SECKEY"; break; + case STATUS_NEED_PASSPHRASE_SYM: s = "NEED_PASSPHRASE_SYM"; break; + case STATUS_DECRYPTION_FAILED: s = "DECRYPTION_FAILED"; break; + case STATUS_DECRYPTION_OKAY: s = "DECRYPTION_OKAY"; break; + case STATUS_MISSING_PASSPHRASE: s = "MISSING_PASSPHRASE"; break; + case STATUS_GOOD_PASSPHRASE : s = "GOOD_PASSPHRASE"; break; + case STATUS_GOODMDC : s = "GOODMDC"; break; + case STATUS_BADMDC : s = "BADMDC"; break; + case STATUS_ERRMDC : s = "ERRMDC"; break; + case STATUS_IMPORTED : s = "IMPORTED"; break; + case STATUS_IMPORT_RES : s = "IMPORT_RES"; break; + case STATUS_FILE_START : s = "FILE_START"; break; + case STATUS_FILE_DONE : s = "FILE_DONE"; break; + case STATUS_FILE_ERROR : s = "FILE_ERROR"; break; + case STATUS_BEGIN_DECRYPTION:s = "BEGIN_DECRYPTION"; break; + case STATUS_END_DECRYPTION : s = "END_DECRYPTION"; break; + case STATUS_BEGIN_ENCRYPTION:s = "BEGIN_ENCRYPTION"; break; + case STATUS_END_ENCRYPTION : s = "END_ENCRYPTION"; break; + case STATUS_DELETE_PROBLEM : s = "DELETE_PROBLEM"; break; + case STATUS_PROGRESS : s = "PROGRESS"; break; + case STATUS_SIG_CREATED : s = "SIG_CREATED"; break; + case STATUS_SESSION_KEY : s = "SESSION_KEY"; break; + case STATUS_NOTATION_NAME : s = "NOTATION_NAME" ; break; + case STATUS_NOTATION_DATA : s = "NOTATION_DATA" ; break; + case STATUS_POLICY_URL : s = "POLICY_URL" ; break; + case STATUS_BEGIN_STREAM : s = "BEGIN_STREAM"; break; + case STATUS_END_STREAM : s = "END_STREAM"; break; + case STATUS_KEY_CREATED : s = "KEY_CREATED"; break; + case STATUS_UNEXPECTED : s = "UNEXPECTED"; break; + case STATUS_INV_RECP : s = "INV_RECP"; break; + case STATUS_NO_RECP : s = "NO_RECP"; break; + case STATUS_ALREADY_SIGNED : s = "ALREADY_SIGNED"; break; + case STATUS_EXPSIG : s = "EXPSIG"; break; + case STATUS_EXPKEYSIG : s = "EXPKEYSIG"; break; + case STATUS_TRUNCATED : s = "TRUNCATED"; break; + case STATUS_ERROR : s = "ERROR"; break; + case STATUS_IMPORT_PROBLEM : s = "IMPORT_PROBLEM"; break; + default: s = "?"; break; + } + return s; +} + + +void +gpgsm_status2 (CTRL ctrl, int no, ...) +{ + va_list arg_ptr; + const char *text; + + va_start (arg_ptr, no); + + if (ctrl->no_server) + { + if (ctrl->status_fd == -1) + return; /* no status wanted */ + if (!statusfp) + { + if (ctrl->status_fd == 1) + statusfp = stdout; + else if (ctrl->status_fd == 2) + statusfp = stderr; + else + statusfp = fdopen (ctrl->status_fd, "w"); + + if (!statusfp) + { + log_fatal ("can't open fd %d for status output: %s\n", + ctrl->status_fd, strerror(errno)); + } + } + + fputs ("[GNUPG:] ", statusfp); + fputs (get_status_string (no), statusfp); + + while ( (text = va_arg (arg_ptr, const char*) )) + { + putc ( ' ', statusfp ); + for (; *text; text++) + { + if (*text == '\n') + fputs ( "\\n", statusfp ); + else if (*text == '\r') + fputs ( "\\r", statusfp ); + else + putc ( *(const byte *)text, statusfp ); + } + } + putc ('\n', statusfp); + fflush (statusfp); + } + else + { + ASSUAN_CONTEXT ctx = ctrl->server_local->assuan_ctx; + char buf[950], *p; + size_t n; + + p = buf; + n = 0; + while ( (text = va_arg (arg_ptr, const char *)) ) + { + if (n) + { + *p++ = ' '; + n++; + } + for ( ; *text && n < DIM (buf)-2; n++) + *p++ = *text++; + } + *p = 0; + assuan_write_status (ctx, get_status_string (no), buf); + } + + va_end (arg_ptr); +} + +void +gpgsm_status (CTRL ctrl, int no, const char *text) +{ + gpgsm_status2 (ctrl, no, text, NULL); +} + +void +gpgsm_status_with_err_code (CTRL ctrl, int no, const char *text, + gpg_err_code_t ec) +{ + char buf[30]; + + sprintf (buf, "%u", (unsigned int)ec); + if (text) + gpgsm_status2 (ctrl, no, text, buf, NULL); + else + gpgsm_status2 (ctrl, no, buf, NULL); +} + +#if 0 +/* + * Write a status line with a buffer using %XX escapes. If WRAP is > + * 0 wrap the line after this length. If STRING is not NULL it will + * be prepended to the buffer, no escaping is done for string. + * A wrap of -1 forces spaces not to be encoded as %20. + */ +void +write_status_text_and_buffer ( int no, const char *string, + const char *buffer, size_t len, int wrap ) +{ + const char *s, *text; + int esc, first; + int lower_limit = ' '; + size_t n, count, dowrap; + + if( !statusfp ) + return; /* not enabled */ + + if (wrap == -1) { + lower_limit--; + wrap = 0; + } + + text = get_status_string (no); + count = dowrap = first = 1; + do { + if (dowrap) { + fprintf (statusfp, "[GNUPG:] %s ", text ); + count = dowrap = 0; + if (first && string) { + fputs (string, statusfp); + count += strlen (string); + } + first = 0; + } + for (esc=0, s=buffer, n=len; n && !esc; s++, n-- ) { + if ( *s == '%' || *(const byte*)s <= lower_limit + || *(const byte*)s == 127 ) + esc = 1; + if ( wrap && ++count > wrap ) { + dowrap=1; + break; + } + } + if (esc) { + s--; n++; + } + if (s != buffer) + fwrite (buffer, s-buffer, 1, statusfp ); + if ( esc ) { + fprintf (statusfp, "%%%02X", *(const byte*)s ); + s++; n--; + } + buffer = s; + len = n; + if ( dowrap && len ) + putc ( '\n', statusfp ); + } while ( len ); + + putc ('\n',statusfp); + fflush (statusfp); +} +#endif diff --git a/sm/sign.c b/sm/sign.c new file mode 100644 index 000000000..0afb52b62 --- /dev/null +++ b/sm/sign.c @@ -0,0 +1,621 @@ +/* sign.c - Sign a message + * Copyright (C) 2001, 2002, 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 +#include + +#include "gpgsm.h" +#include +#include + +#include "keydb.h" +#include "i18n.h" + + +static void +hash_data (int fd, gcry_md_hd_t md) +{ + FILE *fp; + char buffer[4096]; + int nread; + + fp = fdopen ( dup (fd), "rb"); + if (!fp) + { + log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno)); + return; + } + + do + { + nread = fread (buffer, 1, DIM(buffer), fp); + gcry_md_write (md, buffer, nread); + } + while (nread); + if (ferror (fp)) + log_error ("read error on fd %d: %s\n", fd, strerror (errno)); + fclose (fp); +} + +static int +hash_and_copy_data (int fd, gcry_md_hd_t md, KsbaWriter writer) +{ + KsbaError err; + FILE *fp; + char buffer[4096]; + int nread; + int rc = 0; + int any = 0; + + fp = fdopen ( dup (fd), "rb"); + if (!fp) + { + gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); + log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno)); + return tmperr; + } + + do + { + nread = fread (buffer, 1, DIM(buffer), fp); + if (nread) + { + any = 1; + gcry_md_write (md, buffer, nread); + err = ksba_writer_write_octet_string (writer, buffer, nread, 0); + if (err) + { + log_error ("write failed: %s\n", ksba_strerror (err)); + rc = map_ksba_err (err); + } + } + } + while (nread && !rc); + if (ferror (fp)) + { + rc = gpg_error (gpg_err_code_from_errno (errno)); + log_error ("read error on fd %d: %s\n", fd, strerror (errno)); + } + fclose (fp); + if (!any) + { + /* We can't allow to sign an empty message because it does not + make much sense and more seriously, ksba-cms_build has + already written the tag for data and now expects an octet + string but an octet string of zeize 0 is illegal. */ + log_error ("cannot sign an empty message\n"); + rc = gpg_error (GPG_ERR_NO_DATA); + } + if (!rc) + { + err = ksba_writer_write_octet_string (writer, NULL, 0, 1); + if (err) + { + log_error ("write failed: %s\n", ksba_strerror (err)); + rc = map_ksba_err (err); + } + } + + return rc; +} + + +/* Get the default certificate which is defined as the first one our + keyDB retruns and has a secret key available */ +int +gpgsm_get_default_cert (KsbaCert *r_cert) +{ + KEYDB_HANDLE hd; + KsbaCert cert = NULL; + int rc; + char *p; + + hd = keydb_new (0); + if (!hd) + return gpg_error (GPG_ERR_GENERAL); + rc = keydb_search_first (hd); + if (rc) + { + keydb_release (hd); + return rc; + } + + do + { + rc = keydb_get_cert (hd, &cert); + if (rc) + { + log_error ("keydb_get_cert failed: %s\n", gpg_strerror (rc)); + keydb_release (hd); + return rc; + } + + p = gpgsm_get_keygrip_hexstring (cert); + if (p) + { + if (!gpgsm_agent_havekey (p)) + { + xfree (p); + keydb_release (hd); + *r_cert = cert; + return 0; /* got it */ + } + xfree (p); + } + + ksba_cert_release (cert); + cert = NULL; + } + while (!(rc = keydb_search_next (hd))); + if (rc && rc != -1) + log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc)); + + ksba_cert_release (cert); + keydb_release (hd); + return rc; +} + + +static KsbaCert +get_default_signer (void) +{ + KEYDB_SEARCH_DESC desc; + KsbaCert cert = NULL; + KEYDB_HANDLE kh = NULL; + int rc; + + if (!opt.local_user) + { + rc = gpgsm_get_default_cert (&cert); + if (rc) + { + if (rc != -1) + log_debug ("failed to find default certificate: %s\n", + gpg_strerror (rc)); + return NULL; + } + return cert; + } + + rc = keydb_classify_name (opt.local_user, &desc); + if (rc) + { + log_error ("failed to find default signer: %s\n", gpg_strerror (rc)); + return NULL; + } + + kh = keydb_new (0); + if (!kh) + return NULL; + + rc = keydb_search (kh, &desc, 1); + if (rc) + { + log_debug ("failed to find default certificate: rc=%d\n", rc); + } + else + { + rc = keydb_get_cert (kh, &cert); + if (rc) + { + log_debug ("failed to get cert: rc=%d\n", rc); + } + } + + keydb_release (kh); + return cert; +} + +/* Depending on the options in CTRL add the certificate CERT as well as + other certificate up in the chain to the Root-CA to the CMS + object. */ +static int +add_certificate_list (CTRL ctrl, KsbaCMS cms, KsbaCert cert) +{ + KsbaError err; + int rc = 0; + KsbaCert next = NULL; + int n; + int not_root = 0; + + ksba_cert_ref (cert); + + n = ctrl->include_certs; + if (n == -2) + { + not_root = 1; + n = -1; + } + if (n < 0 || n > 50) + n = 50; /* We better apply an upper bound */ + + if (n) + { + if (not_root && gpgsm_is_root_cert (cert)) + err = 0; + else + err = ksba_cms_add_cert (cms, cert); + if (err) + goto ksba_failure; + } + while ( n-- && !(rc = gpgsm_walk_cert_chain (cert, &next)) ) + { + if (not_root && gpgsm_is_root_cert (next)) + err = 0; + else + err = ksba_cms_add_cert (cms, next); + ksba_cert_release (cert); + cert = next; next = NULL; + if (err) + goto ksba_failure; + } + ksba_cert_release (cert); + + return rc == -1? 0: rc; + + ksba_failure: + ksba_cert_release (cert); + log_error ("ksba_cms_add_cert failed: %s\n", ksba_strerror (err)); + return map_ksba_err (err); +} + + + + +/* Perform a sign operation. + + Sign the data received on DATA-FD in embedded mode or in detached + mode when DETACHED is true. Write the signature to OUT_FP. The + keys used to sign are taken from SIGNERLIST or the default one will + be used if the value of this argument is NULL. */ +int +gpgsm_sign (CTRL ctrl, CERTLIST signerlist, + int data_fd, int detached, FILE *out_fp) +{ + int i, rc; + KsbaError err; + Base64Context b64writer = NULL; + KsbaWriter writer; + KsbaCMS cms = NULL; + KsbaStopReason stopreason; + KEYDB_HANDLE kh = NULL; + gcry_md_hd_t data_md = NULL; + int signer; + const char *algoid; + int algo; + time_t signed_at; + CERTLIST cl; + int release_signerlist = 0; + + kh = keydb_new (0); + if (!kh) + { + log_error (_("failed to allocated keyDB handle\n")); + rc = gpg_error (GPG_ERR_GENERAL); + goto leave; + } + + ctrl->pem_name = "SIGNED MESSAGE"; + rc = gpgsm_create_writer (&b64writer, ctrl, out_fp, &writer); + if (rc) + { + log_error ("can't create writer: %s\n", gpg_strerror (rc)); + goto leave; + } + + cms = ksba_cms_new (); + if (!cms) + { + rc = gpg_error (GPG_ERR_ENOMEM); + goto leave; + } + + err = ksba_cms_set_reader_writer (cms, NULL, writer); + if (err) + { + log_debug ("ksba_cms_set_reader_writer failed: %s\n", + ksba_strerror (err)); + rc = map_ksba_err (err); + goto leave; + } + + /* We are going to create signed data with data as encap. content */ + err = ksba_cms_set_content_type (cms, 0, KSBA_CT_SIGNED_DATA); + if (!err) + err = ksba_cms_set_content_type (cms, 1, KSBA_CT_DATA); + if (err) + { + log_debug ("ksba_cms_set_content_type failed: %s\n", + ksba_strerror (err)); + rc = map_ksba_err (err); + goto leave; + } + + /* If no list of signers is given, use a default one. */ + if (!signerlist) + { + KsbaCert cert = get_default_signer (); + if (!cert) + { + log_error ("no default signer found\n"); + rc = gpg_error (GPG_ERR_GENERAL); + goto leave; + } + signerlist = xtrycalloc (1, sizeof *signerlist); + if (!signerlist) + { + rc = OUT_OF_CORE (errno); + ksba_cert_release (cert); + goto leave; + } + signerlist->cert = cert; + release_signerlist = 1; + } + + + /* Gather certificates of signers and store them in the CMS object. */ + for (cl=signerlist; cl; cl = cl->next) + { + rc = gpgsm_cert_use_sign_p (cl->cert); + if (rc) + goto leave; + + err = ksba_cms_add_signer (cms, cl->cert); + if (err) + { + log_error ("ksba_cms_add_signer failed: %s\n", ksba_strerror (err)); + rc = map_ksba_err (err); + goto leave; + } + rc = add_certificate_list (ctrl, cms, cl->cert); + if (rc) + { + log_error ("failed to store list of certificates: %s\n", + gpg_strerror(rc)); + goto leave; + } + /* Set the hash algorithm we are going to use */ + err = ksba_cms_add_digest_algo (cms, "1.3.14.3.2.26" /*SHA-1*/); + if (err) + { + log_debug ("ksba_cms_add_digest_algo failed: %s\n", + ksba_strerror (err)); + rc = map_ksba_err (err); + goto leave; + } + } + + /* Prepare hashing (actually we are figuring out what we have set above)*/ + rc = gcry_md_open (&data_md, 0, 0); + if (rc) + { + log_error ("md_open failed: %s\n", gpg_strerror (rc)); + goto leave; + } + if (DBG_HASHING) + gcry_md_start_debug (data_md, "sign.data"); + + for (i=0; (algoid=ksba_cms_get_digest_algo_list (cms, i)); i++) + { + algo = gcry_md_map_name (algoid); + if (!algo) + { + log_error ("unknown hash algorithm `%s'\n", algoid? algoid:"?"); + rc = gpg_error (GPG_ERR_BUG); + goto leave; + } + gcry_md_enable (data_md, algo); + } + + if (detached) + { /* we hash the data right now so that we can store the message + digest. ksba_cms_build() takes this as an flag that detached + data is expected. */ + unsigned char *digest; + size_t digest_len; + /* Fixme do this for all signers and get the algo to use from + the signer's certificate - does not make mich sense, bu we + should do this consistent as we have already done it above */ + algo = GCRY_MD_SHA1; + hash_data (data_fd, data_md); + digest = gcry_md_read (data_md, algo); + digest_len = gcry_md_get_algo_dlen (algo); + if ( !digest || !digest_len) + { + log_error ("problem getting the hash of the data\n"); + rc = gpg_error (GPG_ERR_BUG); + goto leave; + } + for (cl=signerlist,signer=0; cl; cl = cl->next, signer++) + { + err = ksba_cms_set_message_digest (cms, signer, digest, digest_len); + if (err) + { + log_error ("ksba_cms_set_message_digest failed: %s\n", + ksba_strerror (err)); + rc = map_ksba_err (err); + goto leave; + } + } + } + + signed_at = gnupg_get_time (); + for (cl=signerlist,signer=0; cl; cl = cl->next, signer++) + { + err = ksba_cms_set_signing_time (cms, signer, signed_at); + if (err) + { + log_error ("ksba_cms_set_signing_time failed: %s\n", + ksba_strerror (err)); + rc = map_ksba_err (err); + goto leave; + } + } + + do + { + err = ksba_cms_build (cms, &stopreason); + if (err) + { + log_debug ("ksba_cms_build failed: %s\n", ksba_strerror (err)); + rc = map_ksba_err (err); + goto leave; + } + + if (stopreason == KSBA_SR_BEGIN_DATA) + { /* hash the data and store the message digest */ + unsigned char *digest; + size_t digest_len; + + assert (!detached); + /* Fixme: get the algo to use from the signer's certificate + - does not make much sense, but we should do this + consistent as we have already done it above. Code is + mostly duplicated above. */ + + algo = GCRY_MD_SHA1; + rc = hash_and_copy_data (data_fd, data_md, writer); + if (rc) + goto leave; + digest = gcry_md_read (data_md, algo); + digest_len = gcry_md_get_algo_dlen (algo); + if ( !digest || !digest_len) + { + log_error ("problem getting the hash of the data\n"); + rc = gpg_error (GPG_ERR_BUG); + goto leave; + } + for (cl=signerlist,signer=0; cl; cl = cl->next, signer++) + { + err = ksba_cms_set_message_digest (cms, signer, + digest, digest_len); + if (err) + { + log_error ("ksba_cms_set_message_digest failed: %s\n", + ksba_strerror (err)); + rc = map_ksba_err (err); + goto leave; + } + } + } + else if (stopreason == KSBA_SR_NEED_SIG) + { /* calculate the signature for all signers */ + gcry_md_hd_t md; + + algo = GCRY_MD_SHA1; + rc = gcry_md_open (&md, algo, 0); + if (rc) + { + log_error ("md_open failed: %s\n", gpg_strerror (rc)); + goto leave; + } + if (DBG_HASHING) + gcry_md_start_debug (md, "sign.attr"); + ksba_cms_set_hash_function (cms, HASH_FNC, md); + for (cl=signerlist,signer=0; cl; cl = cl->next, signer++) + { + char *sigval = NULL; + char *buf, *fpr; + + if (signer) + gcry_md_reset (md); + rc = ksba_cms_hash_signed_attrs (cms, signer); + if (rc) + { + log_debug ("hashing signed attrs failed: %s\n", + ksba_strerror (rc)); + gcry_md_close (md); + goto leave; + } + + rc = gpgsm_create_cms_signature (cl->cert, md, algo, &sigval); + if (rc) + { + gcry_md_close (md); + goto leave; + } + + err = ksba_cms_set_sig_val (cms, signer, sigval); + xfree (sigval); + if (err) + { + log_error ("failed to store the signature: %s\n", + ksba_strerror (err)); + rc = map_ksba_err (err); + gcry_md_close (md); + goto leave; + } + + /* write a status message */ + fpr = gpgsm_get_fingerprint_hexstring (cl->cert, GCRY_MD_SHA1); + if (!fpr) + { + rc = gpg_error (GPG_ERR_ENOMEM); + gcry_md_close (md); + goto leave; + } + rc = asprintf (&buf, "%c %d %d 00 %lu %s", + detached? 'D':'S', + GCRY_PK_RSA, /* FIXME: get pk algo from cert */ + algo, + (ulong)signed_at, + fpr); + xfree (fpr); + if (rc < 0) + { + rc = gpg_error (GPG_ERR_ENOMEM); + gcry_md_close (md); + goto leave; + } + rc = 0; + gpgsm_status (ctrl, STATUS_SIG_CREATED, buf); + free (buf); /* yes, we must use the regular free() here */ + } + gcry_md_close (md); + + } + } + while (stopreason != KSBA_SR_READY); + + rc = gpgsm_finish_writer (b64writer); + if (rc) + { + log_error ("write failed: %s\n", gpg_strerror (rc)); + goto leave; + } + + log_info ("signature created\n"); + + + leave: + if (release_signerlist) + gpgsm_release_certlist (signerlist); + ksba_cms_release (cms); + gpgsm_destroy_writer (b64writer); + keydb_release (kh); + gcry_md_close (data_md); + return rc; +} diff --git a/sm/verify.c b/sm/verify.c new file mode 100644 index 000000000..6dd4f4e5b --- /dev/null +++ b/sm/verify.c @@ -0,0 +1,550 @@ +/* verify.c - Verify a messages signature + * Copyright (C) 2001, 2002, 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 +#include + +#include "gpgsm.h" +#include +#include + +#include "keydb.h" +#include "i18n.h" + +static char * +strtimestamp_r (time_t atime) +{ + char *buffer = xmalloc (15); + + if (atime < 0) + strcpy (buffer, "????" "-??" "-??"); + else if (!atime) + strcpy (buffer, "none"); + else + { + struct tm *tp; + + tp = gmtime( &atime ); + sprintf (buffer, "%04d-%02d-%02d", + 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday); + } + return buffer; +} + + + +/* Hash the data for a detached signature */ +static void +hash_data (int fd, gcry_md_hd_t md) +{ + FILE *fp; + char buffer[4096]; + int nread; + + fp = fdopen ( dup (fd), "rb"); + if (!fp) + { + log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno)); + return; + } + + do + { + nread = fread (buffer, 1, DIM(buffer), fp); + gcry_md_write (md, buffer, nread); + } + while (nread); + if (ferror (fp)) + log_error ("read error on fd %d: %s\n", fd, strerror (errno)); + fclose (fp); +} + + + + +/* Perform a verify operation. To verify detached signatures, data_fd + must be different than -1. With OUT_FP given and a non-detached + signature, the signed material is written to that stream. */ +int +gpgsm_verify (CTRL ctrl, int in_fd, int data_fd, FILE *out_fp) +{ + int i, rc; + Base64Context b64reader = NULL; + Base64Context b64writer = NULL; + KsbaError err; + KsbaReader reader; + KsbaWriter writer = NULL; + KsbaCMS cms = NULL; + KsbaStopReason stopreason; + KsbaCert cert; + KEYDB_HANDLE kh; + gcry_md_hd_t data_md = NULL; + int signer; + const char *algoid; + int algo; + int is_detached; + FILE *fp = NULL; + char *p; + + kh = keydb_new (0); + if (!kh) + { + log_error (_("failed to allocated keyDB handle\n")); + rc = gpg_error (GPG_ERR_GENERAL); + goto leave; + } + + + fp = fdopen ( dup (in_fd), "rb"); + if (!fp) + { + rc = gpg_error (gpg_err_code_from_errno (errno)); + log_error ("fdopen() failed: %s\n", strerror (errno)); + goto leave; + } + + rc = gpgsm_create_reader (&b64reader, ctrl, fp, &reader); + if (rc) + { + log_error ("can't create reader: %s\n", gpg_strerror (rc)); + goto leave; + } + + if (out_fp) + { + rc = gpgsm_create_writer (&b64writer, ctrl, out_fp, &writer); + if (rc) + { + log_error ("can't create writer: %s\n", gpg_strerror (rc)); + goto leave; + } + } + + cms = ksba_cms_new (); + if (!cms) + { + rc = gpg_error (GPG_ERR_ENOMEM); + goto leave; + } + + err = ksba_cms_set_reader_writer (cms, reader, writer); + if (err) + { + log_error ("ksba_cms_set_reader_writer failed: %s\n", + ksba_strerror (err)); + rc = map_ksba_err (err); + goto leave; + } + + rc = gcry_md_open (&data_md, 0, 0); + if (rc) + { + log_error ("md_open failed: %s\n", gpg_strerror (rc)); + goto leave; + } + if (DBG_HASHING) + gcry_md_start_debug (data_md, "vrfy.data"); + + is_detached = 0; + do + { + err = ksba_cms_parse (cms, &stopreason); + if (err) + { + log_error ("ksba_cms_parse failed: %s\n", ksba_strerror (err)); + rc = map_ksba_err (err); + goto leave; + } + + if (stopreason == KSBA_SR_NEED_HASH) + { + is_detached = 1; + if (opt.verbose) + log_info ("detached signature\n"); + } + + if (stopreason == KSBA_SR_NEED_HASH + || stopreason == KSBA_SR_BEGIN_DATA) + { /* We are now able to enable the hash algorithms */ + for (i=0; (algoid=ksba_cms_get_digest_algo_list (cms, i)); i++) + { + algo = gcry_md_map_name (algoid); + if (!algo) + log_error ("unknown hash algorithm `%s'\n", + algoid? algoid:"?"); + else + gcry_md_enable (data_md, algo); + } + if (is_detached) + { + if (data_fd == -1) + log_info ("detached signature w/o data " + "- assuming certs-only\n"); + else + hash_data (data_fd, data_md); + } + else + { + ksba_cms_set_hash_function (cms, HASH_FNC, data_md); + } + } + else if (stopreason == KSBA_SR_END_DATA) + { /* The data bas been hashed */ + + } + } + while (stopreason != KSBA_SR_READY); + + if (b64writer) + { + rc = gpgsm_finish_writer (b64writer); + if (rc) + { + log_error ("write failed: %s\n", gpg_strerror (rc)); + goto leave; + } + } + + if (data_fd != -1 && !is_detached) + { + log_error ("data given for a non-detached signature\n"); + rc = gpg_error (GPG_ERR_CONFLICT); + goto leave; + } + + for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++) + { + /* Fixme: it might be better to check the validity of the + certificate first before entering it into the DB. This way + we would avoid cluttering the DB with invalid + certificates. */ + keydb_store_cert (cert, 0, NULL); + ksba_cert_release (cert); + } + + cert = NULL; + err = 0; + for (signer=0; ; signer++) + { + char *issuer = NULL; + KsbaSexp sigval = NULL; + time_t sigtime, keyexptime; + KsbaSexp serial; + char *msgdigest = NULL; + size_t msgdigestlen; + char *ctattr; + + err = ksba_cms_get_issuer_serial (cms, signer, &issuer, &serial); + if (!signer && err == KSBA_No_Data && data_fd == -1 && is_detached) + { + log_info ("certs-only message accepted\n"); + err = 0; + break; + } + if (err) + { + if (signer && err == -1) + err = 0; + break; + } + if (DBG_X509) + { + log_debug ("signer %d - issuer: `%s'\n", + signer, issuer? issuer:"[NONE]"); + log_debug ("signer %d - serial: ", signer); + gpgsm_dump_serial (serial); + log_printf ("\n"); + } + + err = ksba_cms_get_signing_time (cms, signer, &sigtime); + if (err == KSBA_No_Data) + sigtime = 0; + else if (err) + { + log_error ("error getting signing time: %s\n", ksba_strerror (err)); + sigtime = (time_t)-1; + } + + err = ksba_cms_get_message_digest (cms, signer, + &msgdigest, &msgdigestlen); + if (!err) + { + algoid = ksba_cms_get_digest_algo (cms, signer); + algo = gcry_md_map_name (algoid); + if (DBG_X509) + log_debug ("signer %d - digest algo: %d\n", signer, algo); + if ( !gcry_md_info (data_md, GCRYCTL_IS_ALGO_ENABLED, &algo, NULL) ) + { + log_error ("digest algo %d has not been enabled\n", algo); + goto next_signer; + } + } + else if (err == KSBA_No_Data) + { + assert (!msgdigest); + err = 0; + algoid = NULL; + algo = 0; + } + else /* real error */ + break; + + err = ksba_cms_get_sigattr_oids (cms, signer, + "1.2.840.113549.1.9.3",&ctattr); + if (!err) + { + const char *s; + + if (DBG_X509) + log_debug ("signer %d - content-type attribute: %s", signer, ctattr); + s = ksba_cms_get_content_oid (cms, 1); + if (!s || strcmp (ctattr, s)) + { + log_error ("content-type attribute does not match " + "actual content-type\n"); + ksba_free (ctattr); + ctattr = NULL; + goto next_signer; + } + ksba_free (ctattr); + ctattr = NULL; + } + else if (err != -1) + { + log_error ("error getting content-type attribute: %s\n", + ksba_strerror (err)); + goto next_signer; + } + err = 0; + + + sigval = ksba_cms_get_sig_val (cms, signer); + if (!sigval) + { + log_error ("no signature value available\n"); + goto next_signer; + } + if (DBG_X509) + log_debug ("signer %d - signature available", signer); + + /* Find the certificate of the signer */ + keydb_search_reset (kh); + rc = keydb_search_issuer_sn (kh, issuer, serial); + if (rc) + { + if (rc == -1) + { + log_error ("certificate not found\n"); + rc = gpg_error (GPG_ERR_NO_PUBKEY); + } + else + log_error ("failed to find the certificate: %s\n", + gpg_strerror(rc)); + { + char numbuf[50]; + sprintf (numbuf, "%d", rc); + + gpgsm_status2 (ctrl, STATUS_ERROR, "verify.findkey", + numbuf, NULL); + } + /* fixme: we might want to append the issuer and serial + using our standard notation */ + goto next_signer; + } + + rc = keydb_get_cert (kh, &cert); + if (rc) + { + log_error ("failed to get cert: %s\n", gpg_strerror (rc)); + goto next_signer; + } + + log_info (_("Signature made ")); + if (sigtime) + gpgsm_dump_time (sigtime); + else + log_printf (_("[date not given]")); + log_printf (_(" using certificate ID %08lX\n"), + gpgsm_get_short_fingerprint (cert)); + + + if (msgdigest) + { /* Signed attributes are available. */ + gcry_md_hd_t md; + unsigned char *s; + + /* check that the message digest in the signed attributes + matches the one we calculated on the data */ + s = gcry_md_read (data_md, algo); + if ( !s || !msgdigestlen + || gcry_md_get_algo_dlen (algo) != msgdigestlen + || !s || memcmp (s, msgdigest, msgdigestlen) ) + { + char *fpr; + + log_error ("invalid signature: message digest attribute " + "does not match calculated one\n"); + fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); + gpgsm_status (ctrl, STATUS_BADSIG, fpr); + xfree (fpr); + goto next_signer; + } + + rc = gcry_md_open (&md, algo, 0); + if (rc) + { + log_error ("md_open failed: %s\n", gpg_strerror (rc)); + goto next_signer; + } + if (DBG_HASHING) + gcry_md_start_debug (md, "vrfy.attr"); + + ksba_cms_set_hash_function (cms, HASH_FNC, md); + rc = ksba_cms_hash_signed_attrs (cms, signer); + if (rc) + { + log_error ("hashing signed attrs failed: %s\n", + ksba_strerror (rc)); + gcry_md_close (md); + goto next_signer; + } + rc = gpgsm_check_cms_signature (cert, sigval, md, algo); + gcry_md_close (md); + } + else + { + rc = gpgsm_check_cms_signature (cert, sigval, data_md, algo); + } + + if (rc) + { + char *fpr; + + log_error ("invalid signature: %s\n", gpg_strerror (rc)); + fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); + gpgsm_status (ctrl, STATUS_BADSIG, fpr); + xfree (fpr); + goto next_signer; + } + rc = gpgsm_cert_use_verify_p (cert); /*(this displays an info message)*/ + if (rc) + { + gpgsm_status_with_err_code (ctrl, STATUS_ERROR, "verify.keyusage", + gpg_err_code (rc)); + rc = 0; + } + + if (DBG_X509) + log_debug ("signature okay - checking certs\n"); + rc = gpgsm_validate_chain (ctrl, cert, &keyexptime); + if (gpg_err_code (rc) == GPG_ERR_CERT_EXPIRED) + { + gpgsm_status (ctrl, STATUS_EXPKEYSIG, NULL); + rc = 0; + } + else + gpgsm_status (ctrl, STATUS_GOODSIG, NULL); + + { + char *buf, *fpr, *tstr; + + fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); + tstr = strtimestamp_r (sigtime); + buf = xmalloc ( strlen(fpr) + strlen (tstr) + 120); + sprintf (buf, "%s %s %lu %lu", fpr, tstr, + (unsigned long)sigtime, (unsigned long)keyexptime ); + xfree (tstr); + xfree (fpr); + gpgsm_status (ctrl, STATUS_VALIDSIG, buf); + xfree (buf); + } + + if (rc) /* of validate_chain */ + { + log_error ("invalid certification chain: %s\n", gpg_strerror (rc)); + if (gpg_err_code (rc) == GPG_ERR_BAD_CERT_CHAIN + || gpg_err_code (rc) == GPG_ERR_BAD_CERT + || gpg_err_code (rc) == GPG_ERR_BAD_CA_CERT + || gpg_err_code (rc) == GPG_ERR_CERT_REVOKED) + gpgsm_status_with_err_code (ctrl, STATUS_TRUST_NEVER, NULL, + gpg_err_code (rc)); + else + gpgsm_status_with_err_code (ctrl, STATUS_TRUST_UNDEFINED, NULL, + gpg_err_code (rc)); + goto next_signer; + } + + for (i=0; (p = ksba_cert_get_subject (cert, i)); i++) + { + log_info (!i? _("Good signature from") + : _(" aka")); + log_printf (" \""); + gpgsm_print_name (log_get_stream (), p); + log_printf ("\"\n"); + ksba_free (p); + } + + gpgsm_status (ctrl, STATUS_TRUST_FULLY, NULL); + + + next_signer: + rc = 0; + xfree (issuer); + xfree (serial); + xfree (sigval); + xfree (msgdigest); + ksba_cert_release (cert); + cert = NULL; + } + rc = 0; + if (err) + { + log_error ("ksba error: %s\n", ksba_strerror (err)); + rc = map_ksba_err (rc); + } + + + + leave: + ksba_cms_release (cms); + gpgsm_destroy_reader (b64reader); + gpgsm_destroy_writer (b64writer); + keydb_release (kh); + gcry_md_close (data_md); + if (fp) + fclose (fp); + + if (rc) + { + char numbuf[50]; + sprintf (numbuf, "%d", rc ); + gpgsm_status2 (ctrl, STATUS_ERROR, "verify.leave", + numbuf, NULL); + } + + return rc; +} + -- cgit From 1bcf8ef9dea1a9b171c27ef48cadb79df6201e33 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 5 Aug 2003 17:11:04 +0000 Subject: Cleanups, fixes and PC/SC support --- ChangeLog | 4 + NEWS | 10 ++ README | 38 ++++- TODO | 3 + configure.ac | 4 +- doc/scdaemon.texi | 4 + g10/ChangeLog | 20 +++ g10/Makefile.am | 17 +- g10/g10.c | 107 ++----------- g10/pkglue.c | 37 ++++- g10/pkglue.h | 1 + g10/seckey-cert.c | 15 +- g10/status.c | 226 +-------------------------- g10/status.h | 4 - scd/ChangeLog | 22 +++ scd/apdu.c | 426 +++++++++++++++++++++++++++++++++++++++++++-------- scd/apdu.h | 3 +- scd/app-common.h | 1 + scd/app-openpgp.c | 135 ++++------------ scd/app.c | 13 +- scd/sc-copykeys.c | 8 +- scd/sc-investigate.c | 8 +- scd/scdaemon.c | 12 +- scd/scdaemon.h | 1 + 24 files changed, 600 insertions(+), 519 deletions(-) (limited to 'scd/scdaemon.c') diff --git a/ChangeLog b/ChangeLog index b846f27a7..83e9cba0c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2003-08-05 Werner Koch + + * configure.ac (GNUPG_DEFAULT_HONMEDIR): Changed back to ~/.gnupg. + 2003-07-31 Werner Koch * Makefile.am (DISTCLEANFILES): Add g10defs.h diff --git a/NEWS b/NEWS index 794ad11f1..9e6b9f1af 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,16 @@ Noteworthy changes in version 1.9.0 (unreleased) ------------------------------------------------ + * gpg has been renamed to gpg2 and gpgv to gpgv2. This is a + temporary solution to allow co-existing with stable gpg versions. + + * The default config file is ~/.gnupg/gpg.conf-1.9.0 if it exists. + + * Removed the -k, -kv and -kvv commands. -k is now an alias to + --list-keys. New command -K as alias for --list-secret-keys. + + * Removed --run-as-shm-coprocess feature. + * gpg does now also use libgcrypt, libgpg-error is required. * New gpgsm commands --call-dirmngr and --call-protect-tool. diff --git a/README b/README index 7aea069b3..8dea9dbb9 100644 --- a/README +++ b/README @@ -34,6 +34,21 @@ gpgsm: prepended before each block. +gpg2: +----- + +--card-status + + Show information pertaining smartcards implementing the OpenPGP + application. + +--change-pin + + Offers a menu to change the PIN of OpenPGP smartcards and to reset + the retry counters. + + + OPTIONS ======= @@ -139,6 +154,22 @@ gpg-agent: lockups in case of bugs. +scdaemon: +-------- + +--ctapi-driver + + The default for Scdaemon is to use the PC/SC API currently provided + by libpcsclite.so. As an alternative the ctAPI can be used by + specify this option with the appropriate driver name + (e.g. libtowitoko.so). + +--reader-port + + This specifies the port of the chipcard reader. For PC/SC this is + currently ignored and the first PC/SC reader is used. For the + ctAPI, a number must be specified (the default is 32768 for the + first USB port). @@ -174,10 +205,15 @@ gpg.conf Options for gpg. Note that old versions of gpg use the filename `options' instead of `gpg.conf'. +gpg.conf-1.9.x + + Options for gpg; tried before gpg.conf + + policies.txt A list of allowed CA policies. This file should give the - object identifiers of the policies line by line. emptry lines + object identifiers of the policies line by line. Empty lines and lines startung with a hash mark are ignored. ++++++++++ diff --git a/TODO b/TODO index 431f2a2ea..c9b2d18ff 100644 --- a/TODO +++ b/TODO @@ -55,5 +55,8 @@ might want to have an agent context for each service request * sm/export.c ** Return an error code or a status info per user ID. +* scd/apdu.c +** We need close_reader functionality + * ALL ** Return IMPORT_OK status. diff --git a/configure.ac b/configure.ac index 9d8aef9ee..060f31d97 100644 --- a/configure.ac +++ b/configure.ac @@ -224,7 +224,7 @@ AH_BOTTOM([ #ifdef HAVE_DRIVE_LETTERS #define GNUPG_DEFAULT_HOMEDIR "c:/gnupg" #else -#define GNUPG_DEFAULT_HOMEDIR "~/.gnupg2" +#define GNUPG_DEFAULT_HOMEDIR "~/.gnupg" #endif #define GNUPG_PRIVATE_KEYS_DIR "private-keys-v1.d" @@ -966,7 +966,7 @@ cat >g10defs.tmp < + + * Makefile.am (install-data-local): Dropped check for the ancient + gpgm tool. + (bin_PROGRAMS): Renamed gpg to gpg2 and gpgv to gpgv2. This is so + that it won't conflict with the current stable version of gpg. + + * pkglue.c (pk_check_secret_key): New. + * seckey-cert.c (do_check): Reenable this test here again. + + * g10.c (main): Add command -K as an alias for + --list-secret-keys. Command "-k" is now an alias to --list-keys. + Remove special treatment of -kv and -kvv. + (set_cmd): Ditto. + (main): Strip a "-cvs" suffix when testing for a version specific + config file. + + * status.h, status.c, g10.c [USE_SHM_COPROCESSING]: Removed. This + is not any longer available. + 2003-07-29 Werner Koch * g10.c (main): Add secmem features and set the random seed file. diff --git a/g10/Makefile.am b/g10/Makefile.am index d6984941e..59213d04b 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -31,8 +31,7 @@ AM_CFLAGS = -DGNUPG_LIBEXECDIR="\"$(libexecdir)\"" endif needed_libs = ../common/libcommon.a ../jnlib/libjnlib.a -#noinst_PROGRAMS = gpgd -bin_PROGRAMS = gpg gpgv +bin_PROGRAMS = gpg2 gpgv2 common_source = \ global.h gpg.h \ @@ -65,7 +64,7 @@ common_source = \ keylist.c \ pkglue.c pkglue.h -gpg_SOURCES = g10.c \ +gpg2_SOURCES = g10.c \ $(common_source) \ pkclist.c \ skclist.c \ @@ -99,7 +98,7 @@ gpg_SOURCES = g10.c \ card-util.c \ exec.c exec.h -gpgv_SOURCES = gpgv.c \ +gpgv2_SOURCES = gpgv.c \ $(common_source) \ verify.c @@ -111,8 +110,8 @@ gpgv_SOURCES = gpgv.c \ # $(common_source) LDADD = $(needed_libs) @INTLLIBS@ @CAPLIBS@ @ZLIBS@ -gpg_LDADD = $(LIBGCRYPT_LIBS) $(LDADD) -lassuan -lgpg-error -gpgv_LDADD = $(LIBGCRYPT_LIBS) $(LDADD) -lassuan -lgpg-error +gpg2_LDADD = $(LIBGCRYPT_LIBS) $(LDADD) -lassuan -lgpg-error +gpgv2_LDADD = $(LIBGCRYPT_LIBS) $(LDADD) -lassuan -lgpg-error $(PROGRAMS): $(needed_libs) @@ -120,8 +119,4 @@ install-data-local: $(mkinstalldirs) $(DESTDIR)$(pkgdatadir) $(INSTALL_DATA) $(srcdir)/options.skel \ $(DESTDIR)$(pkgdatadir)/options.skel - @set -e;\ - if test -f $(DESTDIR)$(bindir)/gpgm ; then \ - echo "removing obsolete gpgm binary" ; \ - rm $(DESTDIR)$(bindir)/gpgm ; \ - fi + diff --git a/g10/g10.c b/g10/g10.c index f89556184..aef76992b 100644 --- a/g10/g10.c +++ b/g10/g10.c @@ -61,7 +61,8 @@ enum cmd_and_opt_values { aNull = 0, aEncr = 'e', aEncrFiles, oInteractive = 'i', - oKOption = 'k', + aListKeys = 'k', + aListSecretKeys = 'K', oDryRun = 'n', oOutput = 'o', oQuiet = 'q', @@ -93,15 +94,11 @@ enum cmd_and_opt_values { aNull = 0, aDeleteKeys, aDeleteSecretKeys, aDeleteSecretAndPublicKeys, - aKMode, - aKModeC, aImport, aFastImport, aVerify, aVerifyFiles, - aListKeys, aListSigs, - aListSecretKeys, aSendKeys, aRecvKeys, aSearchKeys, @@ -213,7 +210,6 @@ enum cmd_and_opt_values { aNull = 0, oTrustModel, oForceOwnertrust, oEmuChecksumBug, - oRunAsShmCP, oSetFilename, oForYourEyesOnly, oNoForYourEyesOnly, @@ -514,7 +510,6 @@ static ARGPARSE_OPTS opts[] = { /* Not yet used */ /* { aListTrustPath, "list-trust-path",0, "@"}, */ { aPipeMode, "pipemode", 0, "@" }, - { oKOption, NULL, 0, "@"}, { oPasswdFD, "passphrase-fd",1, "@" }, #ifdef __riscos__ { oPasswdFile, "passphrase-file",2, "@" }, @@ -549,7 +544,6 @@ static ARGPARSE_OPTS opts[] = { { oTrustModel, "trust-model", 2, "@"}, { oForceOwnertrust, "force-ownertrust", 2, "@"}, { oEmuChecksumBug, "emulate-checksum-bug", 0, "@"}, - { oRunAsShmCP, "run-as-shm-coprocess", 4, "@" }, { oSetFilename, "set-filename", 2, "@" }, { oForYourEyesOnly, "for-your-eyes-only", 0, "@" }, { oNoForYourEyesOnly, "no-for-your-eyes-only", 0, "@" }, @@ -879,8 +873,6 @@ set_cmd( enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd ) cmd = aSignSym; else if( cmd == aSym && new_cmd == aSign ) cmd = aSignSym; - else if( cmd == aKMode && new_cmd == aSym ) - cmd = aKModeC; else if( ( cmd == aSign && new_cmd == aClearsign ) || ( cmd == aClearsign && new_cmd == aSign ) ) cmd = aClearsign; @@ -1167,9 +1159,6 @@ main( int argc, char **argv ) int pwfd = -1; int with_fpr = 0; /* make an option out of --fingerprint */ int any_explicit_recipient = 0; -#ifdef USE_SHM_COPROCESSING - ulong requested_shm_size=0; -#endif #ifdef __riscos__ riscos_global_defaults(); @@ -1276,19 +1265,6 @@ main( int argc, char **argv ) opt.strict=0; log_set_strict(0); } -#ifdef USE_SHM_COPROCESSING - else if( pargs.r_opt == oRunAsShmCP ) { - /* does not make sense in a options file, we do it here, - * so that we are the able to drop setuid as soon as possible */ - opt.shm_coprocess = 1; - requested_shm_size = pargs.r.ret_ulong; - } - else if ( pargs.r_opt == oStatusFD ) { - /* this is needed to ensure that the status-fd filedescriptor is - * initialized when init_shm_coprocessing() is called */ - set_status_fd( iobuf_translate_file_handle (pargs.r.ret_int, 1) ); - } -#endif } #ifdef HAVE_DOSISH_SYSTEM @@ -1301,11 +1277,7 @@ main( int argc, char **argv ) set_homedir (buf); } #endif -#ifdef USE_SHM_COPROCESSING - if( opt.shm_coprocess ) { - init_shm_coprocessing(requested_shm_size, 1 ); - } -#endif + /* Initialize the secure memory. */ gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0); maybe_setuid = 0; @@ -1318,9 +1290,14 @@ main( int argc, char **argv ) if( default_config ) { - /* Try for a version specific config file first */ + /* Try for a version specific config file first but strip our + usual cvs suffix. That suffix indicates that it is not yet + the given version but we already want this config file. */ configname = make_filename(opt.homedir, "gpg" EXTSEP_S "conf-" SAFE_VERSION, NULL ); + if (!strcmp (configname + strlen (configname) - 4, "-cvs")) + configname[strlen (configname)-4] = 0; + if(access(configname,R_OK)) { xfree (configname); @@ -1458,7 +1435,6 @@ main( int argc, char **argv ) case oInteractive: opt.interactive = 1; break; case oVerbose: g10_opt_verbose++; opt.verbose++; opt.list_sigs=1; break; - case oKOption: set_cmd( &cmd, aKMode ); break; case oBatch: opt.batch = 1; nogreeting = 1; break; case oUseAgent: @@ -1631,17 +1607,6 @@ main( int argc, char **argv ) case oGnuPG: opt.compliance = CO_GNUPG; break; case oEmuMDEncodeBug: opt.emulate_bugs |= EMUBUG_MDENCODE; break; case oCompressSigs: opt.compress_sigs = 1; break; - case oRunAsShmCP: -#ifndef __riscos__ -# ifndef USE_SHM_COPROCESSING - /* not possible in the option file, - * but we print the warning here anyway */ - log_error("shared memory coprocessing is not available\n"); -# endif -#else /* __riscos__ */ - riscos_not_implemented("run-as-shm-coprocess"); -#endif /* __riscos__ */ - break; case oSetFilename: opt.set_filename = pargs.r.ret_str; break; case oForYourEyesOnly: eyes_only = 1; break; case oNoForYourEyesOnly: eyes_only = 0; break; @@ -2276,21 +2241,6 @@ main( int argc, char **argv ) set_cmd( &cmd, aListKeys); } - if( cmd == aKMode || cmd == aKModeC ) { /* kludge to be compatible to pgp */ - if( cmd == aKModeC ) { - opt.fingerprint = 1; - cmd = aKMode; - } - opt.list_sigs = 0; - if( opt.verbose > 2 ) - opt.check_sigs++; - if( opt.verbose > 1 ) - opt.list_sigs++; - - opt.verbose = opt.verbose > 1; - g10_opt_verbose = opt.verbose; - } - /* Compression algorithm 0 means no compression at all */ if( opt.def_compress_algo == 0) opt.compress = 0; @@ -2302,12 +2252,11 @@ main( int argc, char **argv ) if( opt.verbose > 1 ) set_packet_list_mode(1); - /* Add the keyrings, but not for some special commands and not in - case of "-kvv userid keyring". Also avoid adding the secret - keyring for a couple of commands to avoid unneeded access in - case the secrings are stored on a floppy */ - if( cmd != aDeArmor && cmd != aEnArmor - && !(cmd == aKMode && argc == 2 ) ) + /* Add the keyrings, but not for some special commands. Also + avoid adding the secret keyring for a couple of commands to + avoid unneeded access in case the secrings are stored on a + floppy */ + if( cmd != aDeArmor && cmd != aEnArmor ) { if (cmd != aCheckKeys && cmd != aListSigs && cmd != aListKeys && cmd != aVerify && cmd != aVerifyFiles @@ -2544,34 +2493,6 @@ main( int argc, char **argv ) free_strlist(sl); break; - case aKMode: /* list keyring -- NOTE: This will be removed soon */ - if( argc < 2 ) { /* -kv [userid] */ - sl = NULL; - if (argc && **argv) - add_to_strlist2( &sl, *argv, utf8_strings ); - public_key_list( sl ); - free_strlist(sl); - } - else if( argc == 2 ) { /* -kv userid keyring */ - if( access( argv[1], R_OK ) ) { - log_error(_("can't open %s: %s\n"), - print_fname_stdin(argv[1]), strerror(errno)); - } - else { - /* add keyring (default keyrings are not registered in this - * special case */ - keydb_add_resource( argv[1], 0, 0 ); - sl = NULL; - if (**argv) - add_to_strlist2( &sl, *argv, utf8_strings ); - public_key_list( sl ); - free_strlist(sl); - } - } - else - wrong_args(_("-k[v][v][v][c] [user-id] [keyring]") ); - break; - case aKeygen: /* generate a key */ if( opt.batch ) { if( argc > 1 ) diff --git a/g10/pkglue.c b/g10/pkglue.c index 7920a5223..015aaf9ff 100644 --- a/g10/pkglue.c +++ b/g10/pkglue.c @@ -287,8 +287,39 @@ pk_decrypt (int algo, gcry_mpi_t * result, gcry_mpi_t * data, } +/* Check whether SKEY is a suitable secret key. */ +int +pk_check_secret_key (int algo, gcry_mpi_t *skey) +{ + gcry_sexp_t s_skey; + int rc; + if (algo == GCRY_PK_DSA) + { + rc = gcry_sexp_build (&s_skey, NULL, + "(private-key(dsa(p%m)(q%m)(g%m)(y%m)(x%m)))", + skey[0], skey[1], skey[2], skey[3], skey[4]); + } + else if (algo == GCRY_PK_ELG || algo == GCRY_PK_ELG_E) + { + rc = gcry_sexp_build (&s_skey, NULL, + "(private-key(elg(p%m)(g%m)(y%m)(x%m)))", + skey[0], skey[1], skey[2], skey[3]); + } + else if (algo == GCRY_PK_RSA) + { + rc = gcry_sexp_build (&s_skey, NULL, + "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))", + skey[0], skey[1], skey[2], skey[3], skey[4], + skey[5]); + } + else + return GPG_ERR_PUBKEY_ALGO; - - - + if (!rc) + { + rc = gcry_pk_testkey (s_skey); + gcry_sexp_release (s_skey); + } + return rc; +} diff --git a/g10/pkglue.h b/g10/pkglue.h index 3065d66aa..43b82785b 100644 --- a/g10/pkglue.h +++ b/g10/pkglue.h @@ -29,6 +29,7 @@ int pk_encrypt (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, gcry_mpi_t *pkey); int pk_decrypt (int algo, gcry_mpi_t *result, gcry_mpi_t *data, gcry_mpi_t *skey); +int pk_check_secret_key (int algo, gcry_mpi_t *skey); #endif /*GNUPG_G10_PKGLUE_H*/ diff --git a/g10/seckey-cert.c b/g10/seckey-cert.c index 5a7db4c97..5b0238240 100644 --- a/g10/seckey-cert.c +++ b/g10/seckey-cert.c @@ -215,14 +215,13 @@ do_check( PKT_secret_key *sk, const char *tryagain_text, int mode, return gpg_error (GPG_ERR_BAD_PASSPHRASE); } /* the checksum may fail, so we also check the key itself */ -#warning fixme - we need to reenable this -/* res = pubkey_check_secret_key( sk->pubkey_algo, sk->skey ); */ -/* if( res ) { */ -/* copy_secret_key( sk, save_sk ); */ -/* passphrase_clear_cache ( keyid, sk->pubkey_algo ); */ -/* free_secret_key( save_sk ); */ -/* return gpg_error (GPG_ERR_BAD_PASSPHRASE); */ -/* } */ + res = pk_check_secret_key (sk->pubkey_algo, sk->skey); + if (res) { + copy_secret_key( sk, save_sk ); + passphrase_clear_cache ( keyid, sk->pubkey_algo ); + free_secret_key( save_sk ); + return gpg_error (GPG_ERR_BAD_PASSPHRASE); + } free_secret_key( save_sk ); sk->is_protected = 0; } diff --git a/g10/status.c b/g10/status.c index 432ec575c..4414b33e9 100644 --- a/g10/status.c +++ b/g10/status.c @@ -1,5 +1,6 @@ /* status.c - * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, + * 2003 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -25,21 +26,6 @@ #include #include #include -#ifdef USE_SHM_COPROCESSING -#ifdef USE_CAPABILITIES -#include -#endif -#ifdef HAVE_SYS_IPC_H -#include -#include -#endif -#ifdef HAVE_SYS_SHM_H -#include -#endif -#if defined(HAVE_MLOCK) -#include -#endif -#endif #include "gpg.h" #include "util.h" @@ -56,13 +42,6 @@ static FILE *statusfp; -#ifdef USE_SHM_COPROCESSING - static int shm_id = -1; - static volatile char *shm_area; - static size_t shm_size; - static int shm_is_locked; -#endif /*USE_SHM_COPROCESSING*/ - static void progress_cb (void *ctx, const char *what, int printchar, int current, int total) @@ -291,179 +270,6 @@ write_status_buffer ( int no, const char *buffer, size_t len, int wrap ) -#ifdef USE_SHM_COPROCESSING - -#ifndef IPC_RMID_DEFERRED_RELEASE -static void -remove_shmid( void ) -{ - if( shm_id != -1 ) { - shmctl ( shm_id, IPC_RMID, 0); - shm_id = -1; - } -} -#endif - -void -init_shm_coprocessing ( ulong requested_shm_size, int lock_mem ) -{ - char buf[100]; - struct shmid_ds shmds; - -#ifndef IPC_RMID_DEFERRED_RELEASE - atexit( remove_shmid ); -#endif - requested_shm_size = (requested_shm_size + 4095) & ~4095; - if ( requested_shm_size > 2 * 4096 ) - log_fatal("too much shared memory requested; only 8k are allowed\n"); - shm_size = 4096 /* one page for us */ + requested_shm_size; - - shm_id = shmget( IPC_PRIVATE, shm_size, IPC_CREAT | 0700 ); - if ( shm_id == -1 ) - log_fatal("can't get %uk of shared memory: %s\n", - (unsigned)shm_size/1024, strerror(errno)); - -#if !defined(IPC_HAVE_SHM_LOCK) \ - && defined(HAVE_MLOCK) && !defined(HAVE_BROKEN_MLOCK) - /* part of the old code which uses mlock */ - shm_area = shmat( shm_id, 0, 0 ); - if ( shm_area == (char*)-1 ) - log_fatal("can't attach %uk shared memory: %s\n", - (unsigned)shm_size/1024, strerror(errno)); - log_debug("mapped %uk shared memory at %p, id=%d\n", - (unsigned)shm_size/1024, shm_area, shm_id ); - if( lock_mem ) { -#ifdef USE_CAPABILITIES - cap_set_proc( cap_from_text("cap_ipc_lock+ep") ); -#endif - /* (need the cast for Solaris with Sun's workshop compilers) */ - if ( mlock ( (char*)shm_area, shm_size) ) - log_info("locking shared memory %d failed: %s\n", - shm_id, strerror(errno)); - else - shm_is_locked = 1; -#ifdef USE_CAPABILITIES - cap_set_proc( cap_from_text("cap_ipc_lock+p") ); -#endif - } - -#ifdef IPC_RMID_DEFERRED_RELEASE - if( shmctl( shm_id, IPC_RMID, 0) ) - log_fatal("shmctl IPC_RMDID of %d failed: %s\n", - shm_id, strerror(errno)); -#endif - - if( shmctl( shm_id, IPC_STAT, &shmds ) ) - log_fatal("shmctl IPC_STAT of %d failed: %s\n", - shm_id, strerror(errno)); - if( shmds.shm_perm.uid != getuid() ) { - shmds.shm_perm.uid = getuid(); - if( shmctl( shm_id, IPC_SET, &shmds ) ) - log_fatal("shmctl IPC_SET of %d failed: %s\n", - shm_id, strerror(errno)); - } - -#else /* this is the new code which handles the changes in the SHM - * semantics introduced with Linux 2.4. The changes is that we - * now change the permissions and then attach to the memory. - */ - - if( lock_mem ) { -#ifdef USE_CAPABILITIES - cap_set_proc( cap_from_text("cap_ipc_lock+ep") ); -#endif -#ifdef IPC_HAVE_SHM_LOCK - if ( shmctl (shm_id, SHM_LOCK, 0) ) - log_info("locking shared memory %d failed: %s\n", - shm_id, strerror(errno)); - else - shm_is_locked = 1; -#else - log_info("Locking shared memory %d failed: No way to do it\n", shm_id ); -#endif -#ifdef USE_CAPABILITIES - cap_set_proc( cap_from_text("cap_ipc_lock+p") ); -#endif - } - - if( shmctl( shm_id, IPC_STAT, &shmds ) ) - log_fatal("shmctl IPC_STAT of %d failed: %s\n", - shm_id, strerror(errno)); - if( shmds.shm_perm.uid != getuid() ) { - shmds.shm_perm.uid = getuid(); - if( shmctl( shm_id, IPC_SET, &shmds ) ) - log_fatal("shmctl IPC_SET of %d failed: %s\n", - shm_id, strerror(errno)); - } - - shm_area = shmat( shm_id, 0, 0 ); - if ( shm_area == (char*)-1 ) - log_fatal("can't attach %uk shared memory: %s\n", - (unsigned)shm_size/1024, strerror(errno)); - log_debug("mapped %uk shared memory at %p, id=%d\n", - (unsigned)shm_size/1024, shm_area, shm_id ); - -#ifdef IPC_RMID_DEFERRED_RELEASE - if( shmctl( shm_id, IPC_RMID, 0) ) - log_fatal("shmctl IPC_RMDID of %d failed: %s\n", - shm_id, strerror(errno)); -#endif - -#endif - /* write info; Protocol version, id, size, locked size */ - sprintf( buf, "pv=1 pid=%d shmid=%d sz=%u lz=%u", (int)getpid(), - shm_id, (unsigned)shm_size, shm_is_locked? (unsigned)shm_size:0 ); - write_status_text( STATUS_SHM_INFO, buf ); -} - -/**************** - * Request a string from client - * If bool, returns static string on true (do not free) or NULL for false - */ -static char * -do_shm_get( const char *keyword, int hidden, int bool ) -{ - size_t n; - byte *p; - char *string; - - if( !shm_area ) - BUG(); - - shm_area[0] = 0; /* msb of length of control block */ - shm_area[1] = 32; /* and lsb */ - shm_area[2] = 1; /* indicate that we are waiting on a reply */ - shm_area[3] = 0; /* clear data available flag */ - - write_status_text( bool? STATUS_SHM_GET_BOOL : - hidden? STATUS_SHM_GET_HIDDEN : STATUS_SHM_GET, keyword ); - - do { - pause_on_sigusr(1); - if( shm_area[0] || shm_area[1] != 32 || shm_area[2] != 1 ) - log_fatal("client modified shm control block - abort\n"); - } while( !shm_area[3] ); - shm_area[2] = 0; /* reset request flag */ - p = (byte*)shm_area+32; - n = p[0] << 8 | p[1]; - p += 2; - if( n+32+2+1 > 4095 ) - log_fatal("client returns too large data (%u bytes)\n", (unsigned)n ); - - if( bool ) - return p[0]? "" : NULL; - - string = hidden? xmalloc_secure ( n+1 ) : xmalloc ( n+1 ); - memcpy(string, p, n ); - string[n] = 0; /* make sure it is a string */ - if( hidden ) /* invalidate the memory */ - memset( p, 0, n ); - - return string; -} - -#endif /* USE_SHM_COPROCESSING */ - static int myread(int fd, void *buf, size_t count) { @@ -541,10 +347,6 @@ cpr_enabled() { if( opt.command_fd != -1 ) return 1; -#ifdef USE_SHM_COPROCESSING - if( opt.shm_coprocess ) - return 1; -#endif return 0; } @@ -555,10 +357,6 @@ cpr_get_no_help( const char *keyword, const char *prompt ) if( opt.command_fd != -1 ) return do_get_from_fd ( keyword, 0, 0 ); -#ifdef USE_SHM_COPROCESSING - if( opt.shm_coprocess ) - return do_shm_get( keyword, 0, 0 ); -#endif for(;;) { p = tty_get( prompt ); return p; @@ -572,10 +370,6 @@ cpr_get( const char *keyword, const char *prompt ) if( opt.command_fd != -1 ) return do_get_from_fd ( keyword, 0, 0 ); -#ifdef USE_SHM_COPROCESSING - if( opt.shm_coprocess ) - return do_shm_get( keyword, 0, 0 ); -#endif for(;;) { p = tty_get( prompt ); if( *p=='?' && !p[1] && !(keyword && !*keyword)) { @@ -608,10 +402,6 @@ cpr_get_hidden( const char *keyword, const char *prompt ) if( opt.command_fd != -1 ) return do_get_from_fd ( keyword, 1, 0 ); -#ifdef USE_SHM_COPROCESSING - if( opt.shm_coprocess ) - return do_shm_get( keyword, 1, 0 ); -#endif for(;;) { p = tty_get_hidden( prompt ); if( *p == '?' && !p[1] ) { @@ -628,10 +418,6 @@ cpr_kill_prompt(void) { if( opt.command_fd != -1 ) return; -#ifdef USE_SHM_COPROCESSING - if( opt.shm_coprocess ) - return; -#endif tty_kill_prompt(); return; } @@ -644,10 +430,6 @@ cpr_get_answer_is_yes( const char *keyword, const char *prompt ) if( opt.command_fd != -1 ) return !!do_get_from_fd ( keyword, 0, 1 ); -#ifdef USE_SHM_COPROCESSING - if( opt.shm_coprocess ) - return !!do_shm_get( keyword, 0, 1 ); -#endif for(;;) { p = tty_get( prompt ); trim_spaces(p); /* it is okay to do this here */ @@ -672,10 +454,6 @@ cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt ) if( opt.command_fd != -1 ) return !!do_get_from_fd ( keyword, 0, 1 ); -#ifdef USE_SHM_COPROCESSING - if( opt.shm_coprocess ) - return !!do_shm_get( keyword, 0, 1 ); -#endif for(;;) { p = tty_get( prompt ); trim_spaces(p); /* it is okay to do this here */ diff --git a/g10/status.h b/g10/status.h index 44a7d6d32..4a0bcd45b 100644 --- a/g10/status.h +++ b/g10/status.h @@ -110,10 +110,6 @@ void write_status_buffer ( int no, void write_status_text_and_buffer ( int no, const char *text, const char *buffer, size_t len, int wrap ); -#ifdef USE_SHM_COPROCESSING - void init_shm_coprocessing ( ulong requested_shm_size, int lock_mem ); -#endif /*USE_SHM_COPROCESSING*/ - int cpr_enabled(void); char *cpr_get( const char *keyword, const char *prompt ); char *cpr_get_no_help( const char *keyword, const char *prompt ); diff --git a/scd/ChangeLog b/scd/ChangeLog index ad4b0518c..ae2e6ce6e 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,25 @@ +2003-08-05 Werner Koch + + * app-openpgp.c (dump_all_do): Don't analyze constructed DOs after + an error. + +2003-08-04 Werner Koch + + * app.c (app_set_default_reader_port): New. + (select_application): Use it here. + * scdaemon.c (main): and here. + * sc-copykeys.c: --reader-port does now take a string. + * sc-investigate.c, scdaemon.c: Ditto. + * apdu.c (apdu_open_reader): Ditto. Load pcsclite if no ctapi + driver is configured. Always include code for ctapi. + (new_reader_slot): Don't test for already used ports and remove + port arg. + (open_pcsc_reader, pcsc_send_apdu, pcsc_error_string): New. + (apdu_send_le): Changed RC to long to cope with PC/SC. + + * scdaemon.c, scdaemon.h: New option --ctapi-driver. + * sc-investigate.c, sc-copykeys.c: Ditto. + 2003-07-31 Werner Koch * Makefile.am (scdaemon_LDADD): Added INTLLIBS. diff --git a/scd/apdu.c b/scd/apdu.c index 6fec584b9..60de5b953 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -29,8 +29,6 @@ #include "scdaemon.h" #include "apdu.h" -#define HAVE_CTAPI 1 - #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). */ @@ -40,7 +38,13 @@ /* 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 */ + unsigned short port; /* Port number: 0 = unused, 1 - dev/tty */ + int is_ctapi; /* This is a ctAPI driver. */ + struct { + unsigned long context; + unsigned long card; + unsigned long protocol; + } pcsc; int status; unsigned char atr[33]; size_t atrlen; @@ -55,6 +59,61 @@ static char (*CT_data) (unsigned short ctn, unsigned char *dad, unsigned char *rsp); static char (*CT_close) (unsigned short ctn); +/* PC/SC constants and function pointer. */ +#define PCSC_SCOPE_USER 0 +#define PCSC_SCOPE_TERMINAL 1 +#define PCSC_SCOPE_SYSTEM 2 +#define PCSC_SCOPE_GLOBAL 3 + +#define PCSC_PROTOCOL_T0 1 +#define PCSC_PROTOCOL_T1 2 +#define PCSC_PROTOCOL_RAW 4 + +#define PCSC_SHARE_EXCLUSIVE 1 +#define PCSC_SHARE_SHARED 2 +#define PCSC_SHARE_DIRECT 3 + +#define PCSC_LEAVE_CARD 0 +#define PCSC_RESET_CARD 1 +#define PCSC_UNPOWER_CARD 2 +#define PCSC_EJECT_CARD 3 + +struct pcsc_io_request_s { + unsigned long protocol; + unsigned long pci_len; +}; + +typedef struct pcsc_io_request_s *pcsc_io_request_t; + +long (*pcsc_establish_context) (unsigned long scope, + const void *reserved1, + const void *reserved2, + unsigned long *r_context); +long (*pcsc_release_context) (unsigned long context); +long (*pcsc_list_readers) (unsigned long context, const char *groups, + char *readers, unsigned long *readerslen); +long (*pcsc_connect) (unsigned long context, + const char *reader, + unsigned long share_mode, + unsigned long preferred_protocols, + unsigned long *r_card, + unsigned long *r_active_protocol); +long (*pcsc_disconnect) (unsigned long card, unsigned long disposition); +long (*pcsc_status) (unsigned long card, + char *reader, unsigned long *readerlen, + unsigned long *r_state, unsigned long *r_protocol, + unsigned char *atr, unsigned long *atrlen); +long (*pcsc_begin_transaction) (unsigned long card); +long (*pcsc_end_transaction) (unsigned long card); +long (*pcsc_transmit) (unsigned long card, + const pcsc_io_request_t send_pci, + const unsigned char *send_buffer, + unsigned long send_len, + pcsc_io_request_t recv_pci, + unsigned char *recv_buffer, + unsigned long *recv_len); +long (*pcsc_set_timeout) (unsigned long context, unsigned long timeout); + @@ -64,28 +123,16 @@ static char (*CT_close) (unsigned short ctn); */ -/* Find an unused reader slot for PORT and put it into the reader +/* Find an unused reader slot for PORTSTR 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) +new_reader_slot (void) { 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) + if (!reader_table[i].used && reader == -1) reader = i; } if (reader == -1) @@ -94,7 +141,7 @@ new_reader_slot (int port) return -1; } reader_table[reader].used = 1; - reader_table[reader].port = port; + reader_table[reader].is_ctapi = 0; return reader; } @@ -102,11 +149,25 @@ new_reader_slot (int port) 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].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 (reader_table[reader].status != -1) { log_info ("reader %d: ATR=", reader); @@ -117,13 +178,12 @@ dump_reader_status (int reader) -#ifdef HAVE_CTAPI /* ct API Interface */ static const char * -ct_error_string (int err) +ct_error_string (long err) { switch (err) { @@ -150,7 +210,7 @@ ct_activate_card (int reader) unsigned short buflen; if (count) - sleep (1); /* FIXME: we should use a more reliable timer. */ + ; /* FIXME: we should use a more reliable timer than sleep. */ /* Check whether card has been inserted. */ dad[0] = 1; /* Destination address: CT. */ @@ -221,9 +281,15 @@ open_ct_reader (int port) { int rc, reader; - reader = new_reader_slot (port); + if (port < 0 || port > 0xffff) + { + log_error ("open_ct_reader: invalid port %d requested\n", port); + return -1; + } + reader = new_reader_slot (); if (reader == -1) return reader; + reader_table[reader].port = port; rc = CT_init (reader, (unsigned short)port); if (rc) @@ -241,6 +307,7 @@ open_ct_reader (int port) return -1; } + reader_table[reader].is_ctapi = 1; dump_reader_status (reader); return reader; } @@ -271,16 +338,205 @@ ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen, } -#endif /*HAVE_CTAPI*/ - -#ifdef HAVE_PCSC +static const char * +pcsc_error_string (long err) +{ + const char *s; + + if (!err) + return "okay"; + if ((err & 0x80100000) != 0x80100000) + return "invalid PC/SC error code"; + err &= 0xffff; + switch (err) + { + case 0x0002: s = "cancelled"; break; + case 0x000e: s = "can't dispose"; break; + case 0x0008: s = "insufficient buffer"; break; + case 0x0015: s = "invalid ATR"; break; + case 0x0003: s = "invalid handle"; break; + case 0x0004: s = "invalid parameter"; break; + case 0x0005: s = "invalid target"; break; + case 0x0011: s = "invalid value"; break; + case 0x0006: s = "no memory"; break; + case 0x0013: s = "comm error"; break; + case 0x0001: s = "internal error"; break; + case 0x0014: s = "unknown error"; break; + case 0x0007: s = "waited too long"; break; + case 0x0009: s = "unknown reader"; break; + case 0x000a: s = "timeout"; break; + case 0x000b: s = "sharing violation"; break; + case 0x000c: s = "no smartcard"; break; + case 0x000d: s = "unknown card"; break; + case 0x000f: s = "proto mismatch"; break; + case 0x0010: s = "not ready"; break; + case 0x0012: s = "system cancelled"; break; + case 0x0016: s = "not transacted"; break; + case 0x0017: s = "reader unavailable"; break; + case 0x0065: s = "unsupported card"; break; + case 0x0066: s = "unresponsive card"; break; + case 0x0067: s = "unpowered card"; break; + case 0x0068: s = "reset card"; break; + case 0x0069: s = "removed card"; break; + case 0x006a: s = "inserted card"; break; + case 0x001f: s = "unsupported feature"; break; + case 0x0019: s = "PCI too small"; break; + case 0x001a: s = "reader unsupported"; break; + case 0x001b: s = "duplicate reader"; break; + case 0x001c: s = "card unsupported"; break; + case 0x001d: s = "no service"; break; + case 0x001e: s = "service stopped"; break; + default: s = "unknown PC/SC error code"; break; + } + return s; +} + /* PC/SC Interface */ +static int +open_pcsc_reader (const char *portstr) +{ + long err; + int slot; + char *list = NULL; + unsigned long nreader, listlen, atrlen; + char *p; + unsigned long card_state, card_protocol; + + slot = new_reader_slot (); + if (slot == -1) + return -1; + + err = pcsc_establish_context (PCSC_SCOPE_SYSTEM, NULL, NULL, + &reader_table[slot].pcsc.context); + if (err) + { + log_error ("pcsc_establish_context failed: %s (0x%lx)\n", + pcsc_error_string (err), err); + reader_table[slot].used = 0; + return -1; + } + + err = pcsc_list_readers (reader_table[slot].pcsc.context, + NULL, NULL, &nreader); + if (!err) + { + list = xtrymalloc (nreader+1); /* Better add 1 for safety reasons. */ + if (!list) + { + log_error ("error allocating memory for reader list\n"); + pcsc_release_context (reader_table[slot].pcsc.context); + reader_table[slot].used = 0; + return -1; + } + err = pcsc_list_readers (reader_table[slot].pcsc.context, + NULL, list, &nreader); + } + if (err) + { + log_error ("pcsc_list_readers failed: %s (0x%lx)\n", + pcsc_error_string (err), err); + pcsc_release_context (reader_table[slot].pcsc.context); + reader_table[slot].used = 0; + xfree (list); + return -1; + } + + listlen = nreader; + p = list; + while (nreader) + { + if (!*p && !p[1]) + break; + log_info ("detected reader `%s'\n", p); + if (nreader < (strlen (p)+1)) + { + log_error ("invalid response from pcsc_list_readers\n"); + break; + } + nreader -= strlen (p)+1; + p += strlen (p) + 1; + } + + err = pcsc_connect (reader_table[slot].pcsc.context, + portstr? portstr : list, + PCSC_SHARE_EXCLUSIVE, + PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1, + &reader_table[slot].pcsc.card, + &reader_table[slot].pcsc.protocol); + if (err) + { + log_error ("pcsc_connect failed: %s (0x%lx)\n", + pcsc_error_string (err), err); + pcsc_release_context (reader_table[slot].pcsc.context); + reader_table[slot].used = 0; + xfree (list); + return -1; + } + + atrlen = 32; + /* (We need to pass a dummy buffer. We use LIST because it ought to + be large enough.) */ + err = pcsc_status (reader_table[slot].pcsc.card, + list, &listlen, + &card_state, &card_protocol, + reader_table[slot].atr, &atrlen); + xfree (list); + if (err) + { + log_error ("pcsc_status failed: %s (0x%lx)\n", + pcsc_error_string (err), err); + pcsc_release_context (reader_table[slot].pcsc.context); + reader_table[slot].used = 0; + return -1; + } + if (atrlen >= DIM (reader_table[0].atr)) + log_bug ("ATR returned by pcsc_status is too large\n"); + reader_table[slot].atrlen = atrlen; +/* log_debug ("state from pcsc_status: 0x%lx\n", card_state); */ +/* log_debug ("protocol from pcsc_status: 0x%lx\n", card_protocol); */ + + 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: CT API error code. */ +static int +pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, + unsigned char *buffer, size_t *buflen) +{ + long err; + struct pcsc_io_request_s send_pci; + unsigned long recv_len; + + if (DBG_CARD_IO) + log_printhex (" CT_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; +} + + -#endif /*HAVE_PCSC*/ /* @@ -288,35 +544,86 @@ ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen, */ /* Open the reader and return an internal slot number or -1 on - error. */ + error. If PORTSTR is NULL we default to a suitable port (for ctAPI: + the first USB reader. For PCSC/ the first listed reader. */ int -apdu_open_reader (int port) +apdu_open_reader (const char *portstr) { - static int ct_api_loaded; + static int pcsc_api_loaded, ct_api_loaded; + + if (opt.ctapi_driver && *opt.ctapi_driver) + { + int port = portstr? atoi (portstr) : 32768; + + if (!ct_api_loaded) + { + void *handle; + + handle = dlopen (opt.ctapi_driver, 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 ctAPI driver\n"); + dlclose (handle); + return -1; + } + ct_api_loaded = 1; + } + return open_ct_reader (port); + } - if (!ct_api_loaded) + + /* No ctAPI configured, so lets try the PC/SC API */ + if (!pcsc_api_loaded) { void *handle; - handle = dlopen ("libtowitoko.so", RTLD_LAZY); + handle = dlopen ("libpcsclite.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) + + pcsc_establish_context = dlsym (handle, "SCardEstablishContext"); + pcsc_release_context = dlsym (handle, "SCardReleaseContext"); + pcsc_list_readers = dlsym (handle, "SCardListReaders"); + pcsc_connect = dlsym (handle, "SCardConnect"); + pcsc_disconnect = dlsym (handle, "SCardDisconnect"); + pcsc_status = dlsym (handle, "SCardStatus"); + pcsc_begin_transaction = dlsym (handle, "SCardBeginTransaction"); + pcsc_end_transaction = dlsym (handle, "SCardEndTransaction"); + pcsc_transmit = dlsym (handle, "SCardTransmit"); + pcsc_set_timeout = dlsym (handle, "SCardSetTimeout"); + + if (!pcsc_establish_context + || !pcsc_release_context + || !pcsc_list_readers + || !pcsc_connect + || !pcsc_disconnect + || !pcsc_status + || !pcsc_begin_transaction + || !pcsc_end_transaction + || !pcsc_transmit + || !pcsc_set_timeout) { - log_error ("apdu_open_reader: invalid driver\n"); + log_error ("apdu_open_reader: invalid PC/SC driver\n"); dlclose (handle); return -1; } - ct_api_loaded = 1; + pcsc_api_loaded = 1; } - return open_ct_reader (port); + + return open_pcsc_reader (portstr); } @@ -338,15 +645,14 @@ apdu_get_atr (int slot, size_t *atrlen) static const char * -error_string (int slot, int rc) +error_string (int slot, long rc) { -#ifdef HAVE_CTAPI - return ct_error_string (rc); -#elif defined(HAVE_PCSC) - return "?"; -#else - return "?"; -#endif + if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) + return "[invalid slot]"; + if (reader_table[slot].is_ctapi) + return ct_error_string (rc); + else + return pcsc_error_string (rc); } @@ -355,13 +661,12 @@ static int send_apdu (int slot, unsigned char *apdu, size_t apdulen, unsigned char *buffer, size_t *buflen) { -#ifdef HAVE_CTAPI - return ct_send_apdu (slot, apdu, apdulen, buffer, buflen); -#elif defined(HAVE_PCSC) - return SW_HOST_NO_DRIVER; -#else - return SW_HOST_NO_DRIVER; -#endif + 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); + else + return pcsc_send_apdu (slot, apdu, apdulen, buffer, buflen); } /* Send an APDU to the card in SLOT. The APDU is created from all @@ -382,7 +687,8 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, size_t resultlen = 256; unsigned char apdu[5+256+1]; size_t apdulen; - int rc, sw; + int sw; + long rc; /* we need a long here due to PC/SC. */ if (DBG_CARD_IO) log_debug ("send apdu: c=%02X i=%02X p0=%02X p1=%02X lc=%d le=%d\n", diff --git a/scd/apdu.h b/scd/apdu.h index 44166a3fe..6e4244ba0 100644 --- a/scd/apdu.h +++ b/scd/apdu.h @@ -47,12 +47,13 @@ enum { between errnos on a failed malloc. */ SW_HOST_INV_VALUE = 0x10002, SW_HOST_INCOMPLETE_CARD_RESPONSE = 0x10003, + SW_HOST_NO_DRIVER = 0x10004 }; /* Note , that apdu_open_reader returns no status word but -1 on error. */ -int apdu_open_reader (int port); +int apdu_open_reader (const char *portstr); unsigned char *apdu_get_atr (int slot, size_t *atrlen); diff --git a/scd/app-common.h b/scd/app-common.h index 282f82715..1243ca3ec 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -69,6 +69,7 @@ struct app_ctx_s { }; /*-- app.c --*/ +void app_set_default_reader_port (const char *portstr); APP select_application (void); int app_get_serial_and_stamp (APP app, char **serial, time_t *stamp); int app_write_learn_status (APP app, CTRL ctrl); diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 09a19699d..7f6114292 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -214,71 +214,6 @@ get_one_do (int slot, int tag, unsigned char **result, size_t *nbytes) return NULL; } -#if 0 /* not used */ -static void -dump_one_do (int slot, int tag) -{ - int rc, i; - unsigned char *buffer; - size_t buflen; - const char *desc; - int binary; - const unsigned char *value; - size_t valuelen; - - for (i=0; data_objects[i].tag && data_objects[i].tag != tag; i++) - ; - desc = data_objects[i].tag? data_objects[i].desc : "?"; - binary = data_objects[i].tag? data_objects[i].binary : 1; - - value = NULL; - rc = -1; - if (data_objects[i].tag && data_objects[i].get_from) - { - rc = iso7816_get_data (slot, data_objects[i].get_from, - &buffer, &buflen); - if (!rc) - { - value = find_tlv (buffer, buflen, tag, &valuelen, 0); - if (!value) - ; /* not found */ - else if (valuelen > buflen - (value - buffer)) - { - log_error ("warning: constructed DO too short\n"); - value = NULL; - xfree (buffer); buffer = NULL; - } - } - } - - if (!value) /* Not in a constructed DO, try simple. */ - { - rc = iso7816_get_data (slot, tag, &buffer, &buflen); - if (!rc) - { - value = buffer; - valuelen = buflen; - } - } - if (rc == 0x6a88) - log_info ("DO `%s' not available\n", desc); - else if (rc) - log_info ("DO `%s' not available (rc=%04X)\n", desc, rc); - else - { - if (binary) - { - log_info ("DO `%s': ", desc); - log_printhex ("", value, valuelen); - } - else - log_info ("DO `%s': `%.*s'\n", - desc, (int)valuelen, value); /* FIXME: sanitize */ - xfree (buffer); - } -} -#endif /*not used*/ - static void dump_all_do (int slot) @@ -293,11 +228,11 @@ dump_all_do (int slot) continue; rc = iso7816_get_data (slot, data_objects[i].tag, &buffer, &buflen); - if (rc == 0x6a88) + if (gpg_error (rc) == GPG_ERR_NO_OBJ) ; else if (rc) - log_info ("DO `%s' not available (rc=%04X)\n", - data_objects[i].desc, rc); + log_info ("DO `%s' not available: %s\n", + data_objects[i].desc, gpg_strerror (rc)); else { if (data_objects[i].binary) @@ -309,34 +244,34 @@ dump_all_do (int slot) log_info ("DO `%s': `%.*s'\n", data_objects[i].desc, (int)buflen, buffer); /* FIXME: sanitize */ - } - if (data_objects[i].constructed) - { - for (j=0; data_objects[j].tag; j++) + if (data_objects[i].constructed) { - const unsigned char *value; - size_t valuelen; - - if (j==i || data_objects[i].tag != data_objects[j].get_from) - continue; - value = find_tlv (buffer, buflen, - data_objects[j].tag, &valuelen, 0); - if (!value) - ; /* not found */ - else if (valuelen > buflen - (value - buffer)) - log_error ("warning: constructed DO too short\n"); - else + for (j=0; data_objects[j].tag; j++) { - if (data_objects[j].binary) + const unsigned char *value; + size_t valuelen; + + if (j==i || data_objects[i].tag != data_objects[j].get_from) + continue; + value = find_tlv (buffer, buflen, + data_objects[j].tag, &valuelen, 0); + if (!value) + ; /* not found */ + else if (valuelen > buflen - (value - buffer)) + log_error ("warning: constructed DO too short\n"); + else { - log_info ("DO `%s': ", data_objects[j].desc); - log_printhex ("", value, valuelen); + if (data_objects[j].binary) + { + log_info ("DO `%s': ", data_objects[j].desc); + log_printhex ("", value, valuelen); + } + else + log_info ("DO `%s': `%.*s'\n", + data_objects[j].desc, + (int)valuelen, value); /* FIXME: sanitize */ } - else - log_info ("DO `%s': `%.*s'\n", - data_objects[j].desc, - (int)valuelen, value); /* FIXME: sanitize */ } } } @@ -410,7 +345,7 @@ store_fpr (int slot, int keynumber, u32 timestamp, rc = iso7816_put_data (slot, (card_version > 0x0007? 0xC7 : 0xC6) + keynumber, fpr, 20); if (rc) - log_error ("failed to store the fingerprint: rc=%04X\n", rc); + log_error ("failed to store the fingerprint: %s\n",gpg_strerror (rc)); return rc; } @@ -582,7 +517,7 @@ do_setattr (APP app, const char *name, xfree (pinvalue); if (rc) { - log_error ("verify CHV3 failed\n"); + log_error ("verify CHV3 failed: %s\n", gpg_strerror (rc)); rc = gpg_error (GPG_ERR_GENERAL); return rc; } @@ -626,7 +561,7 @@ do_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode, xfree (pinvalue); if (rc) { - log_error ("verify CHV3 failed: rc=%04X\n", rc); + log_error ("verify CHV3 failed: rc=%s\n", gpg_strerror (rc)); goto leave; } } @@ -642,7 +577,7 @@ do_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode, xfree (pinvalue); if (rc) { - log_error ("verify CHV1 failed: rc=%04X\n", rc); + log_error ("verify CHV1 failed: rc=%s\n", gpg_strerror (rc)); goto leave; } } @@ -658,7 +593,7 @@ do_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode, xfree (pinvalue); if (rc) { - log_error ("verify CHV2 failed: rc=%04X\n", rc); + log_error ("verify CHV2 failed: rc=%s\n", gpg_strerror (rc)); goto leave; } } @@ -757,7 +692,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, } if (rc) { - log_error ("verify CHV3 failed: rc=%04X\n", rc); + log_error ("verify CHV3 failed: rc=%s\n", gpg_strerror (rc)); goto leave; } @@ -1224,8 +1159,6 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen) rc = iso7816_select_application (slot, aid, sizeof aid); if (!rc) { - /* fixme: get the full AID and check that the version is okay - with us. */ rc = iso7816_get_data (slot, 0x004F, &buffer, &buflen); if (rc) goto leave; @@ -1386,7 +1319,7 @@ app_openpgp_storekey (APP app, int keyno, } if (rc) { - log_error ("verify CHV3 failed: rc=%04X\n", rc); + log_error ("verify CHV3 failed: rc=%s\n", gpg_strerror (rc)); goto leave; } @@ -1395,7 +1328,7 @@ app_openpgp_storekey (APP app, int keyno, template, template_len); if (rc) { - log_error ("failed to store the key: rc=%04X\n", rc); + log_error ("failed to store the key: rc=%s\n", gpg_strerror (rc)); rc = gpg_error (GPG_ERR_CARD); goto leave; } diff --git a/scd/app.c b/scd/app.c index 7a85df336..04b421b55 100644 --- a/scd/app.c +++ b/scd/app.c @@ -30,17 +30,26 @@ #include "apdu.h" #include "iso7816.h" +static char *default_reader_port; + +void +app_set_default_reader_port (const char *portstr) +{ + xfree (default_reader_port); + default_reader_port = portstr? xstrdup (portstr): NULL; +} + + /* The select the best fitting application and return a context. Returns NULL if no application was found or no card is present. */ APP select_application (void) { - int reader_port = 32768; /* First USB reader. */ int slot; int rc; APP app; - slot = apdu_open_reader (reader_port); + slot = apdu_open_reader (default_reader_port); if (slot == -1) { log_error ("card reader not available\n"); diff --git a/scd/sc-copykeys.c b/scd/sc-copykeys.c index 9caf39a8a..b56b88590 100644 --- a/scd/sc-copykeys.c +++ b/scd/sc-copykeys.c @@ -44,6 +44,7 @@ enum cmd_and_opt_values { oVerbose = 'v', oReaderPort = 500, + octapiDriver, oDebug, oDebugAll, @@ -55,7 +56,8 @@ static ARGPARSE_OPTS opts[] = { { 301, NULL, 0, "@Options:\n " }, { oVerbose, "verbose", 0, "verbose" }, - { oReaderPort, "reader-port", 1, "|N|connect to reader at port N"}, + { oReaderPort, "reader-port", 2, "|N|connect to reader at port N"}, + { octapiDriver, "ctapi-driver", 2, "NAME|use NAME as ctAPI driver"}, { oDebug, "debug" ,4|16, "set debugging flags"}, { oDebugAll, "debug-all" ,0, "enable full debugging"}, {0} @@ -115,7 +117,7 @@ main (int argc, char **argv ) { ARGPARSE_ARGS pargs; int slot, rc; - int reader_port = 32768; /* First USB reader. */ + const char *reader_port = NULL; struct app_ctx_s appbuf; memset (&appbuf, 0, sizeof appbuf); @@ -146,6 +148,8 @@ main (int argc, char **argv ) case oVerbose: opt.verbose++; break; case oDebug: opt.debug |= pargs.r.ret_ulong; break; case oDebugAll: opt.debug = ~0; break; + case oReaderPort: reader_port = pargs.r.ret_str; break; + case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break; default : pargs.err = 2; break; } } diff --git a/scd/sc-investigate.c b/scd/sc-investigate.c index e8f0eb83c..ecd385690 100644 --- a/scd/sc-investigate.c +++ b/scd/sc-investigate.c @@ -39,6 +39,7 @@ enum cmd_and_opt_values { oVerbose = 'v', oReaderPort = 500, + octapiDriver, oDebug, oDebugAll, @@ -52,7 +53,8 @@ static ARGPARSE_OPTS opts[] = { { 301, NULL, 0, "@Options:\n " }, { oVerbose, "verbose", 0, "verbose" }, - { oReaderPort, "reader-port", 1, "|N|connect to reader at port N"}, + { oReaderPort, "reader-port", 2, "|N|connect to reader at port N"}, + { octapiDriver, "ctapi-driver", 2, "NAME|use NAME as ctAPI driver"}, { oDebug, "debug" ,4|16, "set debugging flags"}, { oDebugAll, "debug-all" ,0, "enable full debugging"}, { oGenRandom, "gen-random", 4, "|N|generate N bytes of random"}, @@ -108,7 +110,7 @@ main (int argc, char **argv ) { ARGPARSE_ARGS pargs; int slot, rc; - int reader_port = 32768; /* First USB reader. */ + const char *reader_port = NULL; struct app_ctx_s appbuf; unsigned long gen_random = 0; @@ -139,6 +141,8 @@ main (int argc, char **argv ) case oVerbose: opt.verbose++; break; case oDebug: opt.debug |= pargs.r.ret_ulong; break; case oDebugAll: opt.debug = ~0; break; + case oReaderPort: reader_port = pargs.r.ret_str; break; + case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break; case oGenRandom: gen_random = pargs.r.ret_ulong; break; default : pargs.err = 2; break; } diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 8e0ef37c9..11952615d 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -43,7 +43,7 @@ #include "i18n.h" #include "sysutils.h" - +#include "app-common.h" enum cmd_and_opt_values @@ -69,6 +69,7 @@ enum cmd_and_opt_values oDaemon, oBatch, oReaderPort, + octapiDriver, aTest }; @@ -91,8 +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")}, - + { oReaderPort, "reader-port", 2, N_("|N|connect to reader at port N")}, + { octapiDriver, "ctapi-driver", 2, N_("NAME|use NAME as ctAPI driver")}, {0} }; @@ -230,7 +231,6 @@ main (int argc, char **argv ) int csh_style = 0; char *logfile = NULL; int debug_wait = 0; - int reader_port = 32768; /* First USB reader. */ set_strusage (my_strusage); gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); @@ -299,6 +299,7 @@ main (int argc, char **argv ) if (default_config) configname = make_filename (opt.homedir, "scdaemon.conf", NULL ); + argc = orig_argc; argv = orig_argv; @@ -365,7 +366,8 @@ 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 oReaderPort: app_set_default_reader_port (pargs.r.ret_str); break; + case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break; default : pargs.err = configfp? 1:2; break; } diff --git a/scd/scdaemon.h b/scd/scdaemon.h index b21e19f8c..bdc4c21b3 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -53,6 +53,7 @@ struct { int dry_run; /* don't change any persistent data */ int batch; /* batch mode */ const char *homedir; /* configuration directory name */ + const char *ctapi_driver; /* Library to access the ctAPI. */ } opt; -- cgit From 0506e4ebec8db4f4b2f5ecdb3122abca8f70b670 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 18 Aug 2003 17:34:28 +0000 Subject: * 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. --- ChangeLog | 6 ++ NEWS | 4 + acinclude.m4 | 56 +++++++++++++ configure.ac | 114 +++++++++++++++++---------- scd/ChangeLog | 17 +++- scd/Makefile.am | 4 +- scd/apdu.c | 237 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- scd/scdaemon.c | 12 +++ scd/scdaemon.h | 1 + 9 files changed, 401 insertions(+), 50 deletions(-) (limited to 'scd/scdaemon.c') 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; -- cgit From e3cdba838618c83e87910150d6f292d06ba65b9b Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 19 Aug 2003 09:36:48 +0000 Subject: * scdaemon.c, scdaemon.h: New option --pcsc-driver. * apdu.c (apdu_open_reader): Use that option here instead of a hardcoded one. --- scd/ChangeLog | 6 ++++++ scd/apdu.c | 6 ++++++ scd/scdaemon.c | 10 +++++++++- scd/scdaemon.h | 1 + 4 files changed, 22 insertions(+), 1 deletion(-) (limited to 'scd/scdaemon.c') diff --git a/scd/ChangeLog b/scd/ChangeLog index a1cb11e0b..821b074a2 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,9 @@ +2003-08-19 Werner Koch + + * scdaemon.c, scdaemon.h: New option --pcsc-driver. + * apdu.c (apdu_open_reader): Use that option here instead of a + hardcoded one. + 2003-08-18 Werner Koch * Makefile.am: Add OPENSC_LIBS to all programs. diff --git a/scd/apdu.c b/scd/apdu.c index 978d9ae3d..0d6ad9a85 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -798,6 +798,12 @@ apdu_open_reader (const char *portstr) { void *handle; + if (!opt.pcsc_driver || !*opt.pcsc_driver) + { + log_error ("no PC/SC driver has been specified\n"); + return -1; + } + handle = dlopen ("libpcsclite.so", RTLD_LAZY); if (!handle) { diff --git a/scd/scdaemon.c b/scd/scdaemon.c index af813b57d..49036e045 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -70,6 +70,7 @@ enum cmd_and_opt_values oBatch, oReaderPort, octapiDriver, + opcscDriver, oDisableOpenSC, aTest }; @@ -94,7 +95,8 @@ static ARGPARSE_OPTS opts[] = { { 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", 2, N_("|N|connect to reader at port N")}, - { octapiDriver, "ctapi-driver", 2, N_("NAME|use NAME as ctAPI driver")}, + { octapiDriver, "ctapi-driver", 2, N_("NAME|use NAME as ct-API driver")}, + { opcscDriver, "pcsc-driver", 2, N_("NAME|use NAME as PC/SC driver")}, { oDisableOpenSC, "disable-opensc", 0, #ifdef HAVE_OPENSC N_("Do not use the OpenSC layer") @@ -266,6 +268,11 @@ main (int argc, char **argv ) may_coredump = disable_core_dumps (); + /* Set default options. */ + opt.pcsc_driver = NULL; /* We can't use libpcsclite due to license + conflicts. */ + + shell = getenv ("SHELL"); if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") ) csh_style = 1; @@ -379,6 +386,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 opcscDriver: opt.pcsc_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 20e2fa768..3671c7394 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. */ + const char *pcsc_driver; /* Library to access the PC/SC system. */ int disable_opensc; /* Disable the sue of the OpenSC framework. */ } opt; -- cgit From 936250aac9d87f464e8e4c5958d54e9ac405c878 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 2 Sep 2003 19:06:34 +0000 Subject: * configure.ac (HAVE_LIBUSB): Added a simple test for libusb. * scdaemon.c, scdaemon.h: New option --pcsc-ccid. * ccid-driver.c, ccid-driver.h: New but far from being useful. * Makefile.am: Add above. * apdu.c: Add support for that ccid driver. --- ChangeLog | 4 + TODO | 3 + agent/pksign.c | 4 + configure.ac | 13 ++ g10/ChangeLog | 4 + g10/keygen.c | 9 + scd/ChangeLog | 7 + scd/Makefile.am | 11 +- scd/apdu.c | 96 ++++++++- scd/ccid-driver.c | 627 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ scd/ccid-driver.h | 40 ++++ scd/scdaemon.c | 13 +- scd/scdaemon.h | 3 +- 13 files changed, 827 insertions(+), 7 deletions(-) create mode 100644 scd/ccid-driver.c create mode 100644 scd/ccid-driver.h (limited to 'scd/scdaemon.c') diff --git a/ChangeLog b/ChangeLog index 45be0a5a0..6ac146eec 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2003-09-02 Werner Koch + + * configure.ac (HAVE_LIBUSB): Added a simple test for libusb. + 2003-08-19 Marcus Brinkmann * configure.ac (AM_PATH_GPG_ERROR): Add missing comma in diff --git a/TODO b/TODO index c9b2d18ff..fe81d0241 100644 --- a/TODO +++ b/TODO @@ -20,6 +20,9 @@ might want to have an agent context for each service request ** When a certificate chain was sucessfully verified, make ephemeral certs used in this chain permanent. ** figure out how to auto retrieve a key by serialno+issuer. Dirmngr is currently not able to parse more than the CN. +** Try all available root certs in case we have several of them in our keybox. + For example TC TrustCenter Class 1 CA certs are ambiguous becuase + user certs don't come with a authorityKeyIdentifier. * sm/decrypt.c ** replace leading zero in integer hack by a cleaner solution diff --git a/agent/pksign.c b/agent/pksign.c index 200b6a2cc..342582177 100644 --- a/agent/pksign.c +++ b/agent/pksign.c @@ -39,6 +39,10 @@ do_encode_md (const byte * md, size_t mdlen, int algo, gcry_sexp_t * r_hash) char * p, tmp[16]; int i, rc; +#warning I do do like that stuff - libgcrypt provides easier interfaces. -wk + /* FIXME: Either use the build function or create canonical encoded + S-expressions. */ + p = xmalloc (64 + 2 * mdlen); s = gcry_md_algo_name (algo); if (s && strlen (s) < 16) diff --git a/configure.ac b/configure.ac index 7a7570eae..591d5df53 100644 --- a/configure.ac +++ b/configure.ac @@ -376,6 +376,19 @@ AM_PATH_LIBASSUAN("$NEED_LIBASSUAN_VERSION", # AM_PATH_KSBA("$NEED_KSBA_VERSION",have_ksba=yes,have_ksba=no) +# +# libusb allows us to use the integrated CCID smartcard reader driver. + +# Note, that we need the CVS version. FIXME: libusb should have a +# regular check as the other libraries do. + +AC_CHECK_LIB(usb, usb_find_device, + [ LIBUSB_LIBS="$LIBUSB_LIBS -lusb" + AC_DEFINE(HAVE_LIBUSB,1, + [defined if libusb is available]) + ]) +AC_SUBST(LIBUSB_LIBS) + # # OpenSC is needed by the SCdaemon - if it is not availbale we can only diff --git a/g10/ChangeLog b/g10/ChangeLog index 8eca012af..274637283 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -3,6 +3,10 @@ * pkglue.c (mpi_from_sexp): New. Used to factor out some common code. +2003-08-24 Werner Koch + + * keygen.c (do_generate_keypair): Print a reminder to use --gen-revoke. + 2003-08-18 Timo Schulz * encode.c (encode_sesskey): Checked the code and removed diff --git a/g10/keygen.c b/g10/keygen.c index 210bbc897..1840a58c7 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -2655,6 +2655,15 @@ do_generate_keypair (struct para_data_s *para, "the command \"--edit-key\" to generate a " "secondary key for this purpose.\n")); } + + if (!opt.batch && card) + { + tty_printf(_( +"Please create a revocation certificate now, so that you are able\n" +"to revoke the key if it ever happens that you lose your card or\n" +"the card gets damaged. Use the command \"--gen-revoke\".\n" + )); + } } } diff --git a/scd/ChangeLog b/scd/ChangeLog index ff407f8ec..28dc4a73a 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,10 @@ +2003-09-02 Werner Koch + + * scdaemon.c, scdaemon.h: New option --pcsc-ccid. + * ccid-driver.c, ccid-driver.h: New but far from being useful. + * Makefile.am: Add above. + * apdu.c: Add support for that ccid driver. + 2003-08-26 Timo Schulz * apdu.c (new_reader_slot): Only set 'is_osc' when OpenSC diff --git a/scd/Makefile.am b/scd/Makefile.am index 5ecadd2e8..a2ecd3a81 100644 --- a/scd/Makefile.am +++ b/scd/Makefile.am @@ -32,17 +32,19 @@ scdaemon_SOURCES = \ card-common.h \ card-p15.c card-dinsig.c \ apdu.c apdu.h \ + ccid-driver.c ccid-driver.h \ iso7816.c iso7816.h \ app.c app-common.h \ app-openpgp.c scdaemon_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \ $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) \ - -lgpg-error @INTLLIBS@ -ldl + $(LIBUSB_LIBS) -lgpg-error @INTLLIBS@ -ldl sc_investigate_SOURCES = \ sc-investigate.c scdaemon.h \ apdu.c apdu.h \ + ccid-driver.c ccid-driver.h \ iso7816.c iso7816.h \ app.c app-common.h \ app-openpgp.c \ @@ -50,12 +52,14 @@ sc_investigate_SOURCES = \ sc_investigate_LDADD = \ ../jnlib/libjnlib.a ../common/libcommon.a \ - $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) @INTLLIBS@ -lgpg-error -ldl + $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(LIBUSB_LIBS) @INTLLIBS@ \ + -lgpg-error -ldl sc_copykeys_SOURCES = \ sc-copykeys.c scdaemon.h \ apdu.c apdu.h \ + ccid-driver.c ccid-driver.h \ iso7816.c iso7816.h \ app.c app-common.h \ app-openpgp.c \ @@ -64,7 +68,8 @@ sc_copykeys_SOURCES = \ sc_copykeys_LDADD = \ ../jnlib/libjnlib.a ../common/libcommon.a \ ../common/libsimple-pwquery.a \ - $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) -lgpg-error @INTLLIBS@ -ldl + $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(LIBUSB_LIBS) \ + -lgpg-error @INTLLIBS@ -ldl diff --git a/scd/apdu.c b/scd/apdu.c index 3809ef3ec..4867f10ff 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -31,6 +31,7 @@ #include "scdaemon.h" #include "apdu.h" #include "dynload.h" +#include "ccid-driver.h" #define MAX_READER 4 /* Number of readers we support concurrently. */ #define CARD_CONNECT_TIMEOUT 1 /* Number of seconds to wait for @@ -42,6 +43,10 @@ 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. */ + struct { + ccid_driver_t handle; + } ccid; int is_ctapi; /* This is a ctAPI driver. */ struct { unsigned long context; @@ -155,6 +160,7 @@ new_reader_slot (void) return -1; } 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; @@ -166,7 +172,9 @@ new_reader_slot (void) static void dump_reader_status (int reader) { - if (reader_table[reader].is_ctapi) + 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" : @@ -549,7 +557,74 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, return err? -1:0; /* FIXME: Return appropriate error code. */ } + +#ifdef HAVE_LIBUSB +/* + Internal CCID driver interface. + */ + +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; +} + + +/* 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: Internal CCID driver error code. */ +static int +send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen, + unsigned char *buffer, size_t *buflen) +{ + long err; + size_t maxbuflen; + + if (DBG_CARD_IO) + log_printhex (" APDU_data:", apdu, apdulen); + + maxbuflen = *buflen; + err = ccid_transceive (reader_table[slot].ccid.handle, + apdu, apdulen, + buffer, maxbuflen, buflen); + if (err) + log_error ("ccid_transceive failed: (0x%lx)\n", + err); + + return err? -1:0; /* FIXME: Return appropriate error code. */ +} + +#endif /* HAVE_LIBUSB */ + + #ifdef HAVE_OPENSC /* OpenSC Interface. @@ -755,6 +830,17 @@ apdu_open_reader (const char *portstr) { static int pcsc_api_loaded, ct_api_loaded; +#ifdef HAVE_LIBUSB + if (!opt.disable_ccid) + { + int slot; + + slot = open_ccid_reader (); + if (slot != -1) + return slot; /* got one */ + } +#endif + #ifdef HAVE_OPENSC if (!opt.disable_opensc) { @@ -871,6 +957,10 @@ error_string (int slot, long rc) 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 "no CCID driver error strings yet"; +#endif #ifdef HAVE_OPENSC else if (reader_table[slot].is_osc) return sc_strerror (rc); @@ -889,6 +979,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_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); diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c new file mode 100644 index 000000000..03c9bcb95 --- /dev/null +++ b/scd/ccid-driver.c @@ -0,0 +1,627 @@ +/* ccid-driver.c - USB ChipCardInterfaceDevices driver + * 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 + */ + + +/* CCID (ChipCardInterfaceDevices) is a specification for accessing + smartcard via a reader connected to the USB. + + This is a limited driver allowing to use some CCID drivers directly + without any other specila drivers. This is a fallback driver to be + used when nothing else works or the system should be kept minimal + for security reasons. It makes use of the libusb library to gain + portable access to USB. + + This driver has been tested with the SCM SCR335 smartcard reader + and requires that reader implements the TPDU level exchange and + does fully automatic initialization. +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#if defined(HAVE_LIBUSB) || defined(TEST) +#include +#include +#include +#include +#include + +#include + + +#include "ccid-driver.h" + +enum { + RDR_to_PC_NotifySlotChange= 0x50, + RDR_to_PC_HardwareError = 0x51, + + PC_to_RDR_SetParameters = 0x61, + PC_to_RDR_IccPowerOn = 0x62, + PC_to_RDR_IccPowerOff = 0x63, + PC_to_RDR_GetSlotStatus = 0x65, + PC_to_RDR_Secure = 0x69, + PC_to_RDR_T0APDU = 0x6a, + PC_to_RDR_Escape = 0x6b, + PC_to_RDR_GetParameters = 0x6c, + PC_to_RDR_ResetParameters = 0x6d, + PC_to_RDR_IccClock = 0x6e, + PC_to_RDR_XfrBlock = 0x6f, + PC_to_RDR_Mechanical = 0x71, + PC_to_RDR_Abort = 0x72, + PC_to_RDR_SetDataRate = 0x73, + + RDR_to_PC_DataBlock = 0x80, + RDR_to_PC_SlotStatus = 0x81, + RDR_to_PC_Parameters = 0x82, + RDR_to_PC_Escape = 0x83, + RDR_to_PC_DataRate = 0x84 +}; + + +/* Store information on the driver's state. A pointer to such a + structure is used as handle for most functions. */ +struct ccid_driver_s { + usb_dev_handle *idev; + int seqno; + unsigned char t1_seqno; +}; + + + + +/* Open the reader with the internal number READERNO and return a a + pointer to be used as handle in HANDLE. Returns 0 on success. */ +int +ccid_open_reader (ccid_driver_t *handle, int readerno) +{ + static int initialized; + + int rc; + usb_match_handle *match = NULL; + struct usb_device *dev = NULL; + usb_dev_handle *idev = NULL; + + *handle = NULL; + if (!initialized) + { + usb_init (); + initialized = 1; + } + + rc = usb_create_match (&match, -1, -1, 11, -1, -1); + if (rc) + { + fprintf (stderr, "ccid-driver: usb_create_match failed: %d\n", rc); + return -1; + } + + while (usb_find_device(match, dev, &dev) >= 0) + { + fprintf(stderr, "ccid-driver: %-40s %04X/%04X\n", dev->filename, + dev->descriptor->idVendor, dev->descriptor->idProduct); + if (!readerno) + { + rc = usb_open (dev, &idev); + if (rc) + { + fprintf (stderr, "ccid-driver: usb_open failed: %d\n", rc); + goto leave; + } + + rc = usb_claim_interface (idev, 0); + if (rc) + { + fprintf (stderr, "ccid-driver: usb_claim_interface failed: %d\n", + rc); + goto leave; + } + + *handle = calloc (1, sizeof **handle); + if (!*handle) + { + fprintf (stderr, "ccid-driver: out of memory\n"); + rc = -1; + goto leave; + } + (*handle)->idev = idev; + idev = NULL; + break; + } + 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 ? */ + usb_free_match (match); + + return rc; +} + + +/* Return False if a card is present and powered. */ +int +ccid_check_card_presence (ccid_driver_t handle) +{ + + return -1; +} + + +static void +set_msg_len (unsigned char *msg, unsigned int length) +{ + msg[1] = length; + msg[2] = length >> 8; + msg[3] = length >> 16; + msg[4] = length >> 24; +} + + +/* Write a MSG of length MSGLEN to the designated bulk out endpoint. + Returns 0 on success. */ +static int +bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen) +{ + int rc; + + rc = usb_bulk_write (handle->idev, + 1, /*endpoint */ + msg, msglen, + 1000 /* ms timeout */); + if (rc == msglen) + return 0; + + if (rc == -1) + fprintf (stderr, "ccid-driver: usb_bulk_write error: %s\n", + strerror (errno)); + else + fprintf (stderr, "ccid-driver: usb_bulk_write failed: %d\n", rc); + return -1; +} + + +/* Read a maximum of LENGTH bytes from the bulk in endpoint into + BUFFER and return the actual read number if bytes in NREAD. + Returns 0 on success. */ +static int +bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, + size_t *nread) +{ + int rc; + + rc = usb_bulk_read (handle->idev, + 0x82, + buffer, length, + 1000 /* ms timeout */ ); + if (rc < 0) + { + fprintf (stderr, "ccid-driver: usb_bulk_read error: %s\n", + strerror (errno)); + return -1; + } + + *nread = rc; + return 0; +} + + +/* experimental */ +int +ccid_poll (ccid_driver_t handle) +{ + int rc; + unsigned char msg[10]; + size_t msglen; + int i, j; + + rc = usb_bulk_read (handle->idev, + 0x83, + msg, sizeof msg, + 0 /* ms timeout */ ); + if (rc < 0 && errno == ETIMEDOUT) + return 0; + + if (rc < 0) + { + fprintf (stderr, "ccid-driver: usb_intr_read error: %s\n", + strerror (errno)); + return -1; + } + + msglen = rc; + rc = 0; + + if (msglen < 1) + { + fprintf (stderr, "ccid-driver: intr-in msg too short\n"); + return -1; + } + + if (msg[0] == RDR_to_PC_NotifySlotChange) + { + fprintf (stderr, "ccid-driver: notify slot change:"); + for (i=1; i < msglen; i++) + for (j=0; j < 4; j++) + fprintf (stderr, " %d:%c%c", + (i-1)*4+j, + (msg[i] & (1<<(j*2)))? 'p':'-', + (msg[i] & (2<<(j*2)))? '*':' '); + putc ('\n', stderr); + } + else if (msg[0] == RDR_to_PC_HardwareError) + { + fprintf (stderr, "ccid-driver: hardware error occured\n"); + } + else + { + fprintf (stderr, "ccid-driver: unknown intr-in msg of type %02X\n", + msg[0]); + } + + return 0; +} + + + +int +ccid_slot_status (ccid_driver_t handle) +{ + int rc; + unsigned char msg[100]; + size_t msglen; + unsigned char seqno; + + msg[0] = PC_to_RDR_GetSlotStatus; + msg[5] = 0; /* slot */ + msg[6] = seqno = handle->seqno++; + msg[7] = 0; /* RFU */ + msg[8] = 0; /* RFU */ + msg[9] = 0; /* RFU */ + set_msg_len (msg, 0); + + rc = bulk_out (handle, msg, 10); + if (rc) + return rc; + rc = bulk_in (handle, msg, sizeof msg, &msglen); + if (rc) + return rc; + if (msglen < 10) + { + fprintf (stderr, "ccid-driver: bulk-in msg too short (%u)\n", + (unsigned int)msglen); + return -1; + } + if (msg[0] != RDR_to_PC_SlotStatus) + { + fprintf (stderr, "ccid-driver: unexpected bulk-in msg type (%02x)\n", + msg[0]); + return -1; + } + if (msg[5] != 0) + { + fprintf (stderr, "ccid-driver: unexpected bulk-in slot (%d)\n", + msg[5]); + return -1; + } + if (msg[6] != seqno) + { + fprintf (stderr, "ccid-driver: bulk-in seqno does not match (%d/%d)\n", + seqno, msg[6]); + return -1; + } + + fprintf (stderr, + "ccid-driver: status: %02X error: %02X clock-status: %02X\n", + msg[7], msg[8], msg[9] ); + + return 0; +} + + +int +ccid_get_atr (ccid_driver_t handle, + unsigned char *atr, size_t maxatrlen, size_t *atrlen) +{ + int rc; + unsigned char msg[100]; + size_t msglen; + unsigned char seqno; + int i; + + msg[0] = PC_to_RDR_IccPowerOn; + msg[5] = 0; /* slot */ + msg[6] = seqno = handle->seqno++; + msg[7] = 0; /* power select (0=auto, 1=5V, 2=3V, 3=1.8V) */ + msg[8] = 0; /* RFU */ + msg[9] = 0; /* RFU */ + set_msg_len (msg, 0); + msglen = 10; + + rc = bulk_out (handle, msg, msglen); + if (rc) + return rc; + rc = bulk_in (handle, msg, sizeof msg, &msglen); + if (rc) + return rc; + if (msglen < 10) + { + fprintf (stderr, "ccid-driver: bulk-in msg too short (%u)\n", + (unsigned int)msglen); + return -1; + } + if (msg[0] != RDR_to_PC_DataBlock) + { + fprintf (stderr, "ccid-driver: unexpected bulk-in msg type (%02x)\n", + msg[0]); + return -1; + } + if (msg[5] != 0) + { + fprintf (stderr, "ccid-driver: unexpected bulk-in slot (%d)\n", + msg[5]); + return -1; + } + if (msg[6] != seqno) + { + fprintf (stderr, "ccid-driver: bulk-in seqno does not match (%d/%d)\n", + seqno, msg[6]); + return -1; + } + + fprintf (stderr, + "ccid-driver: status: %02X error: %02X clock-status: %02X\n" + " data:", msg[7], msg[8], msg[9] ); + for (i=10; i < msglen; i++) + fprintf (stderr, " %02X", msg[i]); + putc ('\n', stderr); + + if (atr) + { + size_t n = msglen - 10; + + if (n > maxatrlen) + n = maxatrlen; + memcpy (atr, msg+10, n); + *atrlen = n; + } + + return 0; +} + + + +/* + Protocol T=1 overview + + Block Structure: + Prologue Field: + 1 byte Node Address (NAD) + 1 byte Protocol Control Byte (PCB) + 1 byte Length (LEN) + Information Field: + 0-254 byte APDU or Control Information (INF) + Epilogue Field: + 1 byte Error Detection Code (EDC) + + NAD: + bit 7 unused + bit 4..6 Destination Node Address (DAD) + bit 3 unused + bit 2..0 Source Node Address (SAD) + + If node adresses are not used, SAD and DAD should be set to 0 on + the first block sent to the card. If they are used they should + have different values (0 for one is okay); that first block sets up + the addresses of the node. + + PCB: + Information Block (I-Block): + bit 7 0 + bit 6 Sequence number (yep, that is modulo 2) + bit 5 Chaining flag + bit 4..0 reserved + Received-Ready Block (R-Block): + bit 7 1 + bit 6 0 + bit 5 0 + bit 4 Sequence number + bit 3..0 0 = no error + 1 = EDC or parity error + 2 = other error + other values are reserved + Supervisory Block (S-Block): + bit 7 1 + bit 6 1 + bit 5 clear=request,set=response + bit 4..0 0 = resyncronisation request + 1 = information field size request + 2 = abort request + 3 = extension of BWT request + 4 = VPP error + other values are reserved + +*/ + +int +ccid_transceive (ccid_driver_t handle, + const unsigned char *apdu, size_t apdulen, + unsigned char *resp, size_t maxresplen, size_t *nresp) +{ + int rc; + unsigned char msg[10+258], *tpdu, *p; + size_t msglen; + unsigned char seqno; + int i; + unsigned char crc; + + + /* Construct an I-Block. */ + if (apdulen > 254) + return -1; /* Invalid length. */ + + tpdu = msg+10; + tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */ + tpdu[1] = ((handle->t1_seqno & 1) << 6); /* I-block */ + tpdu[2] = apdulen; + memcpy (tpdu+3, apdu, apdulen); + crc = 0; + for (i=0,p=tpdu; i < apdulen+3; i++) + crc ^= *p++; + tpdu[3+apdulen] = crc; + + handle->t1_seqno ^= 1; + + msg[0] = PC_to_RDR_XfrBlock; + msg[5] = 0; /* slot */ + msg[6] = seqno = handle->seqno++; + msg[7] = 4; /* bBWI */ + msg[8] = 0; /* RFU */ + msg[9] = 0; /* RFU */ + set_msg_len (msg, apdulen+4); + msglen = 10 + apdulen + 4; + + fprintf (stderr, "ccid-driver: sending"); + for (i=0; i < msglen; i++) + fprintf (stderr, " %02X", msg[i]); + putc ('\n', stderr); + + rc = bulk_out (handle, msg, msglen); + if (rc) + return rc; + rc = bulk_in (handle, msg, sizeof msg, &msglen); + if (rc) + return rc; + if (msglen < 10) + { + fprintf (stderr, "ccid-driver: bulk-in msg too short (%u)\n", + (unsigned int)msglen); + return -1; + } + if (msg[0] != RDR_to_PC_DataBlock) + { + fprintf (stderr, "ccid-driver: unexpected bulk-in msg type (%02x)\n", + msg[0]); + return -1; + } + if (msg[5] != 0) + { + fprintf (stderr, "ccid-driver: unexpected bulk-in slot (%d)\n", + msg[5]); + return -1; + } + if (msg[6] != seqno) + { + fprintf (stderr, "ccid-driver: bulk-in seqno does not match (%d/%d)\n", + seqno, msg[6]); + return -1; + } + + fprintf (stderr, + "ccid-driver: status: %02X error: %02X clock-status: %02X\n" + " data:", msg[7], msg[8], msg[9] ); + for (i=10; i < msglen; i++) + fprintf (stderr, " %02X", msg[i]); + putc ('\n', stderr); + + if (resp) + { + size_t n = msglen - 10; + + if (n < 4) + n = 0; /* fixme: this is an empty I-block or some other block + - we ignore it for now until we have implemented the + T=1 machinery. */ + else + { + p = msg + 10 + 3; /* Skip ccid header and prologue field. */ + n -= 3; + n--; /* Strip the epilogue field. */ + if (n > maxresplen) + n = maxresplen; /* fixme: return an error instead of truncating. */ + memcpy (resp, p, n); + } + *nresp = n; + } + + return 0; +} + + + + +#ifdef TEST +int +main (int argc, char **argv) +{ + int rc; + ccid_driver_t ccid; + + rc = ccid_open_reader (&ccid, 0); + if (rc) + return 1; + + ccid_poll (ccid); + fputs ("getting ATR ...\n", stderr); + rc = ccid_get_atr (ccid, NULL, 0, NULL); + if (rc) + return 1; + + ccid_poll (ccid); + fputs ("getting slot status ...\n", stderr); + rc = ccid_slot_status (ccid); + if (rc) + return 1; + + ccid_poll (ccid); + + { + static unsigned char apdu[] = { + 0, 0xA4, 4, 0, 6, 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01}; + rc = ccid_transceive (ccid, + apdu, sizeof apdu, + NULL, 0, NULL); + } + ccid_poll (ccid); + + { + static unsigned char apdu[] = { + 0, 0xCA, 0, 0x65, 254 }; + rc = ccid_transceive (ccid, + apdu, sizeof apdu, + NULL, 0, NULL); + } + ccid_poll (ccid); + + + return 0; +} + +/* + * Local Variables: + * compile-command: "gcc -DTEST -Wall -I/usr/local/include -lusb -g ccid-driver.c" + * End: + */ +#endif /*TEST*/ +#endif /*HAVE_LIBUSB*/ diff --git a/scd/ccid-driver.h b/scd/ccid-driver.h new file mode 100644 index 000000000..8c2d1fe5a --- /dev/null +++ b/scd/ccid-driver.h @@ -0,0 +1,40 @@ +/* ccid-driver.c - USB ChipCardInterfaceDevices driver + * 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 CCID_DRIVER_H +#define CCID_DRIVER_H + + +struct ccid_driver_s; +typedef struct ccid_driver_s *ccid_driver_t; + +int ccid_open_reader (ccid_driver_t *handle, int readerno); +int ccid_get_atr (ccid_driver_t handle, + unsigned char *atr, size_t maxatrlen, size_t *atrlen); +int ccid_transceive (ccid_driver_t handle, + const unsigned char *apdu, size_t apdulen, + unsigned char *resp, size_t maxresplen, size_t *nresp); + + + +#endif /*CCID_DRIVER_H*/ + + + diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 49036e045..fda0bed6f 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -71,6 +71,7 @@ enum cmd_and_opt_values oReaderPort, octapiDriver, opcscDriver, + oDisableCCID, oDisableOpenSC, aTest }; @@ -97,11 +98,18 @@ static ARGPARSE_OPTS opts[] = { { oReaderPort, "reader-port", 2, N_("|N|connect to reader at port N")}, { octapiDriver, "ctapi-driver", 2, N_("NAME|use NAME as ct-API driver")}, { opcscDriver, "pcsc-driver", 2, N_("NAME|use NAME as PC/SC driver")}, + { oDisableCCID, "disable-ccidc", 0, +#ifdef HAVE_LIBUSB + N_("do not use the internal CCID driver") +#else + "@" +#endif + /* end --disable-ccid */}, { oDisableOpenSC, "disable-opensc", 0, #ifdef HAVE_OPENSC - N_("Do not use the OpenSC layer") + N_("do not use the OpenSC layer") #else - "@" + "@" #endif /* end --disable-opensc */}, @@ -387,6 +395,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 opcscDriver: opt.pcsc_driver = pargs.r.ret_str; break; + case oDisableCCID: opt.disable_ccid = 1; 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 3671c7394..43c3189b3 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -55,7 +55,8 @@ struct { const char *homedir; /* configuration directory name */ const char *ctapi_driver; /* Library to access the ctAPI. */ const char *pcsc_driver; /* Library to access the PC/SC system. */ - int disable_opensc; /* Disable the sue of the OpenSC framework. */ + int disable_opensc; /* Disable the use of the OpenSC framework. */ + int disable_ccid; /* Disable the use of the internal CCID driver. */ } opt; -- cgit From be034cf34c3c5bc7787d9b2f8d4c7b6b948d4e2f Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Sat, 6 Sep 2003 13:23:48 +0000 Subject: * configure.ac: Required newer versions of some libraries. * misc.c (openpgp_pk_algo_usage): Allow AUTH where SIGN is allowed. * keygen.c (ask_passphrase): No need to allocated S2K in secure memory. * scdaemon.c (main): --pcsc-driver again defaults to pcsclite. David Corcoran was so kind to remove the GPL incompatible advertisng clause from pcsclite. * apdu.c (apdu_open_reader): Actually make pcsc-driver option work. --- ChangeLog | 6 ++++++ NEWS | 9 +++++++-- configure.ac | 12 ++++++------ g10/ChangeLog | 7 +++++++ g10/keygen.c | 2 +- g10/misc.c | 6 +++--- scd/ChangeLog | 7 +++++++ scd/apdu.c | 18 ++++++------------ scd/ccid-driver.c | 26 ++++++++++++++------------ scd/scdaemon.c | 3 +-- 10 files changed, 58 insertions(+), 38 deletions(-) (limited to 'scd/scdaemon.c') diff --git a/ChangeLog b/ChangeLog index 6ac146eec..6d11d8ca6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2003-09-06 Werner Koch + + Released 1.9.1. + + * configure.ac: Required newer versions of some libraries. + 2003-09-02 Werner Koch * configure.ac (HAVE_LIBUSB): Added a simple test for libusb. diff --git a/NEWS b/NEWS index 14722fbd9..9dc066a9f 100644 --- a/NEWS +++ b/NEWS @@ -1,10 +1,15 @@ -Noteworthy changes in version 1.9.1 (unreleased) +Noteworthy changes in version 1.9.1 (2003-09-06) ------------------------------------------------ - * Support for OpenSC is back. scdaemon support a --disable-opensc to + * Support for OpenSC is back. scdaemon supports a --disable-opensc to disable OpenSC use at runtime, so that PC/SC or ct-API can still be used directly. + * Rudimentary support for the SCR335 smartcard reader using an + internal driver. Requires current libusb from CVS. + + * Bug fixes. + Noteworthy changes in version 1.9.0 (2003-08-05) ------------------------------------------------ diff --git a/configure.ac b/configure.ac index 591d5df53..4d651d380 100644 --- a/configure.ac +++ b/configure.ac @@ -22,16 +22,16 @@ AC_PREREQ(2.52) # Version number: Remember to change it immediately *after* a release. # Add a "-cvs" prefix for non-released code. -AC_INIT(gnupg, 1.9.1-cvs, gnupg-devel@gnupg.org) +AC_INIT(gnupg, 1.9.1, gnupg-devel@gnupg.org) # Set development_version to yes if the minor number is odd or you # 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_GPG_ERROR_VERSION=0.4 +NEED_LIBGCRYPT_VERSION=1.1.43 NEED_LIBASSUAN_VERSION=0.6.0 NEED_KSBA_VERSION=0.4.6 -NEED_OPENSC_VERSION=0.7.0 +NEED_OPENSC_VERSION=0.8.0 PACKAGE=$PACKAGE_NAME @@ -378,10 +378,10 @@ AM_PATH_KSBA("$NEED_KSBA_VERSION",have_ksba=yes,have_ksba=no) # # libusb allows us to use the integrated CCID smartcard reader driver. - +# # Note, that we need the CVS version. FIXME: libusb should have a # regular check as the other libraries do. - +# AC_CHECK_LIB(usb, usb_find_device, [ LIBUSB_LIBS="$LIBUSB_LIBS -lusb" AC_DEFINE(HAVE_LIBUSB,1, diff --git a/g10/ChangeLog b/g10/ChangeLog index afc16cb94..ae602ebdc 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,10 @@ +2003-09-06 Werner Koch + + * misc.c (openpgp_pk_algo_usage): Allow AUTH where SIGN is allowed. + + * keygen.c (ask_passphrase): No need to allocated S2K in secure + memory. + 2003-09-04 Werner Koch * keygen.c (do_add_key_flags, parse_parameter_usage) diff --git a/g10/keygen.c b/g10/keygen.c index 84857ae62..3dbd9b402 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1629,7 +1629,7 @@ ask_passphrase( STRING2KEY **ret_s2k ) tty_printf(_("You need a Passphrase to protect your secret key.\n\n") ); - s2k = xmalloc_secure ( sizeof *s2k ); + s2k = xmalloc ( sizeof *s2k ); for(;;) { s2k->mode = opt.s2k_mode; s2k->hash_algo = opt.s2k_digest_algo; diff --git a/g10/misc.c b/g10/misc.c index ad04ce76f..4abe75661 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -260,7 +260,7 @@ openpgp_pk_algo_usage ( int algo ) /* they are hardwired in gpg 1.0 */ switch ( algo ) { case PUBKEY_ALGO_RSA: - use = PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC; + use = PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC | PUBKEY_USAGE_AUTH; break; case PUBKEY_ALGO_RSA_E: use = PUBKEY_USAGE_ENC; @@ -272,10 +272,10 @@ openpgp_pk_algo_usage ( int algo ) use = PUBKEY_USAGE_ENC; break; case PUBKEY_ALGO_DSA: - use = PUBKEY_USAGE_SIG; + use = PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH; break; case PUBKEY_ALGO_ELGAMAL: - use = PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC; + use = PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC | PUBKEY_USAGE_AUTH; break; default: break; diff --git a/scd/ChangeLog b/scd/ChangeLog index 5ffad3440..1c785b215 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,10 @@ +2003-09-06 Werner Koch + + * scdaemon.c (main): --pcsc-driver again defaults to pcsclite. + David Corcoran was so kind to remove the GPL incompatible + advertisng clause from pcsclite. + * apdu.c (apdu_open_reader): Actually make pcsc-driver option work. + 2003-09-05 Werner Koch * ccid-driver.c: More work, data can now actually be retrieved. diff --git a/scd/apdu.c b/scd/apdu.c index 4867f10ff..d5f64c6d8 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -823,8 +823,8 @@ osc_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 PC/SC the first listed reader). IF - OpenSC support is cmpiled in, we first try to use OpenSC. */ + the first USB reader. For PC/SC the first listed reader). If + OpenSC support is compiled in, we first try to use OpenSC. */ int apdu_open_reader (const char *portstr) { @@ -839,7 +839,7 @@ apdu_open_reader (const char *portstr) if (slot != -1) return slot; /* got one */ } -#endif +#endif /* HAVE_LIBUSB */ #ifdef HAVE_OPENSC if (!opt.disable_opensc) @@ -886,17 +886,11 @@ apdu_open_reader (const char *portstr) { void *handle; - if (!opt.pcsc_driver || !*opt.pcsc_driver) - { - log_error ("no PC/SC driver has been specified\n"); - return -1; - } - - handle = dlopen ("libpcsclite.so", RTLD_LAZY); + handle = dlopen (opt.pcsc_driver, RTLD_LAZY); if (!handle) { - log_error ("apdu_open_reader: failed to open driver: %s", - dlerror ()); + log_error ("apdu_open_reader: failed to open driver `%s': %s", + opt.pcsc_driver, dlerror ()); return -1; } diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c index b18b055b0..6f469de6a 100644 --- a/scd/ccid-driver.c +++ b/scd/ccid-driver.c @@ -286,7 +286,9 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, rc = usb_bulk_read (handle->idev, 0x82, buffer, length, - 1000 /* ms timeout */ ); + 10000 /* ms timeout */ ); + /* Fixme: instead of using a 10 second timeout we should better + handle the timeout here and retry if appropriate. */ if (rc < 0) { DEBUGOUT_1 ("usb_bulk_read error: %s\n", strerror (errno)); @@ -556,10 +558,10 @@ ccid_transceive (ccid_driver_t handle, DEBUGOUT_CONT_1 (" %02X", msg[i]); DEBUGOUT_LF (); - fprintf (stderr, "T1: put %c-block seq=%d\n", - ((msg[11] & 0xc0) == 0x80)? 'R' : - (msg[11] & 0x80)? 'S' : 'I', - ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40))); +/* fprintf (stderr, "T1: put %c-block seq=%d\n", */ +/* ((msg[11] & 0xc0) == 0x80)? 'R' : */ +/* (msg[11] & 0x80)? 'S' : 'I', */ +/* ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40))); */ rc = bulk_out (handle, msg, msglen); if (rc) @@ -576,16 +578,16 @@ ccid_transceive (ccid_driver_t handle, if (tpdulen < 4) { - DEBUGOUT ("cannot yet handle short block!!\n"); + DEBUGOUT ("cannot yet handle short blocks!\n"); return -1; } - fprintf (stderr, "T1: got %c-block seq=%d err=%d\n", - ((msg[11] & 0xc0) == 0x80)? 'R' : - (msg[11] & 0x80)? 'S' : 'I', - ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)), - ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0 - ); +/* fprintf (stderr, "T1: got %c-block seq=%d err=%d\n", */ +/* ((msg[11] & 0xc0) == 0x80)? 'R' : */ +/* (msg[11] & 0x80)? 'S' : 'I', */ +/* ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)), */ +/* ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0 */ +/* ); */ if (!(tpdu[1] & 0x80)) { /* This is an I-block. */ diff --git a/scd/scdaemon.c b/scd/scdaemon.c index fda0bed6f..89be04ab5 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -277,8 +277,7 @@ main (int argc, char **argv ) may_coredump = disable_core_dumps (); /* Set default options. */ - opt.pcsc_driver = NULL; /* We can't use libpcsclite due to license - conflicts. */ + opt.pcsc_driver = "libpcsclite.so"; shell = getenv ("SHELL"); -- cgit From 7134af9fdb194ae8291775fed78cd1a82eaaa41b Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 1 Dec 2003 10:54:09 +0000 Subject: * scdaemon.c, scdaemon.h: New options --allow-admin and --deny-admin. * app-openpgp.c (verify_chv3): Check it here. --- scd/ChangeLog | 5 +++++ scd/app-openpgp.c | 6 ++++++ scd/scdaemon.c | 9 +++++++-- scd/scdaemon.h | 2 ++ 4 files changed, 20 insertions(+), 2 deletions(-) (limited to 'scd/scdaemon.c') diff --git a/scd/ChangeLog b/scd/ChangeLog index cccd3b669..873691f63 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,8 @@ +2003-11-17 Werner Koch + + * scdaemon.c, scdaemon.h: New options --allow-admin and --deny-admin. + * app-openpgp.c (verify_chv3): Check it here. + 2003-11-12 Werner Koch Adjusted for API changes in Libksba. diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 07abf9bfb..6b4a2f7ef 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -569,6 +569,12 @@ verify_chv3 (APP app, { int rc = 0; + if (!opt.allow_admin) + { + log_info ("access to admin commands is not configured\n"); + return gpg_error (GPG_ERR_EACCES); + } + if (!app->did_chv3) { char *pinvalue; diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 89be04ab5..a3100988b 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -73,6 +73,8 @@ enum cmd_and_opt_values opcscDriver, oDisableCCID, oDisableOpenSC, + oAllowAdmin, + oDenyAdmin, aTest }; @@ -112,8 +114,8 @@ static ARGPARSE_OPTS opts[] = { "@" #endif /* end --disable-opensc */}, - - + { oAllowAdmin, "allow-admin", 0, N_("allow the use of admin card commands")}, + { oDenyAdmin, "deny-admin", 0, "@" }, {0} }; @@ -397,6 +399,9 @@ main (int argc, char **argv ) case oDisableCCID: opt.disable_ccid = 1; break; case oDisableOpenSC: opt.disable_opensc = 1; break; + case oAllowAdmin: opt.allow_admin = 1; break; + case oDenyAdmin: opt.allow_admin = 0; break; + default : pargs.err = configfp? 1:2; break; } } diff --git a/scd/scdaemon.h b/scd/scdaemon.h index 43c3189b3..e13377af7 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -57,6 +57,8 @@ struct { const char *pcsc_driver; /* Library to access the PC/SC system. */ int disable_opensc; /* Disable the use of the OpenSC framework. */ int disable_ccid; /* Disable the use of the internal CCID driver. */ + int allow_admin; /* Allow the use of admin commands for certain + cards. */ } opt; -- cgit From f438224daea48f5658a8703a4d6d6c7060f026ae Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 16 Dec 2003 16:30:55 +0000 Subject: (main): Set the prefixes for assuan logging. --- agent/ChangeLog | 4 ++++ agent/gpg-agent.c | 2 ++ scd/ChangeLog | 4 ++++ scd/scdaemon.c | 4 ++++ 4 files changed, 14 insertions(+) (limited to 'scd/scdaemon.c') diff --git a/agent/ChangeLog b/agent/ChangeLog index c4a37f5e8..0e165e31b 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,7 @@ +2003-12-16 Werner Koch + + * gpg-agent.c (main): Set the prefixes for assuan logging. + 2003-12-15 Werner Koch * protect.c (do_encryption): Use gcry_create_nonce instad of the diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 675f2be3f..47420117e 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -355,6 +355,8 @@ main (int argc, char **argv ) } assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free); + assuan_set_assuan_log_stream (log_get_stream ()); + assuan_set_assuan_log_prefix (log_get_prefix (NULL)); gcry_set_log_handler (my_gcry_logger, NULL); gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); diff --git a/scd/ChangeLog b/scd/ChangeLog index 873691f63..6b9654f58 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,7 @@ +2003-12-16 Werner Koch + + * scdaemon.c (main): Set the prefixes for assuan logging. + 2003-11-17 Werner Koch * scdaemon.c, scdaemon.h: New options --allow-admin and --deny-admin. diff --git a/scd/scdaemon.c b/scd/scdaemon.c index a3100988b..91ac93227 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -272,7 +272,11 @@ main (int argc, char **argv ) } ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free); + assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free); + assuan_set_assuan_log_stream (log_get_stream ()); + assuan_set_assuan_log_prefix (log_get_prefix (NULL)); + gcry_set_log_handler (my_gcry_logger, NULL); gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); -- cgit From eb24d8b751750cf96cb200f80b45ed3806648883 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 27 Jan 2004 16:40:42 +0000 Subject: Some minor bug fixes, new test utilities and started support for other smartcard applications. --- ChangeLog | 4 + README | 4 +- TODO | 1 + agent/ChangeLog | 6 +- agent/Makefile.am | 3 +- agent/sexp-parse.h | 99 ---- common/ChangeLog | 6 + common/Makefile.am | 1 + common/sexp-parse.h | 99 ++++ common/util.h | 3 +- configure.ac | 2 +- scd/ChangeLog | 65 +++ scd/Makefile.am | 24 +- scd/apdu.c | 2 +- scd/apdu.h | 5 +- scd/app-common.h | 70 +-- scd/app-dinsig.c | 129 ++++++ scd/app-nks.c | 388 ++++++++++++++++ scd/app-openpgp.c | 139 ++---- scd/app.c | 131 ++++-- scd/card.c | 6 +- scd/ccid-driver.c | 9 +- scd/command.c | 127 ++++-- scd/iso7816.c | 192 +++++++- scd/iso7816.h | 8 + scd/sc-copykeys.c | 2 +- scd/sc-investigate.c | 480 ++++++++++++++++++-- scd/scdaemon.c | 4 +- scd/scdaemon.h | 3 + scd/tlv.c | 219 +++++++++ scd/tlv.h | 84 ++++ tools/gpgparsemail.c | 705 ++++++++++++++++++++++++++++ tools/rfc822parse.c | 1235 ++++++++++++++++++++++++++++++++++++++++++++++++++ tools/rfc822parse.h | 79 ++++ 34 files changed, 3959 insertions(+), 375 deletions(-) delete mode 100644 agent/sexp-parse.h create mode 100644 common/sexp-parse.h create mode 100644 scd/app-dinsig.c create mode 100644 scd/app-nks.c create mode 100644 scd/tlv.c create mode 100644 scd/tlv.h create mode 100644 tools/gpgparsemail.c create mode 100644 tools/rfc822parse.c create mode 100644 tools/rfc822parse.h (limited to 'scd/scdaemon.c') diff --git a/ChangeLog b/ChangeLog index 0752eec64..84abc4dee 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2004-01-24 Werner Koch + + * configure.ac: Now requires libassuan 0.6.3. + 2003-12-23 Werner Koch Released 1.9.3. diff --git a/README b/README index 42099d6f6..84fc8967b 100644 --- a/README +++ b/README @@ -138,8 +138,8 @@ gpgsm: --with-key-data - Displays extra information with the --list-keys commands. Especiall - a line tagged "grp" si printed which tells you the keygrip of a + Displays extra information with the --list-keys commands. Especially + a line tagged "grp" is printed which tells you the keygrip of a key. This is string is for example used as the filename of the secret key. diff --git a/TODO b/TODO index 12fd998aa..621b278ba 100644 --- a/TODO +++ b/TODO @@ -52,6 +52,7 @@ might want to have an agent context for each service request * agent/protect-tool.c ** Export and import certificates along with the secret key. ** Make it more comfortable; i.e. copy files to the correct place. +** BUG? --p12-export seems to work only with unprotected keys * Move pkcs-1 encoding into libgcrypt. diff --git a/agent/ChangeLog b/agent/ChangeLog index bd009ecbe..57f9214f6 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,7 @@ +2004-01-27 Werner Koch + + * sexp-parse.h: Moved to ../common. + 2004-01-24 Werner Koch * call-scd.c (atfork_cb): New. @@ -437,7 +441,7 @@ * protect.c (agent_get_shadow_info): New. * protect.c (snext,sskip,smatch): Moved to - * sexp-parse.h: new file. + * sexp-parse.h: New file. * divert-scd.c: New. 2002-02-27 Werner Koch diff --git a/agent/Makefile.am b/agent/Makefile.am index 400aa2fd2..65af03368 100644 --- a/agent/Makefile.am +++ b/agent/Makefile.am @@ -41,8 +41,7 @@ gpg_agent_SOURCES = \ trustlist.c \ divert-scd.c \ call-scd.c \ - learncard.c \ - sexp-parse.h + learncard.c gpg_agent_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \ diff --git a/agent/sexp-parse.h b/agent/sexp-parse.h deleted file mode 100644 index 89aa7210f..000000000 --- a/agent/sexp-parse.h +++ /dev/null @@ -1,99 +0,0 @@ -/* sexp-parse.h - S-Exp helper functions - * Copyright (C) 2002, 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 SEXP_PARSE_H -#define SEXP_PARSE_H - -#include - -/* Return the length of the next S-Exp part and update the pointer to - the first data byte. 0 is returned on error */ -static inline size_t -snext (unsigned char const **buf) -{ - const unsigned char *s; - int n; - - s = *buf; - for (n=0; *s && *s != ':' && (*s >= '0' && *s <= '9'); s++) - n = n*10 + (*s - '0'); - if (!n || *s != ':') - return 0; /* we don't allow empty lengths */ - *buf = s+1; - return n; -} - -/* Skip over the S-Expression BUF points to and update BUF to point to - the chacter right behind. DEPTH gives the initial number of open - lists and may be passed as a positive number to skip over the - remainder of an S-Expression if the current position is somewhere - in an S-Expression. The function may return an error code if it - encounters an impossible conditions */ -static inline gpg_error_t -sskip (unsigned char const **buf, int *depth) -{ - const unsigned char *s = *buf; - size_t n; - int d = *depth; - - while (d > 0) - { - if (*s == '(') - { - d++; - s++; - } - else if (*s == ')') - { - d--; - s++; - } - else - { - if (!d) - return gpg_error (GPG_ERR_INV_SEXP); - n = snext (&s); - if (!n) - return gpg_error (GPG_ERR_INV_SEXP); - s += n; - } - } - *buf = s; - *depth = d; - return 0; -} - - -/* Check whether the the string at the address BUF points to matches - the token. Return true on match and update BUF to point behind the - token. Return false and dont update tha buffer if it does not - match. */ -static inline int -smatch (unsigned char const **buf, size_t buflen, const char *token) -{ - size_t toklen = strlen (token); - - if (buflen != toklen || memcmp (*buf, token, toklen)) - return 0; - *buf += toklen; - return 1; -} - -#endif /*SEXP_PARSE_H*/ diff --git a/common/ChangeLog b/common/ChangeLog index 1b454fa13..8e5c615d9 100644 --- a/common/ChangeLog +++ b/common/ChangeLog @@ -1,3 +1,9 @@ +2004-01-27 Werner Koch + + * sexp-parse.h: New; moved from../agent. + + * util.h (xtoi_4): New. + 2003-12-23 Werner Koch * maperror.c (map_assuan_err): Prepared for a new error code. diff --git a/common/Makefile.am b/common/Makefile.am index 79dedca34..770ed12d6 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -29,6 +29,7 @@ AM_CPPFLAGS = $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) libcommon_a_SOURCES = \ util.h i18n.h \ errors.h \ + sexp-parse.h \ maperror.c \ sysutils.c sysutils.h \ gettime.c \ diff --git a/common/sexp-parse.h b/common/sexp-parse.h new file mode 100644 index 000000000..89aa7210f --- /dev/null +++ b/common/sexp-parse.h @@ -0,0 +1,99 @@ +/* sexp-parse.h - S-Exp helper functions + * Copyright (C) 2002, 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 SEXP_PARSE_H +#define SEXP_PARSE_H + +#include + +/* Return the length of the next S-Exp part and update the pointer to + the first data byte. 0 is returned on error */ +static inline size_t +snext (unsigned char const **buf) +{ + const unsigned char *s; + int n; + + s = *buf; + for (n=0; *s && *s != ':' && (*s >= '0' && *s <= '9'); s++) + n = n*10 + (*s - '0'); + if (!n || *s != ':') + return 0; /* we don't allow empty lengths */ + *buf = s+1; + return n; +} + +/* Skip over the S-Expression BUF points to and update BUF to point to + the chacter right behind. DEPTH gives the initial number of open + lists and may be passed as a positive number to skip over the + remainder of an S-Expression if the current position is somewhere + in an S-Expression. The function may return an error code if it + encounters an impossible conditions */ +static inline gpg_error_t +sskip (unsigned char const **buf, int *depth) +{ + const unsigned char *s = *buf; + size_t n; + int d = *depth; + + while (d > 0) + { + if (*s == '(') + { + d++; + s++; + } + else if (*s == ')') + { + d--; + s++; + } + else + { + if (!d) + return gpg_error (GPG_ERR_INV_SEXP); + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + s += n; + } + } + *buf = s; + *depth = d; + return 0; +} + + +/* Check whether the the string at the address BUF points to matches + the token. Return true on match and update BUF to point behind the + token. Return false and dont update tha buffer if it does not + match. */ +static inline int +smatch (unsigned char const **buf, size_t buflen, const char *token) +{ + size_t toklen = strlen (token); + + if (buflen != toklen || memcmp (*buf, token, toklen)) + return 0; + *buf += toklen; + return 1; +} + +#endif /*SEXP_PARSE_H*/ diff --git a/common/util.h b/common/util.h index 80a1d01a6..7e134e846 100644 --- a/common/util.h +++ b/common/util.h @@ -134,13 +134,14 @@ int asprintf (char **result, const char *format, ...) JNLIB_GCC_A_PRINTF(2,3); \v, but works for the purposes used here. */ #define ascii_isspace(a) ((a)==' ' || (a)=='\n' || (a)=='\r' || (a)=='\t') -/* the atoi macros assume that the buffer has only valid digits */ +/* The atoi macros assume that the buffer has only valid digits. */ #define atoi_1(p) (*(p) - '0' ) #define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1)) #define atoi_4(p) ((atoi_2(p) * 100) + atoi_2((p)+2)) #define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) +#define xtoi_4(p) ((xtoi_2(p) * 256) + xtoi_2((p)+2)) diff --git a/configure.ac b/configure.ac index b0c8f2379..cd0486f4b 100644 --- a/configure.ac +++ b/configure.ac @@ -31,7 +31,7 @@ AC_INIT(gnupg, 1.9.4-cvs, gnupg-devel@gnupg.org) development_version=yes NEED_GPG_ERROR_VERSION=0.6 NEED_LIBGCRYPT_VERSION=1.1.91 -NEED_LIBASSUAN_VERSION=0.6.2 +NEED_LIBASSUAN_VERSION=0.6.3 NEED_KSBA_VERSION=0.9.1 NEED_OPENSC_VERSION=0.8.0 diff --git a/scd/ChangeLog b/scd/ChangeLog index 0862d356b..3a6a6aea4 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,68 @@ +2004-01-27 Werner Koch + + * command.c (cmd_readcert, cmd_readkey): Work on a copy of LINE. + + * app-common.h (app_ctx_s): Added readcert field. + * app.c (app_readcert): New. + * tlv.c (parse_ber_header): Added; taken from libksba. + +2004-01-26 Werner Koch + + * card.c (map_sc_err): Use SCD as the error source. + + * command.c (open_card): ADD arg NAME to allow requesting a + specific application. Changed all callers. + (cmd_serialno): Allow optional argument to select the desired + application. + + * app-nks.c: New. + + * scdaemon.h (opt): Add READER_PORT. + * scdaemon.c (main): Set it here. + * app.c (app_set_default_reader_port): Removed. + (select_application): Add NAME arg and figure out a + default serial number from the GDO. Add SLOT arg and remove all + reader management. + (release_application): New. + (app_write_learn_status): Output an APPTYPE status line. + * command.c (open_card): Adapt for select_application change. + * app-openpgp.c (app_select_openpgp): Removed SN and SNLEN args + and set it directly. Changed all callers. + +2004-01-25 Werner Koch + + * iso7816.c (iso7816_select_application): P1 kludge for OpenPGP + card. + * app-openpgp.c (find_tlv): Factor out this function to .. + * tlv.c, tlv.h: .. new. + + * scdaemon.h: Introduced app_t and ctrl_t as the new types for APP + and CTRL. + +2004-01-21 Werner Koch + + * apdu.c (apdu_send_le): Treat SW_EOF_REACHED as a warning. + +2004-01-20 Werner Koch + + * iso7816.c (iso7816_read_binary): New. + (iso7816_select_file): New. + (iso7816_list_directory): New. + + * sc-investigate.c: Add option -i. + (select_app, read_line, interactive_shell): New. + +2004-01-16 Werner Koch + + * apdu.h: Add SW_FILE_NOT_FOUND. + * iso7816.c (map_sw): Map it to GPG_ERR_ENOENT. + * iso7816.c (iso7816_select_file): New. + + * app-dinsig.c: New file w/o any real code yet. + * Makefile.am (scdaemon_SOURCES,sc_investigate_SOURCES): Add file. + + * sc-investigate.c: Add option --disable-ccid. + 2003-12-19 Werner Koch * apdu.c (apdu_send_le): Send a get_response with the indicated diff --git a/scd/Makefile.am b/scd/Makefile.am index a2ecd3a81..c8bf3d0de 100644 --- a/scd/Makefile.am +++ b/scd/Makefile.am @@ -26,6 +26,8 @@ bin_PROGRAMS = scdaemon sc-investigate sc-copykeys AM_CPPFLAGS = -I$(top_srcdir)/common $(OPENSC_CFLAGS) $(LIBGCRYPT_CFLAGS) \ $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) +card_apps = app-openpgp.c app-nks.c app-dinsig.c + scdaemon_SOURCES = \ scdaemon.c scdaemon.h \ command.c card.c \ @@ -34,8 +36,9 @@ scdaemon_SOURCES = \ apdu.c apdu.h \ ccid-driver.c ccid-driver.h \ iso7816.c iso7816.h \ - app.c app-common.h \ - app-openpgp.c + tlv.c tlv.h \ + app.c app-common.h $(card_apps) + scdaemon_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \ $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) \ @@ -46,9 +49,9 @@ sc_investigate_SOURCES = \ apdu.c apdu.h \ ccid-driver.c ccid-driver.h \ iso7816.c iso7816.h \ - app.c app-common.h \ - app-openpgp.c \ - atr.c atr.h + tlv.c tlv.h \ + atr.c atr.h \ + app.c app-common.h $(card_apps) sc_investigate_LDADD = \ ../jnlib/libjnlib.a ../common/libcommon.a \ @@ -61,17 +64,12 @@ sc_copykeys_SOURCES = \ apdu.c apdu.h \ ccid-driver.c ccid-driver.h \ iso7816.c iso7816.h \ - app.c app-common.h \ - app-openpgp.c \ - atr.c atr.h + tlv.c tlv.h \ + atr.c atr.h \ + app.c app-common.h $(card_apps) sc_copykeys_LDADD = \ ../jnlib/libjnlib.a ../common/libcommon.a \ ../common/libsimple-pwquery.a \ $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(LIBUSB_LIBS) \ -lgpg-error @INTLLIBS@ -ldl - - - - - diff --git a/scd/apdu.c b/scd/apdu.c index 02038b65c..e5295f566 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -1168,7 +1168,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, log_printhex (" dump: ", result, resultlen); } - if (sw == SW_SUCCESS) + if (sw == SW_SUCCESS || sw == SW_EOF_REACHED) { if (retbuf) { diff --git a/scd/apdu.h b/scd/apdu.h index 21e2b9840..fd7634f13 100644 --- a/scd/apdu.h +++ b/scd/apdu.h @@ -27,13 +27,16 @@ enum { SW_MORE_DATA = 0x6100, /* Note: that the low byte must be masked of.*/ + SW_EOF_REACHED = 0x6282, SW_EEPROM_FAILURE = 0x6581, SW_WRONG_LENGTH = 0x6700, SW_CHV_WRONG = 0x6982, SW_CHV_BLOCKED = 0x6983, SW_USE_CONDITIONS = 0x6985, - SW_NOT_SUPPORTED = 0x6a81, SW_BAD_PARAMETER = 0x6a80, /* (in the data field) */ + SW_NOT_SUPPORTED = 0x6a81, + SW_FILE_NOT_FOUND = 0x6a82, + SW_RECORD_NOT_FOUND = 0x6a83, SW_REF_NOT_FOUND = 0x6a88, SW_BAD_P0_P1 = 0x6b00, SW_INS_NOT_SUP = 0x6d00, diff --git a/scd/app-common.h b/scd/app-common.h index de1e02cac..cda17700f 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -29,43 +29,46 @@ struct app_ctx_s { int slot; /* Used reader. */ unsigned char *serialno; /* Serialnumber in raw form, allocated. */ size_t serialnolen; /* Length in octets of serialnumber. */ + const char *apptype; unsigned int card_version; int did_chv1; int force_chv1; /* True if the card does not cache CHV1. */ int did_chv2; int did_chv3; struct { - int (*learn_status) (APP app, CTRL ctrl); - int (*getattr) (APP app, CTRL ctrl, const char *name); - int (*setattr) (APP app, const char *name, + int (*learn_status) (app_t app, ctrl_t ctrl); + int (*readcert) (app_t app, const char *certid, + unsigned char **cert, size_t *certlen); + int (*getattr) (app_t app, ctrl_t ctrl, const char *name); + int (*setattr) (app_t app, const char *name, int (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *value, size_t valuelen); - int (*sign) (APP app, + int (*sign) (app_t app, const char *keyidstr, int hashalgo, int (pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ); - int (*auth) (APP app, const char *keyidstr, + int (*auth) (app_t app, const char *keyidstr, int (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen); - int (*decipher) (APP app, const char *keyidstr, + int (*decipher) (app_t app, const char *keyidstr, int (pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen); - int (*genkey) (APP app, CTRL ctrl, + int (*genkey) (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags, int (*pincb)(void*, const char *, char **), void *pincb_arg); - int (*change_pin) (APP app, CTRL ctrl, + int (*change_pin) (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode, int (*pincb)(void*, const char *, char **), void *pincb_arg); - int (*check_pin) (APP app, const char *keyidstr, + int (*check_pin) (app_t app, const char *keyidstr, int (pincb)(void*, const char *, char **), void *pincb_arg); } fnc; @@ -74,66 +77,77 @@ struct app_ctx_s { }; #if GNUPG_MAJOR_VERSION == 1 -int app_select_openpgp (APP app, unsigned char **sn, size_t *snlen); -int app_get_serial_and_stamp (APP app, char **serial, time_t *stamp); +int app_select_openpgp (app_t app); +int app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp); #else /*-- app.c --*/ -void app_set_default_reader_port (const char *portstr); -APP select_application (void); -int app_get_serial_and_stamp (APP app, char **serial, time_t *stamp); -int app_write_learn_status (APP app, CTRL ctrl); -int app_getattr (APP app, CTRL ctrl, const char *name); -int app_setattr (APP app, const char *name, +app_t select_application (ctrl_t ctrl, int slot, const char *name); +void release_application (app_t app); +int app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp); +int app_write_learn_status (app_t app, ctrl_t ctrl); +int app_readcert (app_t app, const char *certid, + unsigned char **cert, size_t *certlen); +int app_getattr (app_t app, ctrl_t ctrl, const char *name); +int app_setattr (app_t app, const char *name, int (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *value, size_t valuelen); -int app_sign (APP app, const char *keyidstr, int hashalgo, +int app_sign (app_t app, const char *keyidstr, int hashalgo, int (pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ); -int app_auth (APP app, const char *keyidstr, +int app_auth (app_t app, const char *keyidstr, int (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen); -int app_decipher (APP app, const char *keyidstr, +int app_decipher (app_t app, const char *keyidstr, int (pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ); -int app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, +int app_genkey (app_t app, ctrl_t ctrl, + const char *keynostr, unsigned int flags, int (*pincb)(void*, const char *, char **), void *pincb_arg); -int app_get_challenge (APP app, size_t nbytes, unsigned char *buffer); -int app_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode, +int app_get_challenge (app_t app, size_t nbytes, unsigned char *buffer); +int app_change_pin (app_t app, ctrl_t ctrl, + const char *chvnostr, int reset_mode, int (*pincb)(void*, const char *, char **), void *pincb_arg); -int app_check_pin (APP app, const char *keyidstr, +int app_check_pin (app_t app, const char *keyidstr, int (*pincb)(void*, const char *, char **), void *pincb_arg); /*-- app-openpgp.c --*/ -int app_select_openpgp (APP app, unsigned char **sn, size_t *snlen); +int app_select_openpgp (app_t app); -int app_openpgp_cardinfo (APP app, +int app_openpgp_cardinfo (app_t app, char **serialno, char **disp_name, char **pubkey_url, unsigned char **fpr1, unsigned char **fpr2, unsigned char **fpr3); -int app_openpgp_storekey (APP app, int keyno, +int app_openpgp_storekey (app_t app, int keyno, unsigned char *template, size_t template_len, time_t created_at, const unsigned char *m, size_t mlen, const unsigned char *e, size_t elen, int (*pincb)(void*, const char *, char **), void *pincb_arg); -int app_openpgp_readkey (APP app, int keyno, +int app_openpgp_readkey (app_t app, int keyno, unsigned char **m, size_t *mlen, unsigned char **e, size_t *elen); +/*-- app-nks.c --*/ +int app_select_nks (app_t app); + +/*-- app-dinsig.c --*/ +int app_select_dinsig (app_t app); + + #endif diff --git a/scd/app-dinsig.c b/scd/app-dinsig.c new file mode 100644 index 000000000..4b5b517eb --- /dev/null +++ b/scd/app-dinsig.c @@ -0,0 +1,129 @@ +/* app-dinsig.c - The DINSIG (DIN V 66291-1) card application. + * Copyright (C) 2004 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 + */ + + +/* The German signature law and its bylaw (SigG and SigV) is currently + used with an interface specification described in DIN V 66291-1. + The AID to be used is: 'D27600006601'. + + The file IDs for certificates utilize the generic format: + Cxyz + C being the hex digit 'C' (12). + x being the service indicator: + '0' := SigG conform digital signature. + '1' := entity authentication. + '2' := key encipherment. + '3' := data encipherment. + '4' := key agreement. + other values are reserved for future use. + y being the security environment number using '0' for cards + not supporting a SE number. + z being the certificate type: + '0' := C.CH (base certificate of card holder) or C.ICC. + '1' .. '7' := C.CH (business or professional certificate + of card holder. + '8' .. 'D' := C.CA (certificate of a CA issue by the Root-CA). + 'E' := C.RCA (self certified certificate of the Root-CA). + 'F' := reserved. + + The file IDs used by default are: + '1F00' EF.SSD (security service descriptor). [o,o] + '2F02' EF.GDO (global data objects) [m,m] + 'A000' EF.PROT (signature log). Cyclic file with 20 records of 53 byte. + Read and update after user authentication. [o,o] + 'B000' EF.PK.RCA.DS (public keys of Root-CA). Size is 512b or size + of keys. [m (unless a 'C00E' is present),m] + 'B001' EF.PK.CA.DS (public keys of CAs). Size is 512b or size + of keys. [o,o] + 'C00n' EF.C.CH.DS (digital signature certificate of card holder) + with n := 0 .. 7. Size is 2k or size of cert. Read and + update allowed after user authentication. [m,m] + 'C00m' EF.C.CA.DS (digital signature certificate of CA) + with m := 8 .. E. Size is 1k or size of cert. Read always + allowed, update after user authentication. [o,o] + 'C100' EF.C.ICC.AUT (AUT certificate of ICC) [o,m] + 'C108' EF.C.CA.AUT (AUT certificate of CA) [o,m] + 'D000' EF.DM (display message) [-,m] + + The letters in brackets indicate optional or mandatory files: The + first for card terminals under full control and the second for + "business" card terminals. + + FIXME: Needs a lot more explanation. + +*/ + + + + +#include +#include +#include +#include +#include +#include +#include + +#include "scdaemon.h" + +#include "iso7816.h" +#include "app-common.h" + + + +static int +do_learn_status (APP app, CTRL ctrl) +{ + return 0; +} + + + + + +/* Select the DINSIG application on the card in SLOT. This function + must be used before any other DINSIG application functions. */ +int +app_select_dinsig (APP app) +{ + static char const aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x66, 0x01 }; + int slot = app->slot; + int rc; + + rc = iso7816_select_application (slot, aid, sizeof aid); + if (!rc) + { + app->apptype = "DINSIG"; + + app->fnc.learn_status = do_learn_status; + app->fnc.getattr = NULL; + app->fnc.setattr = NULL; + app->fnc.genkey = NULL; + app->fnc.sign = NULL; + app->fnc.auth = NULL; + app->fnc.decipher = NULL; + app->fnc.change_pin = NULL; + app->fnc.check_pin = NULL; + } + + return rc; +} + + diff --git a/scd/app-nks.c b/scd/app-nks.c new file mode 100644 index 000000000..0a04f7511 --- /dev/null +++ b/scd/app-nks.c @@ -0,0 +1,388 @@ +/* app-nks.c - The Telesec NKS 2.0 card application. + * Copyright (C) 2004 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 + +#include "scdaemon.h" + +#include "iso7816.h" +#include "app-common.h" +#include "tlv.h" + +static struct { + int fid; /* File ID. */ + int certtype; /* Type of certificate or 0 if it is not a certificate. */ + int iskeypair; /* If true has the FID of the correspoding certificate. */ +} filelist[] = { + { 0x4531, 0, 0xC000 }, + { 0xC000, 101 }, + { 0x4331, 100 }, + { 0x4332, 100 }, + { 0xB000, 110 }, + { 0x45B1, 0, 0xC200 }, + { 0xC200, 101 }, + { 0x43B1, 100 }, + { 0x43B2, 100 }, + { 0, 0 } +}; + + + +/* Given the slot and the File Id FID, return the length of the + certificate contained in that file. Returns 0 if the file does not + exists or does not contain a certificate. */ +static size_t +get_length_of_cert (int slot, int fid) +{ + gpg_error_t err; + unsigned char *buffer; + const unsigned char *p; + size_t buflen, n; + int class, tag, constructed, ndef; + size_t objlen, hdrlen; + + err = iso7816_select_file (slot, fid, 0, NULL, NULL); + if (err) + { + log_info ("error selecting FID 0x%04X: %s\n", fid, gpg_strerror (err)); + return 0; + } + + err = iso7816_read_binary (slot, 0, 32, &buffer, &buflen); + if (err) + { + log_info ("error reading certificate from FID 0x%04X: %s\n", + fid, gpg_strerror (err)); + return 0; + } + + if (!buflen || *buffer == 0xff) + { + log_info ("no certificate contained in FID 0x%04X\n", fid); + xfree (buffer); + return 0; + } + + p = buffer; + n = buflen; + err = parse_ber_header (&p, &n, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (err) + { + log_info ("error parsing certificate in FID 0x%04X: %s\n", + fid, gpg_strerror (err)); + xfree (buffer); + return 0; + } + + /* All certificates should commence with a SEQUENCE expect fro the + special ROOT CA which are enclosed in a SET. */ + if ( !(class == CLASS_UNIVERSAL && constructed + && (tag == TAG_SEQUENCE || tag == TAG_SET))) + { + log_info ("contents of FID 0x%04X does not look like a certificate\n", + fid); + return 0; + } + + return objlen + hdrlen; +} + + + +/* Read the file with FID, assume it contains a public key and return + its keygrip in the caller provided 41 byte buffer R_GRIPSTR. */ +static gpg_error_t +keygripstr_from_pk_file (int slot, int fid, char *r_gripstr) +{ + gpg_error_t err; + unsigned char grip[20]; + unsigned char *buffer[2]; + size_t buflen[2]; + gcry_sexp_t sexp; + int i; + + err = iso7816_select_file (slot, fid, 0, NULL, NULL); + if (err) + return err; + err = iso7816_read_record (slot, 1, 1, &buffer[0], &buflen[0]); + if (err) + return err; + err = iso7816_read_record (slot, 2, 1, &buffer[1], &buflen[1]); + if (err) + { + xfree (buffer[0]); + return err; + } + + for (i=0; i < 2; i++) + { + /* Check that the value appears like an integer encoded as + Simple-TLV. We don't check the tag because the tests cards I + have use 1 for both, the modulus and the exponent - the + example in the documentation gives 2 for the exponent. */ + if (buflen[i] < 3) + err = gpg_error (GPG_ERR_TOO_SHORT); + else if (buffer[i][1] != buflen[i]-2 ) + err = gpg_error (GPG_ERR_INV_OBJ); + } + + if (!err) + err = gcry_sexp_build (&sexp, NULL, + "(public-key (rsa (n %b) (e %b)))", + (int)buflen[0]-2, buffer[0]+2, + (int)buflen[1]-2, buffer[1]+2); + + xfree (buffer[0]); + xfree (buffer[1]); + if (err) + return err; + + if (!gcry_pk_get_keygrip (sexp, grip)) + { + err = gpg_error (GPG_ERR_INTERNAL); /* i.e. RSA not supported by + libgcrypt. */ + } + else + { + for (i=0; i < 20; i++) + sprintf (r_gripstr+i*2, "%02X", grip[i]); + } + gcry_sexp_release (sexp); + return err; +} + + + +static int +do_learn_status (APP app, CTRL ctrl) +{ + gpg_error_t err; + char ct_buf[100], id_buf[100]; + int i; + + /* Output information about all useful objects. */ + for (i=0; filelist[i].fid; i++) + { + if (filelist[i].certtype) + { + size_t len = get_length_of_cert (app->slot, filelist[i].fid); + + if (len) + { + /* FIXME: We should store the length in the application's + context so that a following readcert does only need to + read that many bytes. */ + sprintf (ct_buf, "%d", filelist[i].certtype); + sprintf (id_buf, "NKS-DF01.%04X", filelist[i].fid); + send_status_info (ctrl, "CERTINFO", + ct_buf, strlen (ct_buf), + id_buf, strlen (id_buf), + NULL, (size_t)0); + } + } + else if (filelist[i].iskeypair) + { + char gripstr[40+1]; + + err = keygripstr_from_pk_file (app->slot, filelist[i].fid, gripstr); + if (err) + log_error ("can't get keygrip from FID 0x%04X: %s\n", + filelist[i].fid, gpg_strerror (err)); + else + { + sprintf (id_buf, "NKS-DF01.%04X", filelist[i].fid); + send_status_info (ctrl, "KEYPAIRINFO", + gripstr, 40, + id_buf, strlen (id_buf), + NULL, (size_t)0); + } + } + } + + return 0; +} + + + + +/* Read the certificate with id CERTID (as returned by learn_status in + the CERTINFO status lines) and return it in the freshly allocated + buffer put into CERT and the length of the certificate put into + CERTLEN. */ +static int +do_readcert (app_t app, const char *certid, + unsigned char **cert, size_t *certlen) +{ + int i, fid; + gpg_error_t err; + unsigned char *buffer; + const unsigned char *p; + size_t buflen, n; + int class, tag, constructed, ndef; + size_t totobjlen, objlen, hdrlen; + int rootca = 0; + + *cert = NULL; + *certlen = 0; + if (strncmp (certid, "NKS-DF01.", 9) ) + return gpg_error (GPG_ERR_INV_ID); + certid += 9; + if (!hexdigitp (certid) || !hexdigitp (certid+1) + || !hexdigitp (certid+2) || !hexdigitp (certid+3) + || certid[4]) + return gpg_error (GPG_ERR_INV_ID); + fid = xtoi_4 (certid); + for (i=0; filelist[i].fid; i++) + if ((filelist[i].certtype || filelist[i].iskeypair) + && filelist[i].fid == fid) + break; + if (!filelist[i].fid) + return gpg_error (GPG_ERR_NOT_FOUND); + + /* If the requested objects is a plain public key, redirect it to + the corresponding certificate. The whole system is a bit messy + becuase we sometime use the key directly or let the caller + retrieve the key from the certificate. The valid point behind + that is to support not-yet stored certificates. */ + if (filelist[i].iskeypair) + fid = filelist[i].iskeypair; + + + /* Read the entire file. fixme: This could be optimized by first + reading the header to figure out how long the certificate + actually is. */ + err = iso7816_select_file (app->slot, fid, 0, NULL, NULL); + if (err) + { + log_error ("error selecting FID 0x%04X: %s\n", fid, gpg_strerror (err)); + return err; + } + + err = iso7816_read_binary (app->slot, 0, 0, &buffer, &buflen); + if (err) + { + log_error ("error reading certificate from FID 0x%04X: %s\n", + fid, gpg_strerror (err)); + return err; + } + + if (!buflen || *buffer == 0xff) + { + log_info ("no certificate contained in FID 0x%04X\n", fid); + err = gpg_error (GPG_ERR_NOT_FOUND); + goto leave; + } + + /* Now figure something out about the object. */ + p = buffer; + n = buflen; + err = parse_ber_header (&p, &n, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (err) + goto leave; + if ( class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed ) + ; + else if ( class == CLASS_UNIVERSAL && tag == TAG_SET && constructed ) + rootca = 1; + else + return gpg_error (GPG_ERR_INV_OBJ); + totobjlen = objlen + hdrlen; + assert (totobjlen <= buflen); + + err = parse_ber_header (&p, &n, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (err) + goto leave; + + if (rootca) + ; + else if (class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !constructed) + { + const unsigned char *save_p; + + /* The certificate seems to be contained in a userCertificate + container. Skip this and assume the following sequence is + the certificate. */ + if (n < objlen) + { + err = gpg_error (GPG_ERR_INV_OBJ); + goto leave; + } + p += objlen; + n -= objlen; + save_p = p; + err = parse_ber_header (&p, &n, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (err) + goto leave; + if ( !(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed) ) + return gpg_error (GPG_ERR_INV_OBJ); + totobjlen = objlen + hdrlen; + assert (save_p + totobjlen <= buffer + buflen); + memmove (buffer, save_p, totobjlen); + } + + *cert = buffer; + buffer = NULL; + *certlen = totobjlen; + + leave: + xfree (buffer); + return err; +} + + + +/* Select the NKS 2.0 application on the card in SLOT. */ +int +app_select_nks (APP app) +{ + static char const aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x03, 0x01, 0x02 }; + int slot = app->slot; + int rc; + + rc = iso7816_select_application (slot, aid, sizeof aid); + if (!rc) + { + app->apptype = "NKS"; + + app->fnc.learn_status = do_learn_status; + app->fnc.readcert = do_readcert; + app->fnc.getattr = NULL; + app->fnc.setattr = NULL; + app->fnc.genkey = NULL; + app->fnc.sign = NULL; + app->fnc.auth = NULL; + app->fnc.decipher = NULL; + app->fnc.change_pin = NULL; + app->fnc.check_pin = NULL; + } + + return rc; +} + + diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 8f9a303fe..75e3e299e 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -1,5 +1,5 @@ /* app-openpgp.c - The OpenPGP card application. - * Copyright (C) 2003 Free Software Foundation, Inc. + * Copyright (C) 2003, 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -42,7 +42,7 @@ #include "iso7816.h" #include "app-common.h" - +#include "tlv.h" static struct { @@ -80,94 +80,6 @@ static unsigned long convert_sig_counter_value (const unsigned char *value, static unsigned long get_sig_counter (APP app); -/* Locate a TLV encoded data object in BUFFER of LENGTH and - return a pointer to value as well as its length in NBYTES. Return - NULL if it was not found. Note, that the function does not check - whether the value fits into the provided buffer. - - FIXME: Move this to an extra file, it is mostly duplicated from card.c. -*/ -static const unsigned char * -find_tlv (const unsigned char *buffer, size_t length, - int tag, size_t *nbytes, int nestlevel) -{ - const unsigned char *s = buffer; - size_t n = length; - size_t len; - int this_tag; - int composite; - - for (;;) - { - buffer = s; - if (n < 2) - return NULL; /* buffer definitely too short for tag and length. */ - if (!*s || *s == 0xff) - { /* Skip optional filler between TLV objects. */ - s++; - n--; - continue; - } - composite = !!(*s & 0x20); - if ((*s & 0x1f) == 0x1f) - { /* more tag bytes to follow */ - s++; - n--; - if (n < 2) - return NULL; /* buffer definitely too short for tag and length. */ - if ((*s & 0x1f) == 0x1f) - return NULL; /* We support only up to 2 bytes. */ - this_tag = (s[-1] << 8) | (s[0] & 0x7f); - } - else - this_tag = s[0]; - len = s[1]; - s += 2; n -= 2; - if (len < 0x80) - ; - else if (len == 0x81) - { /* One byte length follows. */ - if (!n) - return NULL; /* we expected 1 more bytes with the length. */ - len = s[0]; - s++; n--; - } - else if (len == 0x82) - { /* Two byte length follows. */ - if (n < 2) - return NULL; /* we expected 2 more bytes with the length. */ - len = (s[0] << 8) | s[1]; - s += 2; n -= 2; - } - else - return NULL; /* APDU limit is 65535, thus it does not make - sense to assume longer length fields. */ - - if (composite && nestlevel < 100) - { /* Dive into this composite DO after checking for too deep - nesting. */ - const unsigned char *tmp_s; - size_t tmp_len; - - tmp_s = find_tlv (s, len, tag, &tmp_len, nestlevel+1); - if (tmp_s) - { - *nbytes = tmp_len; - return tmp_s; - } - } - - if (this_tag == tag) - { - *nbytes = len; - return s; - } - if (len > n) - return NULL; /* buffer too short to skip to the next tag. */ - s += len; n -= len; - } -} - /* Get the DO identified by TAG from the card in SLOT and return a buffer with its content in RESULT and NBYTES. The return value is @@ -197,7 +109,7 @@ get_one_do (int slot, int tag, unsigned char **result, size_t *nbytes) { const unsigned char *s; - s = find_tlv (buffer, buflen, tag, &valuelen, 0); + s = find_tlv (buffer, buflen, tag, &valuelen); if (!s) value = NULL; /* not found */ else if (valuelen > buflen - (s - buffer)) @@ -271,7 +183,7 @@ dump_all_do (int slot) if (j==i || data_objects[i].tag != data_objects[j].get_from) continue; value = find_tlv (buffer, buflen, - data_objects[j].tag, &valuelen, 0); + data_objects[j].tag, &valuelen); if (!value) ; /* not found */ else if (valuelen > buflen - (value - buffer)) @@ -443,7 +355,7 @@ do_getattr (APP app, CTRL ctrl, const char *name) { /* The serial number is very special. We could have used the AID DO to retrieve it, but we have it already in the app - context and the stanmp argument is required anyway which we + context and the stamp argument is required anyway which we can't by other means. The AID DO is available anyway but not hex formatted. */ char *serial; @@ -772,7 +684,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, log_error ("error reading application data\n"); return gpg_error (GPG_ERR_GENERAL); } - fpr = find_tlv (buffer, buflen, 0x00C5, &n, 0); + fpr = find_tlv (buffer, buflen, 0x00C5, &n); if (!fpr || n != 60) { rc = gpg_error (GPG_ERR_GENERAL); @@ -820,7 +732,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, } log_info ("key generation completed (%d seconds)\n", (int)(time (NULL) - start_at)); - keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen, 0); + keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen); if (!keydata) { rc = gpg_error (GPG_ERR_CARD); @@ -828,7 +740,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, goto leave; } - m = find_tlv (keydata, keydatalen, 0x0081, &mlen, 0); + m = find_tlv (keydata, keydatalen, 0x0081, &mlen); if (!m) { rc = gpg_error (GPG_ERR_CARD); @@ -838,7 +750,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, /* log_printhex ("RSA n:", m, mlen); */ send_key_data (ctrl, "n", m, mlen); - e = find_tlv (keydata, keydatalen, 0x0082, &elen, 0); + e = find_tlv (keydata, keydatalen, 0x0082, &elen); if (!e) { rc = gpg_error (GPG_ERR_CARD); @@ -913,7 +825,7 @@ compare_fingerprint (APP app, int keyno, unsigned char *sha1fpr) log_error ("error reading application data\n"); return gpg_error (GPG_ERR_GENERAL); } - fpr = find_tlv (buffer, buflen, 0x00C5, &n, 0); + fpr = find_tlv (buffer, buflen, 0x00C5, &n); if (!fpr || n != 60) { xfree (buffer); @@ -1268,7 +1180,7 @@ do_check_pin (APP app, const char *keyidstr, /* Select the OpenPGP application on the card in SLOT. This function must be used before any other OpenPGP application functions. */ int -app_select_openpgp (APP app, unsigned char **sn, size_t *snlen) +app_select_openpgp (APP app) { static char const aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 }; int slot = app->slot; @@ -1280,10 +1192,17 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen) rc = iso7816_select_application (slot, aid, sizeof aid); if (!rc) { + app->apptype = "OPENPGP"; + app->did_chv1 = 0; app->did_chv2 = 0; app->did_chv3 = 0; + /* The OpenPGP card returns the serial number as part of the + AID; because we prefer to use OpenPGP serial numbers, we + repalce a possibly already set one from a EF.GDO with this + one. Note, that for current OpenPGP cards, no EF.GDO exists + and thus it won't matter at all. */ rc = iso7816_get_data (slot, 0x004F, &buffer, &buflen); if (rc) goto leave; @@ -1293,15 +1212,12 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen) log_printhex ("", buffer, buflen); } - if (sn) - { - *sn = buffer; - *snlen = buflen; - app->card_version = buffer[6] << 8; - app->card_version |= buffer[7]; - } - else - xfree (buffer); + app->card_version = buffer[6] << 8; + app->card_version |= buffer[7]; + xfree (app->serialno); + app->serialno = buffer; + app->serialnolen = buflen; + buffer = NULL; relptr = get_one_do (app->slot, 0x00C4, &buffer, &buflen); if (!relptr) @@ -1316,6 +1232,7 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen) dump_all_do (slot); app->fnc.learn_status = do_learn_status; + app->fnc.readcert = NULL; app->fnc.getattr = do_getattr; app->fnc.setattr = do_setattr; app->fnc.genkey = do_genkey; @@ -1498,7 +1415,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen, goto leave; } - keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen, 0); + keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen); if (!keydata) { log_error ("response does not contain the public key data\n"); @@ -1506,7 +1423,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen, goto leave; } - a = find_tlv (keydata, keydatalen, 0x0081, &alen, 0); + a = find_tlv (keydata, keydatalen, 0x0081, &alen); if (!a) { log_error ("response does not contain the RSA modulus\n"); @@ -1517,7 +1434,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen, *m = xmalloc (alen); memcpy (*m, a, alen); - a = find_tlv (keydata, keydatalen, 0x0082, &alen, 0); + a = find_tlv (keydata, keydatalen, 0x0082, &alen); if (!e) { log_error ("response does not contain the RSA public exponent\n"); diff --git a/scd/app.c b/scd/app.c index 1f142ea32..6ac18272b 100644 --- a/scd/app.c +++ b/scd/app.c @@ -29,49 +29,78 @@ #include "app-common.h" #include "apdu.h" #include "iso7816.h" -#include "dynload.h" +#include "tlv.h" -static char *default_reader_port; -void -app_set_default_reader_port (const char *portstr) -{ - xfree (default_reader_port); - default_reader_port = portstr? xstrdup (portstr): NULL; -} - - -/* The select the best fitting application and return a context. - Returns NULL if no application was found or no card is present. */ +/* If called with NAME as NULL, select the best fitting application + and return a context; otherwise select the application with NAME + and return a context. SLOT identifies the reader device. Returns + NULL if no application was found or no card is present. */ APP -select_application (void) +select_application (ctrl_t ctrl, int slot, const char *name) { - int slot; int rc; APP app; - - slot = apdu_open_reader (default_reader_port); - if (slot == -1) - { - log_error ("card reader not available\n"); - return NULL; - } + unsigned char *result = NULL; + size_t resultlen; app = xtrycalloc (1, sizeof *app); if (!app) { rc = out_of_core (); log_info ("error allocating context: %s\n", gpg_strerror (rc)); - /*apdu_close_reader (slot);*/ return NULL; } - app->slot = slot; - rc = app_select_openpgp (app, &app->serialno, &app->serialnolen); + + /* Fixme: We should now first check whether a card is at all + present. */ + + /* Try to read the GDO file first to get a default serial number. */ + rc = iso7816_select_file (slot, 0x3F00, 1, NULL, NULL); + if (!rc) + rc = iso7816_select_file (slot, 0x2F02, 0, NULL, NULL); + if (!rc) + rc = iso7816_read_binary (slot, 0, 0, &result, &resultlen); + if (!rc) + { + size_t n; + const unsigned char *p; + + p = find_tlv (result, resultlen, 0x5A, &n); + if (p && n && n >= (resultlen - (p - result))) + { + /* The GDO file is pretty short, thus we simply reuse it for + storing the serial number. */ + memmove (result, p, n); + app->serialno = result; + app->serialnolen = n; + } + else + xfree (result); + result = NULL; + } + + + rc = gpg_error (GPG_ERR_NOT_FOUND); + + if (!name || !strcmp (name, "openpgp")) + rc = app_select_openpgp (app); + if (rc && (!name || !strcmp (name, "nks"))) + rc = app_select_nks (app); + if (rc && (!name || !strcmp (name, "dinsig"))) + rc = app_select_dinsig (app); + if (rc && name) + rc = gpg_error (GPG_ERR_NOT_SUPPORTED); + if (rc) { -/* apdu_close_reader (slot); */ - log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc)); + if (name) + log_info ("can't select application `%s': %s\n", + name, gpg_strerror (rc)); + else + log_info ("no supported card application found: %s\n", + gpg_strerror (rc)); xfree (app); return NULL; } @@ -81,23 +110,36 @@ select_application (void) } +void +release_application (app_t app) +{ + if (!app) + return; + + xfree (app->serialno); + xfree (app); +} + + /* Retrieve the serial number and the time of the last update of the card. The serial number is returned as a malloced string (hex encoded) in SERIAL and the time of update is returned in STAMP. If no update time is available the returned value is 0. Caller must - free SERIAL unless the function returns an error. */ + free SERIAL unless the function returns an error. If STAMP is not + of interest, NULL may be passed. */ int -app_get_serial_and_stamp (APP app, char **serial, time_t *stamp) +app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp) { unsigned char *buf, *p; int i; - if (!app || !serial || !stamp) + if (!app || !serial) return gpg_error (GPG_ERR_INV_VALUE); *serial = NULL; - *stamp = 0; /* not available */ + if (stamp) + *stamp = 0; /* not available */ buf = xtrymalloc (app->serialnolen * 2 + 1); if (!buf) @@ -121,10 +163,34 @@ app_write_learn_status (APP app, CTRL ctrl) return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.learn_status) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + + if (app->apptype) + send_status_info (ctrl, "APPTYPE", + app->apptype, strlen (app->apptype), NULL, 0); + return app->fnc.learn_status (app, ctrl); } +/* Read the certificate with id CERTID (as returned by learn_status in + the CERTINFO status lines) and return it in the freshly allocated + buffer put into CERT and the length of the certificate put into + CERTLEN. */ +int +app_readcert (app_t app, const char *certid, + unsigned char **cert, size_t *certlen) +{ + if (!app) + return gpg_error (GPG_ERR_INV_VALUE); + if (!app->initialized) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (!app->fnc.readcert) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + + return app->fnc.readcert (app, certid, cert, certlen); +} + + /* Perform a GETATTR operation. */ int app_getattr (APP app, CTRL ctrl, const char *name) @@ -317,8 +383,3 @@ app_check_pin (APP app, const char *keyidstr, return rc; } - - - - - diff --git a/scd/card.c b/scd/card.c index 95149074d..53c89f3a4 100644 --- a/scd/card.c +++ b/scd/card.c @@ -53,7 +53,10 @@ map_sc_err (int rc) #endif default: e = GPG_ERR_CARD; break; } - return gpg_err_make (GPG_ERR_SOURCE_UNKNOWN, e); + /* It does not make much sense to further distingusih the error + source between OpenSC and SCD. Thus we use SCD as source + here. */ + return gpg_err_make (GPG_ERR_SOURCE_SCD, e); } /* Get the keygrip from CERT, return 0 on success */ @@ -462,6 +465,7 @@ card_enum_keypairs (CARD card, int idx, 100 := Regular X.509 cert 101 := Trusted X.509 cert 102 := Useful X.509 cert + 110 := Root CA cert (DINSIG) */ int card_enum_certs (CARD card, int idx, char **certid, int *certtype) diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c index 936672cc6..f910722eb 100644 --- a/scd/ccid-driver.c +++ b/scd/ccid-driver.c @@ -98,6 +98,11 @@ # include "scdaemon.h" # endif +/* Disable all debgging output for now. */ +#undef DBG_CARD_IO +#define DBG_CARD_IO 0 + + # define DEBUGOUT(t) do { if (DBG_CARD_IO) \ log_debug (DRVNAME t); } while (0) # define DEBUGOUT_1(t,a) do { if (DBG_CARD_IO) \ @@ -944,7 +949,9 @@ ccid_transceive (ccid_driver_t handle, { if (n > maxresplen) { - DEBUGOUT ("provided buffer too short for received data\n"); + DEBUGOUT_2 ("provided buffer too short for received data " + "(%u/%u)\n", + (unsigned int)n, (unsigned int)maxresplen); return -1; } diff --git a/scd/command.c b/scd/command.c index bc3132a0b..9e571f228 100644 --- a/scd/command.c +++ b/scd/command.c @@ -1,5 +1,5 @@ /* command.c - SCdaemon command handler - * Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. + * Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -31,6 +31,7 @@ #include "scdaemon.h" #include #include "app-common.h" +#include "apdu.h" /* Required for apdu_*_reader (). */ /* maximum length aloowed as a PIN; used for INQUIRE NEEDPIN */ #define MAXLEN_PIN 100 @@ -90,17 +91,34 @@ option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value) function returns an Assuan error, so don't map the error a second time */ static AssuanError -open_card (CTRL ctrl) +open_card (CTRL ctrl, const char *apptype) { + int slot; + if (ctrl->app_ctx) return 0; /* Already initialized for one specific application. */ if (ctrl->card_ctx) return 0; /* Already initialized using a card context. */ - ctrl->app_ctx = select_application (); + slot = apdu_open_reader (opt.reader_port); + if (slot != -1) + { + ctrl->app_ctx = select_application (ctrl, slot, apptype); + if (!ctrl->app_ctx) + apdu_close_reader (slot); + } if (!ctrl->app_ctx) { /* No application found - fall back to old mode. */ - int rc = card_open (&ctrl->card_ctx); + /* Note that we should rework the old code to use the + application paradigma too. */ + int rc; + + /* If an APPTYPE was requested and it is not pkcs#15, we return + an error here. */ + if (apptype && !(!strcmp (apptype, "P15") || !strcmp (apptype, "p15"))) + rc = gpg_error (GPG_ERR_NOT_SUPPORTED); + else + rc = card_open (&ctrl->card_ctx); if (rc) return map_to_assuan_status (rc); } @@ -143,11 +161,17 @@ percent_plus_unescape (unsigned char *string) -/* SERIALNO +/* SERIALNO [APPTYPE] Return the serial number of the card using a status reponse. This functon should be used to check for the presence of a card. + If APPTYPE is given, an application of that type is selected and an + error is returned if the application is not supported or available. + The default is to auto-select the application using a hardwired + preference system. Note, that a future extension to this function + may allow to specify a list and order of applications to try. + This function is special in that it can be used to reset the card. Most other functions will return an error when a card change has been detected and the use of this function is therefore required. @@ -165,7 +189,7 @@ cmd_serialno (ASSUAN_CONTEXT ctx, char *line) char *serial; time_t stamp; - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, *line? line:NULL))) return rc; if (ctrl->app_ctx) @@ -223,6 +247,7 @@ cmd_serialno (ASSUAN_CONTEXT ctx, char *line) 100 := Regular X.509 cert 101 := Trusted X.509 cert 102 := Useful X.509 cert + 110 := Root CA cert (DINSIG) For certain cards, more information will be returned: @@ -240,7 +265,7 @@ cmd_serialno (ASSUAN_CONTEXT ctx, char *line) S DISP-NAME The name of the card holder as stored on the card; percent - aescaping takes place, spaces are encoded as '+' + escaping takes place, spaces are encoded as '+' S PUBKEY-URL @@ -254,7 +279,7 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line) int rc = 0; int idx; - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, NULL))) return rc; /* Unless the force option is used we try a shortcut by identifying @@ -305,10 +330,15 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line) free (serial_and_stamp); } - /* Return information about the certificates. */ - if (ctrl->app_ctx) - rc = -1; /* This information is not yet available for applications. */ - for (idx=0; !rc; idx++) + /* If we are using the modern application paradigma, let the + application print out its collection of useful status + information. */ + if (!rc && ctrl->app_ctx) + rc = app_write_learn_status (ctrl->app_ctx, ctrl); + + /* Return information about the certificates. FIXME: Move this into + an app-p15.c*/ + for (idx=0; !rc && !ctrl->app_ctx; idx++) { char *certid; int certtype; @@ -333,11 +363,9 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line) if (rc == -1) rc = 0; - - /* Return information about the keys. */ - if (ctrl->app_ctx) - rc = -1; /* This information is not yet available for applications. */ - for (idx=0; !rc; idx++) + /* Return information about the keys. FIXME: Move this into an + app-p15.c */ + for (idx=0; !rc && !ctrl->app_ctx; idx++) { unsigned char keygrip[20]; char *keyid; @@ -346,7 +374,7 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line) rc = card_enum_keypairs (ctrl->card_ctx, idx, keygrip, &keyid); if (gpg_err_code (rc) == GPG_ERR_MISSING_CERT && keyid) { - /* this does happen with an incomplete personalized + /* This does happen with an incomplete personalized card; i.e. during the time we have stored the key on the card but not stored the certificate; probably becuase it has not yet been received back from the CA. Note that we @@ -383,10 +411,6 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line) if (rc == -1) rc = 0; - if (!rc && ctrl->app_ctx) - rc = app_write_learn_status (ctrl->app_ctx, ctrl); - - return map_to_assuan_status (rc); } @@ -403,17 +427,24 @@ cmd_readcert (ASSUAN_CONTEXT ctx, char *line) unsigned char *cert; size_t ncert; - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, NULL))) return rc; + line = xstrdup (line); /* Need a copy of the line. */ if (ctrl->app_ctx) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - - rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert); - if (rc) { - log_error ("card_read_cert failed: %s\n", gpg_strerror (rc)); + rc = app_readcert (ctrl->app_ctx, line, &cert, &ncert); + if (rc) + log_error ("app_readcert failed: %s\n", gpg_strerror (rc)); + } + else + { + rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert); + if (rc) + log_error ("card_read_cert failed: %s\n", gpg_strerror (rc)); } + xfree (line); + line = NULL; if (!rc) { rc = assuan_send_data (ctx, cert, ncert); @@ -440,18 +471,26 @@ cmd_readkey (ASSUAN_CONTEXT ctx, char *line) ksba_cert_t kc = NULL; ksba_sexp_t p; - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, NULL))) return rc; + line = xstrdup (line); /* Need a copy of the line. */ if (ctrl->app_ctx) - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - - rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert); - if (rc) { - log_error ("card_read_cert failed: %s\n", gpg_strerror (rc)); - goto leave; + rc = app_readcert (ctrl->app_ctx, line, &cert, &ncert); + if (rc) + log_error ("app_readcert failed: %s\n", gpg_strerror (rc)); + } + else + { + rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert); + if (rc) + log_error ("card_read_cert failed: %s\n", gpg_strerror (rc)); } + xfree (line); + line = NULL; + if (rc) + goto leave; rc = ksba_cert_new (&kc); if (rc) @@ -569,7 +608,7 @@ cmd_pksign (ASSUAN_CONTEXT ctx, char *line) size_t outdatalen; char *keyidstr; - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, NULL))) return rc; /* We have to use a copy of the key ID because the function may use @@ -619,7 +658,7 @@ cmd_pkauth (ASSUAN_CONTEXT ctx, char *line) size_t outdatalen; char *keyidstr; - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, NULL))) return rc; if (!ctrl->app_ctx) @@ -665,7 +704,7 @@ cmd_pkdecrypt (ASSUAN_CONTEXT ctx, char *line) size_t outdatalen; char *keyidstr; - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, NULL))) return rc; keyidstr = xtrystrdup (line); @@ -718,7 +757,7 @@ cmd_getattr (ASSUAN_CONTEXT ctx, char *line) int rc; char *keyword; - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, NULL))) return rc; keyword = line; @@ -757,7 +796,7 @@ cmd_setattr (ASSUAN_CONTEXT ctx, char *orig_line) size_t nbytes; char *line, *linebuf; - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, NULL))) return rc; /* We need to use a copy of LINE, because PIN_CB uses the same @@ -823,7 +862,7 @@ cmd_genkey (ASSUAN_CONTEXT ctx, char *line) line++; *line = 0; - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, NULL))) return rc; if (!ctrl->app_ctx) @@ -854,7 +893,7 @@ cmd_random (ASSUAN_CONTEXT ctx, char *line) return set_error (Parameter_Error, "number of requested bytes missing"); nbytes = strtoul (line, NULL, 0); - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, NULL))) return rc; if (!ctrl->app_ctx) @@ -904,7 +943,7 @@ cmd_passwd (ASSUAN_CONTEXT ctx, char *line) line++; *line = 0; - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, NULL))) return rc; if (!ctrl->app_ctx) @@ -931,7 +970,7 @@ cmd_checkpin (ASSUAN_CONTEXT ctx, char *line) int rc; char *keyidstr; - if ((rc = open_card (ctrl))) + if ((rc = open_card (ctrl, NULL))) return rc; if (!ctrl->app_ctx) diff --git a/scd/iso7816.c b/scd/iso7816.c index f4aa18c6f..9a15ce953 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -51,6 +51,8 @@ #define CMD_INTERNAL_AUTHENTICATE 0x88 #define CMD_GENERATE_KEYPAIR 0x47 #define CMD_GET_CHALLENGE 0x84 +#define CMD_READ_BINARY 0xB0 +#define CMD_READ_RECORD 0xB2 static gpg_error_t map_sw (int sw) @@ -66,6 +68,8 @@ map_sw (int sw) case SW_USE_CONDITIONS: ec = GPG_ERR_USE_CONDITIONS; break; case SW_NOT_SUPPORTED: ec = GPG_ERR_NOT_SUPPORTED; break; case SW_BAD_PARAMETER: ec = GPG_ERR_INV_VALUE; break; + case SW_FILE_NOT_FOUND: ec = GPG_ERR_ENOENT; break; + case SW_RECORD_NOT_FOUND:ec= GPG_ERR_NOT_FOUND; break; case SW_REF_NOT_FOUND: ec = GPG_ERR_NO_OBJ; break; case SW_BAD_P0_P1: ec = GPG_ERR_INV_VALUE; break; case SW_INS_NOT_SUP: ec = GPG_ERR_CARD; break; @@ -91,18 +95,79 @@ map_sw (int sw) apdu_open_reader (), AID is a buffer of size AIDLEN holding the requested application ID. The function can't be used to enumerate AIDs and won't return the AID on success. The return value is 0 - for okay or GNUPG error code. Note that ISO error codes are + for okay or a GPG error code. Note that ISO error codes are internally mapped. */ gpg_error_t iso7816_select_application (int slot, const char *aid, size_t aidlen) { + static char const openpgp_aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 }; int sw; + int p1 = 0x0C; /* No FCI to be returned. */ + + if (aidlen == sizeof openpgp_aid + && !memcmp (aid, openpgp_aid, sizeof openpgp_aid)) + p1 = 0; /* The current openpgp cards don't allow 0x0c. */ - sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, 0, aidlen, aid); + sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, p1, aidlen, aid); return map_sw (sw); } +gpg_error_t +iso7816_select_file (int slot, int tag, int is_dir, + unsigned char **result, size_t *resultlen) +{ + int sw, p0, p1; + unsigned char tagbuf[2]; + + tagbuf[0] = (tag >> 8) & 0xff; + tagbuf[1] = tag & 0xff; + + if (result || resultlen) + { + *result = NULL; + *resultlen = 0; + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + } + else + { + p0 = (tag == 0x3F00)? 0: is_dir? 1:2; + p1 = 0x0c; /* No FC return. */ + sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, + p0, p1, 2, tagbuf ); + return map_sw (sw); + } + + return 0; +} + + +/* This is a private command currently only working for TCOS cards. */ +gpg_error_t +iso7816_list_directory (int slot, int list_dirs, + unsigned char **result, size_t *resultlen) +{ + int sw; + + if (!result || !resultlen) + return gpg_error (GPG_ERR_INV_VALUE); + *result = NULL; + *resultlen = 0; + + sw = apdu_send (slot, 0x80, 0xAA, list_dirs? 1:2, 0, -1, NULL, + result, resultlen); + if (sw != SW_SUCCESS) + { + /* Make sure that pending buffers are released. */ + xfree (*result); + *result = NULL; + *resultlen = 0; + } + return map_sw (sw); +} + + + /* Perform a VERIFY command on SLOT using the card holder verification vector CHVNO with a CHV of lenght CHVLEN. Returns 0 on success. */ gpg_error_t @@ -381,3 +446,126 @@ iso7816_get_challenge (int slot, int length, unsigned char *buffer) return 0; } + +/* Perform a READ BINARY command requesting a maximum of NMAX bytes + from OFFSET. With NMAX = 0 the entire file is read. The result is + stored in a newly allocated buffer at the address passed by RESULT. + Returns the length of this data at the address of RESULTLEN. */ +gpg_error_t +iso7816_read_binary (int slot, size_t offset, size_t nmax, + unsigned char **result, size_t *resultlen) +{ + int sw; + unsigned char *buffer; + size_t bufferlen; + + if (!result || !resultlen) + return gpg_error (GPG_ERR_INV_VALUE); + *result = NULL; + *resultlen = 0; + + /* We can only encode 15 bits in p0,p1 to indicate an offset. Thus + we check for this limit. */ + if (offset > 32767 || nmax > 254) + return gpg_error (GPG_ERR_INV_VALUE); + + do + { + buffer = NULL; + bufferlen = 0; + /* Fixme: Either the ccid driver of the TCOS cards have problems + with an Le of 0. */ + sw = apdu_send_le (slot, 0x00, CMD_READ_BINARY, + ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL, + nmax? nmax : 254, &buffer, &bufferlen); + + if (sw != SW_SUCCESS && sw != SW_EOF_REACHED) + { + /* Make sure that pending buffers are released. */ + xfree (buffer); + xfree (*result); + *result = NULL; + *resultlen = 0; + return map_sw (sw); + } + if (*result) /* Need to extend the buffer. */ + { + unsigned char *p = xtryrealloc (*result, *resultlen + bufferlen); + if (!p) + { + gpg_error_t err = gpg_error_from_errno (errno); + xfree (buffer); + xfree (*result); + *result = NULL; + *resultlen = 0; + return err; + } + *result = p; + memcpy (*result + *resultlen, buffer, bufferlen); + *resultlen += bufferlen; + xfree (buffer); + buffer = NULL; + } + else /* Transfer the buffer into our result. */ + { + *result = buffer; + *resultlen = bufferlen; + } + offset += bufferlen; + if (offset > 32767) + break; /* We simply truncate the result for too large + files. */ + } + while (!nmax && sw != SW_EOF_REACHED); + + return 0; +} + +/* Perform a READ RECORD command. RECNO gives the record number to + read with 0 indicating the current record. RECCOUNT must be 1 (not + all cards support reading of more than one record). The result is + stored in a newly allocated buffer at the address passed by RESULT. + Returns the length of this data at the address of RESULTLEN. */ +gpg_error_t +iso7816_read_record (int slot, int recno, int reccount, + unsigned char **result, size_t *resultlen) +{ + int sw; + unsigned char *buffer; + size_t bufferlen; + + if (!result || !resultlen) + return gpg_error (GPG_ERR_INV_VALUE); + *result = NULL; + *resultlen = 0; + + /* We can only encode 15 bits in p0,p1 to indicate an offset. Thus + we check for this limit. */ + if (recno < 0 || recno > 255 || reccount != 1) + return gpg_error (GPG_ERR_INV_VALUE); + + buffer = NULL; + bufferlen = 0; + /* Fixme: Either the ccid driver of the TCOS cards have problems + with an Le of 0. */ + sw = apdu_send_le (slot, 0x00, CMD_READ_RECORD, + recno, + 0x04, + -1, NULL, + 254, &buffer, &bufferlen); + + if (sw != SW_SUCCESS && sw != SW_EOF_REACHED) + { + /* Make sure that pending buffers are released. */ + xfree (buffer); + xfree (*result); + *result = NULL; + *resultlen = 0; + return map_sw (sw); + } + *result = buffer; + *resultlen = bufferlen; + + return 0; +} + diff --git a/scd/iso7816.h b/scd/iso7816.h index 26b8d6aba..98e688693 100644 --- a/scd/iso7816.h +++ b/scd/iso7816.h @@ -27,6 +27,10 @@ gpg_error_t iso7816_select_application (int slot, const char *aid, size_t aidlen); +gpg_error_t iso7816_select_file (int slot, int tag, int is_dir, + unsigned char **result, size_t *resultlen); +gpg_error_t iso7816_list_directory (int slot, int list_dirs, + unsigned char **result, size_t *resultlen); gpg_error_t iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen); gpg_error_t iso7816_change_reference_data (int slot, int chvno, @@ -56,5 +60,9 @@ gpg_error_t iso7816_read_public_key (int slot, gpg_error_t iso7816_get_challenge (int slot, int length, unsigned char *buffer); +gpg_error_t iso7816_read_binary (int slot, size_t offset, size_t nmax, + unsigned char **result, size_t *resultlen); +gpg_error_t iso7816_read_record (int slot, int recno, int reccount, + unsigned char **result, size_t *resultlen); #endif /*ISO7816_H*/ diff --git a/scd/sc-copykeys.c b/scd/sc-copykeys.c index b56b88590..78cb2acc8 100644 --- a/scd/sc-copykeys.c +++ b/scd/sc-copykeys.c @@ -165,7 +165,7 @@ main (int argc, char **argv ) /* FIXME: Use select_application. */ appbuf.slot = slot; - rc = app_select_openpgp (&appbuf, &appbuf.serialno, &appbuf.serialnolen); + rc = app_select_openpgp (&appbuf); if (rc) { log_error ("selecting openpgp failed: %s\n", gpg_strerror (rc)); diff --git a/scd/sc-investigate.c b/scd/sc-investigate.c index ecd385690..acef86ead 100644 --- a/scd/sc-investigate.c +++ b/scd/sc-investigate.c @@ -24,6 +24,13 @@ #include #include #include +#include +#include + +#ifdef HAVE_READLINE_READLINE_H +#include +#include +#endif #define JNLIB_NEED_LOG_LOGV #include "scdaemon.h" @@ -32,17 +39,25 @@ #include "apdu.h" /* for open_reader */ #include "atr.h" #include "app-common.h" +#include "iso7816.h" #define _(a) (a) +#define CONTROL_D ('D' - 'A' + 1) + enum cmd_and_opt_values -{ oVerbose = 'v', +{ + oInteractive = 'i', + oVerbose = 'v', oReaderPort = 500, octapiDriver, oDebug, oDebugAll, + oDisableCCID, + + oGenRandom, aTest }; @@ -52,15 +67,27 @@ static ARGPARSE_OPTS opts[] = { { 301, NULL, 0, "@Options:\n " }, + { oInteractive, "interactive", 0, "start in interactive explorer mode"}, { oVerbose, "verbose", 0, "verbose" }, { oReaderPort, "reader-port", 2, "|N|connect to reader at port N"}, { octapiDriver, "ctapi-driver", 2, "NAME|use NAME as ctAPI driver"}, + { oDisableCCID, "disable-ccid", 0, +#ifdef HAVE_LIBUSB + "do not use the internal CCID driver" +#else + "@" +#endif + }, { oDebug, "debug" ,4|16, "set debugging flags"}, { oDebugAll, "debug-all" ,0, "enable full debugging"}, { oGenRandom, "gen-random", 4, "|N|generate N bytes of random"}, {0} }; + +static void interactive_shell (int slot); + + static const char * my_strusage (int level) { @@ -111,10 +138,8 @@ main (int argc, char **argv ) ARGPARSE_ARGS pargs; int slot, rc; const char *reader_port = NULL; - struct app_ctx_s appbuf; unsigned long gen_random = 0; - - memset (&appbuf, 0, sizeof appbuf); + int interactive = 0; set_strusage (my_strusage); gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); @@ -143,7 +168,9 @@ main (int argc, char **argv ) case oDebugAll: opt.debug = ~0; break; case oReaderPort: reader_port = pargs.r.ret_str; break; case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break; + case oDisableCCID: opt.disable_ccid = 1; break; case oGenRandom: gen_random = pargs.r.ret_ulong; break; + case oInteractive: interactive = 1; break; default : pargs.err = 2; break; } } @@ -151,7 +178,7 @@ main (int argc, char **argv ) exit(2); if (opt.verbose < 2) - opt.verbose = 2; /* hack to let select_openpgp print some info. */ + opt.verbose = 2; /* Hack to let select_openpgp print some info. */ if (argc) usage (1); @@ -167,40 +194,61 @@ main (int argc, char **argv ) log_error ("can't dump ATR: %s\n", gpg_strerror (rc)); } - appbuf.slot = slot; - rc = app_select_openpgp (&appbuf, NULL, NULL); - if (rc) - log_error ("selecting openpgp failed: %s\n", gpg_strerror (rc)); + if (interactive) + interactive_shell (slot); else { - appbuf.initialized = 1; - log_info ("openpgp application selected\n"); + struct app_ctx_s appbuf; - if (gen_random) + /* Fixme: We better use app.c directly. */ + memset (&appbuf, 0, sizeof appbuf); + appbuf.slot = slot; + rc = app_select_openpgp (&appbuf); + if (rc) { - size_t nbytes; - unsigned char *buffer; - - buffer = xmalloc (4096); - do + log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc)); + memset (&appbuf, 0, sizeof appbuf); + appbuf.slot = slot; + rc = app_select_dinsig (&appbuf); + if (rc) + log_info ("selecting dinsig failed: %s\n", gpg_strerror (rc)); + else { - nbytes = gen_random > 4096? 4096 : gen_random; - rc = app_get_challenge (&appbuf, nbytes, buffer); - if (rc) - log_error ("app_get_challenge failed: %s\n",gpg_strerror (rc)); - else + appbuf.initialized = 1; + log_info ("dinsig application selected\n"); + } + } + else + { + appbuf.initialized = 1; + log_info ("openpgp application selected\n"); + + if (gen_random) + { + size_t nbytes; + unsigned char *buffer; + + buffer = xmalloc (4096); + do { - if (fwrite (buffer, nbytes, 1, stdout) != 1) - log_error ("writing to stdout failed: %s\n", - strerror (errno)); - gen_random -= nbytes; + nbytes = gen_random > 4096? 4096 : gen_random; + rc = app_get_challenge (&appbuf, nbytes, buffer); + if (rc) + log_error ("app_get_challenge failed: %s\n",gpg_strerror (rc)); + else + { + if (fwrite (buffer, nbytes, 1, stdout) != 1) + log_error ("writing to stdout failed: %s\n", + strerror (errno)); + gen_random -= nbytes; + } } + while (gen_random && !log_get_errorcount (0)); + xfree (buffer); } - while (gen_random && !log_get_errorcount (0)); - xfree (buffer); } } - + return log_get_errorcount (0)? 2:0; } @@ -211,3 +259,377 @@ send_status_info (CTRL ctrl, const char *keyword, ...) { /* DUMMY */ } + + + +/* Dump BUFFER of length NBYTES in a nicely human readable format. */ +static void +dump_buffer (const unsigned char *buffer, size_t nbytes) +{ + int i; + + while (nbytes) + { + for (i=0; i < 16 && i < nbytes; i++) + printf ("%02X%s ", buffer[i], i==8? " ":""); + for (; i < 16; i++) + printf (" %s ", i==8? " ":""); + putchar (' '); + putchar (' '); + for (i=0; i < 16 && i < nbytes; i++) + if (isprint (buffer[i])) + putchar (buffer[i]); + else + putchar ('.'); + nbytes -= i; + buffer += i; + for (; i < 16; i++) + putchar (' '); + putchar ('\n'); + } +} + + +static void +dump_or_store_buffer (const char *arg, + const unsigned char *buffer, size_t nbytes) +{ + const char *s = strchr (arg, '>'); + int append; + FILE *fp; + + if (!s) + { + dump_buffer (buffer, nbytes); + return; + } + if ((append = (*++s == '>'))) + s++; + fp = fopen (s, append? "ab":"wb"); + if (!fp) + { + log_error ("failed to create `%s': %s\n", s, strerror (errno)); + return; + } + if (nbytes && fwrite (buffer, nbytes, 1, fp) != 1) + log_error ("failed to write to `%s': %s\n", s, strerror (errno)); + if (fclose (fp)) + log_error ("failed to close `%s': %s\n", s, strerror (errno)); +} + + +/* Convert STRING into a a newly allocated buffer and return the + length of the buffer in R_LENGTH. Detect xx:xx:xx... sequence and + unhexify that one. */ +static unsigned char * +pin_to_buffer (const char *string, size_t *r_length) +{ + unsigned char *buffer = xmalloc (strlen (string)+1); + const char *s; + size_t n; + + for (s=string, n=0; *s; s += 3) + { + if (hexdigitp (s) && hexdigitp (s+1) && (s[2]==':'||!s[2])) + { + buffer[n++] = xtoi_2 (s); + if (!s[2]) + break; + } + else + { + memcpy (buffer, string, strlen (string)); + *r_length = strlen (string); + return buffer; + } + } + *r_length = n; + return buffer; +} + + +static char * +read_line (int use_readline, char *prompt) +{ + static char buf[256]; + +#ifdef HAVE_READLINE + if (use_readline) + { + char *line = readline (prompt); + if (line) + trim_spaces (line); + if (line && strlen (line) > 2 ) + add_history (line); + return line; + } +#endif + /* Either we don't have readline or we are not running + interactively */ +#ifndef HAVE_READLINE + printf ("%s", prompt ); +#endif + fflush(stdout); + if (!fgets(buf, sizeof(buf), stdin)) + return NULL; + if (!strlen(buf)) + return NULL; + if (buf[strlen (buf)-1] == '\n') + buf[strlen (buf)-1] = 0; + trim_spaces (buf); + return buf; +} + +/* Run a shell for interactive exploration of the card. */ +static void +interactive_shell (int slot) +{ + enum cmdids + { + cmdNOP = 0, + cmdQUIT, cmdHELP, + cmdSELECT, + cmdCHDIR, + cmdLS, + cmdAPP, + cmdREAD, + cmdREADREC, + cmdDEBUG, + cmdVERIFY, + cmdCHANGEREF, + + cmdINVCMD + }; + static struct + { + const char *name; + enum cmdids id; + const char *desc; + } cmds[] = { + { "quit" , cmdQUIT , "quit this menu" }, + { "q" , cmdQUIT , NULL }, + { "help" , cmdHELP , "show this help" }, + { "?" , cmdHELP , NULL }, + { "debug" , cmdDEBUG, "set debugging flags" }, + { "select" , cmdSELECT, "select file (EF)" }, + { "s" , cmdSELECT, NULL }, + { "chdir" , cmdCHDIR, "change directory (select DF)"}, + { "cd" , cmdCHDIR, NULL }, + { "ls" , cmdLS, "list directory (some cards only)"}, + { "app" , cmdAPP, "select application"}, + { "read" , cmdREAD, "read binary" }, + { "rb" , cmdREAD, NULL }, + { "readrec", cmdREADREC, "read record(s)" }, + { "rr" , cmdREADREC, NULL }, + { "verify" , cmdVERIFY, "verify CHVNO PIN" }, + { "ver" , cmdVERIFY, NULL }, + { "changeref", cmdCHANGEREF, "change reference data" }, + { NULL, cmdINVCMD } + }; + enum cmdids cmd = cmdNOP; + int use_readline = isatty (fileno(stdin)); + char *line; + gpg_error_t err = 0; + unsigned char *result = NULL; + size_t resultlen; + +#ifdef HAVE_READLINE + if (use_readline) + using_history (); +#endif + + for (;;) + { + int arg_number; + const char *arg_string = ""; + const char *arg_next = ""; + char *p; + int i; + + if (err) + printf ("command failed: %s\n", gpg_strerror (err)); + err = 0; + xfree (result); + result = NULL; + + printf ("\n"); + do + { + line = read_line (use_readline, "cmd> "); + } + while ( line && *line == '#' ); + + arg_number = 0; + if (!line || *line == CONTROL_D) + cmd = cmdQUIT; + else if (!*line) + cmd = cmdNOP; + else { + if ((p=strchr (line,' '))) + { + char *endp; + + *p++ = 0; + trim_spaces (line); + trim_spaces (p); + arg_number = strtol (p, &endp, 0); + arg_string = p; + if (endp != p) + { + arg_next = endp; + while ( spacep (arg_next) ) + arg_next++; + } + } + + for (i=0; cmds[i].name; i++ ) + if (!ascii_strcasecmp (line, cmds[i].name )) + break; + + cmd = cmds[i].id; + } + + switch (cmd) + { + case cmdHELP: + for (i=0; cmds[i].name; i++ ) + if (cmds[i].desc) + printf("%-10s %s\n", cmds[i].name, _(cmds[i].desc) ); + break; + + case cmdQUIT: + goto leave; + + case cmdNOP: + break; + + case cmdDEBUG: + if (!*arg_string) + opt.debug = opt.debug? 0 : 2048; + else + opt.debug = arg_number; + break; + + case cmdSELECT: + err = iso7816_select_file (slot, arg_number, 0, NULL, NULL); + break; + + case cmdCHDIR: + err = iso7816_select_file (slot, arg_number, 1, NULL, NULL); + break; + + case cmdLS: + err = iso7816_list_directory (slot, 1, &result, &resultlen); + if (!err || gpg_err_code (err) == GPG_ERR_ENOENT) + err = iso7816_list_directory (slot, 0, &result, &resultlen); + /* FIXME: Do something with RESULT. */ + break; + + case cmdAPP: + { + app_t app; + + app = select_application (NULL, slot, *arg_string? arg_string:NULL); + if (app) + { + char *sn; + + app_get_serial_and_stamp (app, &sn, NULL); + log_info ("application `%s' ready; sn=%s\n", + app->apptype?app->apptype:"?", sn? sn:"[none]"); + release_application (app); + } + } + break; + + case cmdREAD: + err = iso7816_read_binary (slot, 0, 0, &result, &resultlen); + if (!err) + dump_or_store_buffer (arg_string, result, resultlen); + break; + + case cmdREADREC: + if (*arg_string == '*' && (!arg_string[1] || arg_string[1] == ' ')) + { + /* Fixme: Can't write to a file yet. */ + for (i=1, err=0; !err; i++) + { + xfree (result); result = NULL; + err = iso7816_read_record (slot, i, 1, &result, &resultlen); + if (!err) + dump_buffer (result, resultlen); + } + if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) + err = 0; + } + else + { + err = iso7816_read_record (slot, arg_number, 1, + &result, &resultlen); + if (!err) + dump_or_store_buffer (arg_string, result, resultlen); + } + break; + + case cmdVERIFY: + if (arg_number < 0 || arg_number > 255 || (arg_number & 127) > 31) + printf ("error: invalid CHVNO\n"); + else + { + unsigned char *pin; + size_t pinlen; + + pin = pin_to_buffer (arg_next, &pinlen); + err = iso7816_verify (slot, arg_number, pin, pinlen); + xfree (pin); + } + break; + + case cmdCHANGEREF: + { + const char *newpin = arg_next; + + while ( *newpin && !spacep (newpin) ) + newpin++; + while ( spacep (newpin) ) + newpin++; + + if (arg_number < 0 || arg_number > 255 || (arg_number & 127) > 31) + printf ("error: invalid CHVNO\n"); + else if (!*arg_next || !*newpin || newpin == arg_next) + printf ("usage: changeref CHVNO OLDPIN NEWPIN\n"); + else + { + char *oldpin = xstrdup (arg_next); + unsigned char *oldpin_buf, *newpin_buf; + size_t oldpin_len, newpin_len; + + for (p=oldpin; *p && !spacep (p); p++ ) + ; + *p = 0; + oldpin_buf = pin_to_buffer (oldpin, &oldpin_len); + newpin_buf = pin_to_buffer (newpin, &newpin_len); + + err = iso7816_change_reference_data (slot, arg_number, + oldpin_buf, oldpin_len, + newpin_buf, newpin_len); + + xfree (newpin_buf); + xfree (oldpin_buf); + xfree (oldpin); + } + } + break; + + case cmdINVCMD: + default: + printf ("\n"); + printf ("Invalid command (try \"help\")\n"); + break; + } /* End command switch. */ + } /* End of main menu loop. */ + + leave: + ; +} + diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 91ac93227..c6652c8dc 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -100,7 +100,7 @@ static ARGPARSE_OPTS opts[] = { { oReaderPort, "reader-port", 2, N_("|N|connect to reader at port N")}, { octapiDriver, "ctapi-driver", 2, N_("NAME|use NAME as ct-API driver")}, { opcscDriver, "pcsc-driver", 2, N_("NAME|use NAME as PC/SC driver")}, - { oDisableCCID, "disable-ccidc", 0, + { oDisableCCID, "disable-ccid", 0, #ifdef HAVE_LIBUSB N_("do not use the internal CCID driver") #else @@ -397,7 +397,7 @@ main (int argc, char **argv ) case oServer: pipe_server = 1; break; case oDaemon: is_daemon = 1; break; - case oReaderPort: app_set_default_reader_port (pargs.r.ret_str); break; + case oReaderPort: opt.reader_port = pargs.r.ret_str; break; case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break; case opcscDriver: opt.pcsc_driver = pargs.r.ret_str; break; case oDisableCCID: opt.disable_ccid = 1; break; diff --git a/scd/scdaemon.h b/scd/scdaemon.h index e13377af7..2bbf271da 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -55,6 +55,7 @@ struct { const char *homedir; /* configuration directory name */ const char *ctapi_driver; /* Library to access the ctAPI. */ const char *pcsc_driver; /* Library to access the PC/SC system. */ + const char *reader_port; /* NULL or reder port to use. */ int disable_opensc; /* Disable the use of the OpenSC framework. */ int disable_ccid; /* Disable the use of the internal CCID driver. */ int allow_admin; /* Allow the use of admin commands for certain @@ -96,8 +97,10 @@ struct server_control_s { }; typedef struct server_control_s *CTRL; +typedef struct server_control_s *ctrl_t; typedef struct card_ctx_s *CARD; typedef struct app_ctx_s *APP; +typedef struct app_ctx_s *app_t; /*-- scdaemon.c --*/ void scd_exit (int rc); diff --git a/scd/tlv.c b/scd/tlv.c new file mode 100644 index 000000000..dbcd24546 --- /dev/null +++ b/scd/tlv.c @@ -0,0 +1,219 @@ +/* tlv.c - Tag-Length-Value Utilities + * Copyright (C) 2003, 2004 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 "tlv.h" + +static const unsigned char * +do_find_tlv (const unsigned char *buffer, size_t length, + int tag, size_t *nbytes, int nestlevel) +{ + const unsigned char *s = buffer; + size_t n = length; + size_t len; + int this_tag; + int composite; + + for (;;) + { + buffer = s; + if (n < 2) + return NULL; /* Buffer definitely too short for tag and length. */ + if (!*s || *s == 0xff) + { /* Skip optional filler between TLV objects. */ + s++; + n--; + continue; + } + composite = !!(*s & 0x20); + if ((*s & 0x1f) == 0x1f) + { /* more tag bytes to follow */ + s++; + n--; + if (n < 2) + return NULL; /* buffer definitely too short for tag and length. */ + if ((*s & 0x1f) == 0x1f) + return NULL; /* We support only up to 2 bytes. */ + this_tag = (s[-1] << 8) | (s[0] & 0x7f); + } + else + this_tag = s[0]; + len = s[1]; + s += 2; n -= 2; + if (len < 0x80) + ; + else if (len == 0x81) + { /* One byte length follows. */ + if (!n) + return NULL; /* we expected 1 more bytes with the length. */ + len = s[0]; + s++; n--; + } + else if (len == 0x82) + { /* Two byte length follows. */ + if (n < 2) + return NULL; /* We expected 2 more bytes with the length. */ + len = (s[0] << 8) | s[1]; + s += 2; n -= 2; + } + else + return NULL; /* APDU limit is 65535, thus it does not make + sense to assume longer length fields. */ + + if (composite && nestlevel < 100) + { /* Dive into this composite DO after checking for a too deep + nesting. */ + const unsigned char *tmp_s; + size_t tmp_len; + + tmp_s = do_find_tlv (s, len, tag, &tmp_len, nestlevel+1); + if (tmp_s) + { + *nbytes = tmp_len; + return tmp_s; + } + } + + if (this_tag == tag) + { + *nbytes = len; + return s; + } + if (len > n) + return NULL; /* Buffer too short to skip to the next tag. */ + s += len; n -= len; + } +} + + +/* Locate a TLV encoded data object in BUFFER of LENGTH and + return a pointer to value as well as its length in NBYTES. Return + NULL if it was not found. Note, that the function does not check + whether the value fits into the provided buffer. */ +const unsigned char * +find_tlv (const unsigned char *buffer, size_t length, + int tag, size_t *nbytes) +{ + return do_find_tlv (buffer, length, tag, nbytes, 0); +} + + + + +/* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag + and the length part from the TLV triplet. Update BUFFER and SIZE + on success. */ +gpg_error_t +parse_ber_header (unsigned char const **buffer, size_t *size, + int *r_class, int *r_tag, + int *r_constructed, int *r_ndef, + size_t *r_length, size_t *r_nhdr) +{ + int c; + unsigned long tag; + const unsigned char *buf = *buffer; + size_t length = *size; + + *r_ndef = 0; + *r_length = 0; + *r_nhdr = 0; + + /* Get the tag. */ + if (!length) + return gpg_error (GPG_ERR_EOF); + c = *buf++; length--; ++*r_nhdr; + + *r_class = (c & 0xc0) >> 6; + *r_constructed = !!(c & 0x20); + tag = c & 0x1f; + + if (tag == 0x1f) + { + tag = 0; + do + { + /* Simple check against overflow. We limit our maximim tag + value more than needed but that should not be a problem + because I have nver encountered such large value. We + assume at least 32 bit integers. */ + if (tag > (1 << 24)) + return gpg_error (GPG_ERR_TOO_LARGE); + tag <<= 7; + if (!length) + return gpg_error (GPG_ERR_EOF); + c = *buf++; length--; ++*r_nhdr; + tag |= c & 0x7f; + + } + while (c & 0x80); + } + *r_tag = tag; + + /* Get the length. */ + if (!length) + return gpg_error (GPG_ERR_EOF); + c = *buf++; length--; ++*r_nhdr; + + if ( !(c & 0x80) ) + *r_length = c; + else if (c == 0x80) + *r_ndef = 1; + else if (c == 0xff) + return gpg_error (GPG_ERR_BAD_BER); + else + { + unsigned long len = 0; + int count = c & 0x7f; + + for (; count; count--) + { + /* Simple check against overflow. We limit our maximim + length more than needed but that should not be a problem + because I have never encountered such large value and + well they are managed in memory and thus we would run + into memory problems anyway. We assume at least 32 bit + integers. */ + if (len > (1 << 24)) + return gpg_error (GPG_ERR_TOO_LARGE); + len <<= 8; + if (!length) + return gpg_error (GPG_ERR_EOF); + c = *buf++; length--; ++*r_nhdr; + len |= c & 0xff; + } + *r_length = len; + } + + /* Without this kludge some example certs can't be parsed. */ + if (*r_class == CLASS_UNIVERSAL && !*r_tag) + *r_length = 0; + + *buffer = buf; + *size = length; + return 0; +} diff --git a/scd/tlv.h b/scd/tlv.h new file mode 100644 index 000000000..26a9905f7 --- /dev/null +++ b/scd/tlv.h @@ -0,0 +1,84 @@ +/* tlv.h - Tag-Length-Value Utilities + * Copyright (C) 2004 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 SCD_TLV_H +#define SCD_TLV_H 1 + + +enum tlv_tag_class { + CLASS_UNIVERSAL = 0, + CLASS_APPLICATION = 1, + CLASS_CONTEXT = 2, + CLASS_PRIVATE =3 +}; + +enum tlv_tag_type { + TAG_NONE = 0, + TAG_BOOLEAN = 1, + TAG_INTEGER = 2, + TAG_BIT_STRING = 3, + TAG_OCTET_STRING = 4, + TAG_NULL = 5, + TAG_OBJECT_ID = 6, + TAG_OBJECT_DESCRIPTOR = 7, + TAG_EXTERNAL = 8, + TAG_REAL = 9, + TAG_ENUMERATED = 10, + TAG_EMBEDDED_PDV = 11, + TAG_UTF8_STRING = 12, + TAG_REALTIVE_OID = 13, + TAG_SEQUENCE = 16, + TAG_SET = 17, + TAG_NUMERIC_STRING = 18, + TAG_PRINTABLE_STRING = 19, + TAG_TELETEX_STRING = 20, + TAG_VIDEOTEX_STRING = 21, + TAG_IA5_STRING = 22, + TAG_UTC_TIME = 23, + TAG_GENERALIZED_TIME = 24, + TAG_GRAPHIC_STRING = 25, + TAG_VISIBLE_STRING = 26, + TAG_GENERAL_STRING = 27, + TAG_UNIVERSAL_STRING = 28, + TAG_CHARACTER_STRING = 29, + TAG_BMP_STRING = 30 +}; + + + +/* Locate a TLV encoded data object in BUFFER of LENGTH and return a + pointer to value as well as its length in NBYTES. Return NULL if + it was not found. Note, that the function does not check whether + the value fits into the provided buffer.*/ +const unsigned char *find_tlv (const unsigned char *buffer, size_t length, + int tag, size_t *nbytes); + + +/* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag + and the length part from the TLV triplet. Update BUFFER and SIZE + on success. */ +gpg_error_t parse_ber_header (unsigned char const **buffer, size_t *size, + int *r_class, int *r_tag, + int *r_constructed, + int *r_ndef, size_t *r_length, size_t *r_nhdr); + + + +#endif /* SCD_TLV_H */ diff --git a/tools/gpgparsemail.c b/tools/gpgparsemail.c new file mode 100644 index 000000000..956cf18d9 --- /dev/null +++ b/tools/gpgparsemail.c @@ -0,0 +1,705 @@ +/* gpgparsemail.c - Standalone crypto mail parser + * 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 + */ + + +/* This utility prints an RFC8222, possible MIME structured, message + in an annotated format with the first column having an indicator + for the content of the line.. Several options are available to + scrutinize the message. S/MIME and OpenPGP suuport is included. */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rfc822parse.h" + + +#define PGM "gpgparsemail" + +/* Option flags. */ +static int verbose; +static int debug; +static int opt_crypto; /* Decrypt or verify messages. */ +static int opt_no_header; /* Don't output the header lines. */ + +/* Structure used to communicate with the parser callback. */ +struct parse_info_s { + int show_header; /* Show the header lines. */ + int show_data; /* Show the data lines. */ + unsigned int skip_show; /* Temporary disable above for these + number of lines. */ + int show_data_as_note; /* The next data line should be shown + as a note. */ + int show_boundary; + int nesting_level; + + int gpgsm_mime; /* gpgsm shall be used from S/MIME. */ + char *signing_protocol; + int hashing_level; /* The nesting level we are hashing. */ + int hashing; + FILE *hash_file; + FILE *sig_file; + int verify_now; /* Falg set when all signature data is + available. */ +}; + + +/* Print diagnostic message and exit with failure. */ +static void +die (const char *format, ...) +{ + va_list arg_ptr; + + fflush (stdout); + fprintf (stderr, "%s: ", PGM); + + va_start (arg_ptr, format); + vfprintf (stderr, format, arg_ptr); + va_end (arg_ptr); + putc ('\n', stderr); + + exit (1); +} + + +/* Print diagnostic message. */ +static void +err (const char *format, ...) +{ + va_list arg_ptr; + + fflush (stdout); + fprintf (stderr, "%s: ", PGM); + + va_start (arg_ptr, format); + vfprintf (stderr, format, arg_ptr); + va_end (arg_ptr); + putc ('\n', stderr); +} + +static void * +xmalloc (size_t n) +{ + void *p = malloc (n); + if (!p) + die ("out of core: %s", strerror (errno)); + return p; +} + +/* static void * */ +/* xcalloc (size_t n, size_t m) */ +/* { */ +/* void *p = calloc (n, m); */ +/* if (!p) */ +/* die ("out of core: %s", strerror (errno)); */ +/* return p; */ +/* } */ + +/* static void * */ +/* xrealloc (void *old, size_t n) */ +/* { */ +/* void *p = realloc (old, n); */ +/* if (!p) */ +/* die ("out of core: %s", strerror (errno)); */ +/* return p; */ +/* } */ + +static char * +xstrdup (const char *string) +{ + void *p = malloc (strlen (string)+1); + if (!p) + die ("out of core: %s", strerror (errno)); + strcpy (p, string); + return p; +} + +static char * +stpcpy (char *a,const char *b) +{ + while (*b) + *a++ = *b++; + *a = 0; + + return (char*)a; +} + + +static int +run_gnupg (int smime, int sig_fd, int data_fd, int *close_list) +{ + int rp[2]; + pid_t pid; + int i, c, is_status; + unsigned int pos; + char status_buf[10]; + const char *cmd = smime? "gpgsm":"gpg"; + FILE *fp; + + if (pipe (rp) == -1) + die ("error creating a pipe: %s", strerror (errno)); + + pid = fork (); + if (pid == -1) + die ("error forking process: %s", strerror (errno)); + + if (!pid) + { /* Child. */ + char data_fd_buf[50]; + int fd; + + /* Connect our signature fd to stdin. */ + if (sig_fd != 0) + { + if (dup2 (sig_fd, 0) == -1) + die ("dup2 stdin failed: %s", strerror (errno)); + } + + /* Keep our data fd and format it for gpg/gpgsm use. */ + sprintf (data_fd_buf, "-&%d", data_fd); + + /* Send stdout to the bit bucket. */ + fd = open ("/dev/null", O_WRONLY); + if (fd == -1) + die ("can't open `/dev/null': %s", strerror (errno)); + if (fd != 1) + { + if (dup2 (fd, 1) == -1) + die ("dup2 stderr failed: %s", strerror (errno)); + } + + /* Connect stderr to our pipe. */ + if (rp[1] != 2) + { + if (dup2 (rp[1], 2) == -1) + die ("dup2 stderr failed: %s", strerror (errno)); + } + + /* Close other files. */ + for (i=0; (fd=close_list[i]) != -1; i++) + if (fd > 2 && fd != data_fd) + close (fd); + errno = 0; + + execlp (cmd, cmd, + "--enable-special-filenames", + "--status-fd", "2", + "--assume-base64", + "--verify", + "--", + "-", data_fd_buf, + NULL); + + die ("failed to exec the crypto command: %s", strerror (errno)); + } + + /* Parent. */ + close (rp[1]); + + fp = fdopen (rp[0], "r"); + if (!fp) + die ("can't fdopen pipe for reading: %s", strerror (errno)); + + pos = 0; + is_status = 0; + assert (sizeof status_buf > 9); + while ((c=getc (fp)) != EOF) + { + if (pos < 9) + status_buf[pos] = c; + else + { + if (pos == 9) + { + is_status = !memcmp (status_buf, "[GNUPG:] ", 9); + if (is_status) + fputs ( "c ", stdout); + else if (verbose) + fputs ( "# ", stdout); + fwrite (status_buf, 9, 1, stdout); + } + putchar (c); + } + if (c == '\n') + { + if (verbose && pos < 9) + { + fputs ( "# ", stdout); + fwrite (status_buf, pos+1, 1, stdout); + } + pos = 0; + } + else + pos++; + } + if (pos) + { + if (verbose && pos < 9) + { + fputs ( "# ", stdout); + fwrite (status_buf, pos+1, 1, stdout); + } + putchar ('\n'); + } + fclose (fp); + + while ( (i=waitpid (pid, NULL, 0)) == -1 && errno == EINTR) + ; + if (i == -1) + die ("waiting for child failed: %s", strerror (errno)); + + return 0; +} + + + + +/* Verify the signature in the current temp files. */ +static void +verify_signature (struct parse_info_s *info) +{ + int close_list[10]; + + assert (info->hash_file); + assert (info->sig_file); + rewind (info->hash_file); + rewind (info->sig_file); + +/* printf ("# Begin hashed data\n"); */ +/* while ( (c=getc (info->hash_file)) != EOF) */ +/* putchar (c); */ +/* printf ("# End hashed data signature\n"); */ +/* printf ("# Begin signature\n"); */ +/* while ( (c=getc (info->sig_file)) != EOF) */ +/* putchar (c); */ +/* printf ("# End signature\n"); */ +/* rewind (info->hash_file); */ +/* rewind (info->sig_file); */ + + close_list[0] = -1; + run_gnupg (1, fileno (info->sig_file), fileno (info->hash_file), close_list); +} + + + + + +/* Prepare for a multipart/signed. + FIELD_CTX is the parsed context of the content-type header.*/ +static void +mime_signed_begin (struct parse_info_s *info, rfc822parse_t msg, + rfc822parse_field_t field_ctx) +{ + const char *s; + s = rfc822parse_query_parameter (field_ctx, "protocol", 1); + if (s) + { + printf ("h signed.protocol: %s\n", s); + if (!strcmp (s, "application/pkcs7-signature") + || !strcmp (s, "application/x-pkcs7-signature")) + { + if (info->gpgsm_mime) + err ("note: ignoring nested pkcs7-signature"); + else + { + info->gpgsm_mime = 1; + free (info->signing_protocol); + info->signing_protocol = xstrdup (s); + } + } + else if (verbose) + printf ("# this protocol is not supported\n"); + } +} + + +/* Prepare for a multipart/encrypted. + FIELD_CTX is the parsed context of the content-type header.*/ +static void +mime_encrypted_begin (struct parse_info_s *info, rfc822parse_t msg, + rfc822parse_field_t field_ctx) +{ + const char *s; + s = rfc822parse_query_parameter (field_ctx, "protocol", 0); + if (s) + printf ("h encrypted.protocol: %s\n", s); +} + + + +/* Print the event received by the parser for debugging as comment + line. */ +static void +show_event (rfc822parse_event_t event) +{ + const char *s; + + switch (event) + { + case RFC822PARSE_OPEN: s= "Open"; break; + case RFC822PARSE_CLOSE: s= "Close"; break; + case RFC822PARSE_CANCEL: s= "Cancel"; break; + case RFC822PARSE_T2BODY: s= "T2Body"; break; + case RFC822PARSE_FINISH: s= "Finish"; break; + case RFC822PARSE_RCVD_SEEN: s= "Rcvd_Seen"; break; + case RFC822PARSE_LEVEL_DOWN: s= "Level_Down"; break; + case RFC822PARSE_LEVEL_UP: s= "Level_Up"; break; + case RFC822PARSE_BOUNDARY: s= "Boundary"; break; + case RFC822PARSE_LAST_BOUNDARY: s= "Last_Boundary"; break; + case RFC822PARSE_BEGIN_HEADER: s= "Begin_Header"; break; + case RFC822PARSE_PREAMBLE: s= "Preamble"; break; + case RFC822PARSE_EPILOGUE: s= "Epilogue"; break; + default: s= "[unknown event]"; break; + } + printf ("# *** got RFC822 event %s\n", s); +} + +/* This function is called by the parser to communicate events. This + callback comminucates with the main program using a structure + passed in OPAQUE. Should retrun 0 or set errno and return -1. */ +static int +message_cb (void *opaque, rfc822parse_event_t event, rfc822parse_t msg) +{ + struct parse_info_s *info = opaque; + + if (debug) + show_event (event); + if (event == RFC822PARSE_OPEN) + { + /* Initialize for a new message. */ + info->show_header = 1; + } + else if (event == RFC822PARSE_T2BODY) + { + rfc822parse_field_t ctx; + + ctx = rfc822parse_parse_field (msg, "Content-Type", -1); + if (ctx) + { + const char *s1, *s2; + s1 = rfc822parse_query_media_type (ctx, &s2); + if (s1) + { + printf ("h media: %*s%s %s\n", + info->nesting_level*2, "", s1, s2); + if (info->gpgsm_mime == 3) + { + char *buf = xmalloc (strlen (s1) + strlen (s2) + 2); + strcpy (stpcpy (stpcpy (buf, s1), "/"), s2); + assert (info->signing_protocol); + if (strcmp (buf, info->signing_protocol)) + err ("invalid S/MIME structure; expected `%s', found `%s'", + info->signing_protocol, buf); + else + { + printf ("c begin_signature\n"); + info->gpgsm_mime++; + if (opt_crypto) + { + assert (!info->sig_file); + info->sig_file = tmpfile (); + if (!info->sig_file) + die ("error creating temp file: %s", + strerror (errno)); + } + } + free (buf); + } + else if (!strcmp (s1, "multipart")) + { + if (!strcmp (s2, "signed")) + mime_signed_begin (info, msg, ctx); + else if (!strcmp (s2, "encrypted")) + mime_encrypted_begin (info, msg, ctx); + } + } + else + printf ("h media: %*s none\n", info->nesting_level*2, ""); + + rfc822parse_release_field (ctx); + } + else + printf ("h media: %*stext plain [assumed]\n", + info->nesting_level*2, ""); + info->show_header = 0; + info->show_data = 1; + info->skip_show = 1; + } + else if (event == RFC822PARSE_PREAMBLE) + info->show_data_as_note = 1; + else if (event == RFC822PARSE_LEVEL_DOWN) + { + printf ("b down\n"); + info->nesting_level++; + } + else if (event == RFC822PARSE_LEVEL_UP) + { + printf ("b up\n"); + if (info->nesting_level) + info->nesting_level--; + else + err ("invalid structure (bad nesting level)"); + } + else if (event == RFC822PARSE_BOUNDARY || event == RFC822PARSE_LAST_BOUNDARY) + { + info->show_data = 0; + info->show_boundary = 1; + if (event == RFC822PARSE_BOUNDARY) + { + info->show_header = 1; + info->skip_show = 1; + printf ("b part\n"); + } + else + printf ("b last\n"); + + if (info->gpgsm_mime == 2 && info->nesting_level == info->hashing_level) + { + printf ("c end_hash\n"); + info->gpgsm_mime++; + info->hashing = 0; + } + else if (info->gpgsm_mime == 4) + { + printf ("c end_signature\n"); + info->verify_now = 1; + } + } + else if (event == RFC822PARSE_BEGIN_HEADER) + { + if (info->gpgsm_mime == 1) + { + printf ("c begin_hash\n"); + info->hashing = 1; + info->hashing_level = info->nesting_level; + info->gpgsm_mime++; + + if (opt_crypto) + { + assert (!info->hash_file); + info->hash_file = tmpfile (); + if (!info->hash_file) + die ("failed to create temporary file: %s", strerror (errno)); + } + } + } + + return 0; +} + + +/* Read a message from FP and process it according to the global + options. */ +static void +parse_message (FILE *fp) +{ + char line[5000]; + size_t length; + rfc822parse_t msg; + unsigned int lineno = 0; + int no_cr_reported = 0; + struct parse_info_s info; + + memset (&info, 0, sizeof info); + + msg = rfc822parse_open (message_cb, &info); + if (!msg) + die ("can't open parser: %s", strerror (errno)); + + /* Fixme: We should not use fgets becuase it can't cope with + embedded nul characters. */ + while (fgets (line, sizeof (line), fp)) + { + lineno++; + if (lineno == 1 && !strncmp (line, "From ", 5)) + continue; /* We better ignore a leading From line. */ + + length = strlen (line); + if (length && line[length - 1] == '\n') + line[--length] = 0; + else + err ("line number %u too long or last line not terminated", lineno); + if (length && line[length - 1] == '\r') + line[--length] = 0; + else if (verbose && !no_cr_reported) + { + err ("non canonical ended line detected (line %u)", lineno); + no_cr_reported = 1; + } + + + if (rfc822parse_insert (msg, line, length)) + die ("parser failed: %s", strerror (errno)); + + if (info.hashing) + { + /* Delay hashing of the CR/LF because the last line ending + belongs to the next boundary. */ + if (debug) + printf ("# hashing %s`%s'\n", info.hashing==2?"CR,LF+":"", line); + if (opt_crypto) + { + if (info.hashing == 2) + fputs ("\r\n", info.hash_file); + fputs (line, info.hash_file); + if (ferror (info.hash_file)) + die ("error writing to temporary file: %s", strerror (errno)); + } + + info.hashing = 2; + } + + if (info.sig_file && opt_crypto) + { + if (info.verify_now) + { + verify_signature (&info); + fclose (info.hash_file); + info.hash_file = NULL; + fclose (info.sig_file); + info.sig_file = NULL; + info.gpgsm_mime = 0; + } + else + { + fputs (line, info.sig_file); + fputs ("\r\n", info.sig_file); + if (ferror (info.sig_file)) + die ("error writing to temporary file: %s", strerror (errno)); + } + } + + if (info.show_boundary) + { + if (!opt_no_header) + printf (":%s\n", line); + info.show_boundary = 0; + } + + if (info.skip_show) + info.skip_show--; + else if (info.show_data) + { + if (info.show_data_as_note) + { + if (verbose) + printf ("# DATA: %s\n", line); + info.show_data_as_note = 0; + } + else + printf (" %s\n", line); + } + else if (info.show_header && !opt_no_header) + printf (".%s\n", line); + + } + + rfc822parse_close (msg); +} + + +int +main (int argc, char **argv) +{ + int last_argc = -1; + + if (argc) + { + argc--; argv++; + } + while (argc && last_argc != argc ) + { + last_argc = argc; + if (!strcmp (*argv, "--")) + { + argc--; argv++; + break; + } + else if (!strcmp (*argv, "--help")) + { + puts ( + "Usage: " PGM " [OPTION] [FILE]\n" + "Parse a mail message into an annotated format.\n\n" + " --crypto decrypt or verify messages\n" + " --no-header don't output the header lines\n" + " --verbose enable extra informational output\n" + " --debug enable additional debug output\n" + " --help display this help and exit\n\n" + "With no FILE, or when FILE is -, read standard input.\n\n" + "Report bugs to ."); + exit (0); + } + else if (!strcmp (*argv, "--verbose")) + { + verbose = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--debug")) + { + verbose = debug = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--crypto")) + { + opt_crypto = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--no-header")) + { + opt_no_header = 1; + argc--; argv++; + } + } + + if (argc > 1) + die ("usage: " PGM " [OPTION] [FILE] (try --help for more information)\n"); + + signal (SIGPIPE, SIG_IGN); + + if (argc && strcmp (*argv, "-")) + { + FILE *fp = fopen (*argv, "rb"); + if (!fp) + die ("can't open `%s': %s", *argv, strerror (errno)); + parse_message (fp); + fclose (fp); + } + else + parse_message (stdin); + + return 0; +} + + +/* +Local Variables: +compile-command: "gcc -Wall -g -o gpgparsemail rfc822parse.c gpgparsemail.c" +End: +*/ diff --git a/tools/rfc822parse.c b/tools/rfc822parse.c new file mode 100644 index 000000000..be1cf4a47 --- /dev/null +++ b/tools/rfc822parse.c @@ -0,0 +1,1235 @@ +/* rfc822parse.c - Simple mail and MIME parser + * Copyright (C) 1999, 2000 Werner Koch, Duesseldorf + * Copyright (C) 2003, g10 Code GmbH + * + * This program 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. + * + * This program is 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 + */ + + +/* According to RFC822 binary 0 are allowed at many places. We + * do not handle this correct especially in the field parsing code. It + * should be easy to fix and the API provides a interfcaes which returns + * the length but in addition makes sure that returned strings are always + * ended by a \0. + * + * Furthermore, the case of field names is changed and thus it is not + * always a good idea to use these modified header + * lines (e.g. signatures may break). + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "rfc822parse.h" + +enum token_type +{ + tSPACE, + tATOM, + tQUOTED, + tDOMAINLIT, + tSPECIAL +}; + +/* For now we directly use our TOKEN as the parse context */ +typedef struct rfc822parse_field_context *TOKEN; +struct rfc822parse_field_context +{ + TOKEN next; + enum token_type type; + struct { + unsigned int cont:1; + unsigned int lowered:1; + } flags; + /*TOKEN owner_pantry; */ + char data[1]; +}; + +struct hdr_line +{ + struct hdr_line *next; + int cont; /* This is a continuation of the previous line. */ + unsigned char line[1]; +}; + +typedef struct hdr_line *HDR_LINE; + + +struct part +{ + struct part *right; /* The next part. */ + struct part *down; /* A contained part. */ + HDR_LINE hdr_lines; /* Header lines os that part. */ + HDR_LINE *hdr_lines_tail; /* Helper for adding lines. */ + char *boundary; /* Only used in the first part. */ +}; +typedef struct part *part_t; + +struct rfc822parse_context +{ + rfc822parse_cb_t callback; + void *callback_value; + int callback_error; + int in_body; + int in_preamble; /* Wether we are before the first boundary. */ + part_t parts; /* The tree of parts. */ + part_t current_part; /* Whom we are processing (points into parts). */ + const char *boundary; /* Current boundary. */ +}; + +static HDR_LINE find_header (rfc822parse_t msg, const char *name, + int which, HDR_LINE * rprev); + + +static size_t +length_sans_trailing_ws (const unsigned char *line, size_t len) +{ + const unsigned char *p, *mark; + size_t n; + + for (mark=NULL, p=line, n=0; n < len; n++, p++) + { + if (strchr (" \t\r\n", *p )) + { + if( !mark ) + mark = p; + } + else + mark = NULL; + } + + if (mark) + return mark - line; + return len; +} + + +static void +lowercase_string (unsigned char *string) +{ + for (; *string; string++) + if (*string >= 'A' && *string <= 'Z') + *string = *string - 'A' + 'a'; +} + +/* Transform a header name into a standard capitalized format; i.e + "Content-Type". Conversion stops at the colon. As usual we don't + use the localized versions of ctype.h. + */ +static void +capitalize_header_name (unsigned char *name) +{ + int first = 1; + + for (; *name && *name != ':'; name++) + if (*name == '-') + first = 1; + else if (first) + { + if (*name >= 'a' && *name <= 'z') + *name = *name - 'a' + 'A'; + first = 0; + } + else if (*name >= 'A' && *name <= 'Z') + *name = *name - 'A' + 'a'; +} + + +static char * +stpcpy (char *a,const char *b) +{ + while (*b) + *a++ = *b++; + *a = 0; + + return (char*)a; +} + + +/* If a callback has been registerd, call it for the event of type + EVENT. */ +static int +do_callback (rfc822parse_t msg, rfc822parse_event_t event) +{ + int rc; + + if (!msg->callback || msg->callback_error) + return 0; + rc = msg->callback (msg->callback_value, event, msg); + if (rc) + msg->callback_error = rc; + return rc; +} + +static part_t +new_part (void) +{ + part_t part; + + part = calloc (1, sizeof *part); + if (part) + { + part->hdr_lines_tail = &part->hdr_lines; + } + return part; +} + + +static void +release_part (part_t part) +{ + part_t tmp; + HDR_LINE hdr, hdr2; + + for (; part; part = tmp) + { + tmp = part->right; + if (part->down) + release_part (part->down); + for (hdr = part->hdr_lines; hdr; hdr = hdr2) + { + hdr2 = hdr->next; + free (hdr); + } + free (part->boundary); + free (part); + } +} + + +static void +release_handle_data (rfc822parse_t msg) +{ + release_part (msg->parts); + msg->parts = NULL; + msg->current_part = NULL; + msg->boundary = NULL; +} + + +/* Create a new parsing context for an entire rfc822 message and + return it. CB and CB_VALUE may be given to callback for certain + events. NULL is returned on error with errno set appropriately. */ +rfc822parse_t +rfc822parse_open (rfc822parse_cb_t cb, void *cb_value) +{ + rfc822parse_t msg = calloc (1, sizeof *msg); + if (msg) + { + msg->parts = msg->current_part = new_part (); + if (!msg->parts) + { + free (msg); + msg = NULL; + } + else + { + msg->callback = cb; + msg->callback_value = cb_value; + if (do_callback (msg, RFC822PARSE_OPEN)) + { + release_handle_data (msg); + free (msg); + msg = NULL; + } + } + } + return msg; +} + + +void +rfc822parse_cancel (rfc822parse_t msg) +{ + if (msg) + { + do_callback (msg, RFC822PARSE_CANCEL); + release_handle_data (msg); + free (msg); + } +} + + +void +rfc822parse_close (rfc822parse_t msg) +{ + if (msg) + { + do_callback (msg, RFC822PARSE_CLOSE); + release_handle_data (msg); + free (msg); + } +} + +static part_t +find_parent (part_t tree, part_t target) +{ + part_t part; + + for (part = tree->down; part; part = part->right) + { + if (part == target) + return tree; /* Found. */ + if (part->down) + { + part_t tmp = find_parent (part, target); + if (tmp) + return tmp; + } + } + return NULL; +} + +static void +set_current_part_to_parent (rfc822parse_t msg) +{ + part_t parent; + + assert (msg->current_part); + parent = find_parent (msg->parts, msg->current_part); + if (!parent) + return; /* Already at the top. */ + +#ifndef NDEBUG + { + part_t part; + for (part = parent->down; part; part = part->right) + if (part == msg->current_part) + break; + assert (part); + } +#endif + msg->current_part = parent; + + parent = find_parent (msg->parts, parent); + msg->boundary = parent? parent->boundary: NULL; +} + + + +/**************** + * We have read in all header lines and are about to receive the body + * part. The delimiter line has already been processed. + * + * FIXME: we's better return an error in case of memory failures. + */ +static int +transition_to_body (rfc822parse_t msg) +{ + rfc822parse_field_t ctx; + int rc; + + rc = do_callback (msg, RFC822PARSE_T2BODY); + if (!rc) + { + /* Store the boundary if we have multipart type. */ + ctx = rfc822parse_parse_field (msg, "Content-Type", -1); + if (ctx) + { + const char *s; + + s = rfc822parse_query_media_type (ctx, NULL); + if (s && !strcmp (s,"multipart")) + { + s = rfc822parse_query_parameter (ctx, "boundary", 0); + if (s) + { + assert (!msg->current_part->boundary); + msg->current_part->boundary = malloc (strlen (s) + 1); + if (msg->current_part->boundary) + { + part_t part; + + strcpy (msg->current_part->boundary, s); + msg->boundary = msg->current_part->boundary; + part = new_part (); + if (!part) + { + int save_errno = errno; + rfc822parse_release_field (ctx); + errno = save_errno; + return -1; + } + rc = do_callback (msg, RFC822PARSE_LEVEL_DOWN); + assert (!msg->current_part->down); + msg->current_part->down = part; + msg->current_part = part; + msg->in_preamble = 1; + } + } + } + rfc822parse_release_field (ctx); + } + } + + return rc; +} + +/* We have just passed a MIME boundary and need to prepare for new part. + headers. */ +static int +transition_to_header (rfc822parse_t msg) +{ + part_t part; + + assert (msg->current_part); + assert (!msg->current_part->right); + + part = new_part (); + if (!part) + return -1; + + msg->current_part->right = part; + msg->current_part = part; + return 0; +} + + +static int +insert_header (rfc822parse_t msg, const unsigned char *line, size_t length) +{ + HDR_LINE hdr; + + assert (msg->current_part); + if (!length) + { + msg->in_body = 1; + return transition_to_body (msg); + } + + if (!msg->current_part->hdr_lines) + do_callback (msg, RFC822PARSE_BEGIN_HEADER); + + length = length_sans_trailing_ws (line, length); + hdr = malloc (sizeof (*hdr) + length); + if (!hdr) + return -1; + hdr->next = NULL; + hdr->cont = (*line == ' ' || *line == '\t'); + memcpy (hdr->line, line, length); + hdr->line[length] = 0; /* Make it a string. */ + + /* Transform a field name into canonical format. */ + if (!hdr->cont && strchr (line, ':')) + capitalize_header_name (hdr->line); + + *msg->current_part->hdr_lines_tail = hdr; + msg->current_part->hdr_lines_tail = &hdr->next; + + /* Lets help the caller to prevent mail loops and issue an event for + * every Received header. */ + if (length >= 9 && !memcmp (line, "Received:", 9)) + do_callback (msg, RFC822PARSE_RCVD_SEEN); + return 0; +} + + +/**************** + * Note: We handle the body transparent to allow binary zeroes in it. + */ +static int +insert_body (rfc822parse_t msg, const unsigned char *line, size_t length) +{ + int rc = 0; + + if (length > 2 && *line == '-' && line[1] == '-' && msg->boundary) + { + size_t blen = strlen (msg->boundary); + + if (length == blen + 2 + && !memcmp (line+2, msg->boundary, blen)) + { + rc = do_callback (msg, RFC822PARSE_BOUNDARY); + msg->in_body = 0; + if (!rc && !msg->in_preamble) + rc = transition_to_header (msg); + msg->in_preamble = 0; + } + else if (length == blen + 4 + && line[length-2] =='-' && line[length-1] == '-' + && !memcmp (line+2, msg->boundary, blen)) + { + rc = do_callback (msg, RFC822PARSE_LAST_BOUNDARY); + msg->boundary = NULL; /* No current boundary anymore. */ + set_current_part_to_parent (msg); + + /* Fixme: The next should acctually be sent right before the + next boundary, so that we can mark the epilogue. */ + if (!rc) + rc = do_callback (msg, RFC822PARSE_LEVEL_UP); + } + } + if (msg->in_preamble && !rc) + rc = do_callback (msg, RFC822PARSE_PREAMBLE); + + return rc; +} + +/* Insert the next line into the parser. Return 0 on success or true + on error with errno set appropriately. */ +int +rfc822parse_insert (rfc822parse_t msg, const unsigned char *line, size_t length) +{ + return (msg->in_body + ? insert_body (msg, line, length) + : insert_header (msg, line, length)); +} + + +/* Tell the parser that we have finished the message. */ +int +rfc822parse_finish (rfc822parse_t msg) +{ + return do_callback (msg, RFC822PARSE_FINISH); +} + + + +/**************** + * Get a copy of a header line. The line is returned as one long + * string with LF to separate the continuation line. Caller must free + * the return buffer. which may be used to enumerate over all lines. + * Wildcards are allowed. This function works on the current headers; + * i.e. the regular mail headers or the MIME headers of the current + * part. + * + * WHICH gives the mode: + * -1 := Take the last occurence + * n := Take the n-th one. + * + * Returns a newly allocated buffer or NULL on error. errno is set in + * case of a memory failure or set to 0 if the requested field is not + * available. + */ +char * +rfc822parse_get_field (rfc822parse_t msg, const char *name, int which) +{ + HDR_LINE h, h2; + char *buf, *p; + size_t n; + + h = find_header (msg, name, which, NULL); + if (!h) + { + errno = 0; + return NULL; /* no such field */ + } + + n = strlen (h->line) + 1; + for (h2 = h->next; h2 && h2->cont; h2 = h2->next) + n += strlen (h2->line) + 1; + + buf = p = malloc (n); + if (buf) + { + p = stpcpy (p, h->line); + *p++ = '\n'; + for (h2 = h->next; h2 && h2->cont; h2 = h2->next) + { + p = stpcpy (p, h2->line); + *p++ = '\n'; + } + p[-1] = 0; + } + return buf; +} + + +/**************** + * Enumerate all header. Caller has to provide the address of a pointer + * which has to be initialzed to NULL, the caller should then never change this + * pointer until he has closed the enumeration by passing again the address + * of the pointer but with msg set to NULL. + * The function returns pointers to all the header lines or NULL when + * all lines have been enumerated or no headers are available. + */ +const char * +rfc822parse_enum_header_lines (rfc822parse_t msg, void **context) +{ + HDR_LINE l; + + if (!msg) /* Close. */ + return NULL; + + if (*context == msg || !msg->current_part) + return NULL; + + l = *context ? (HDR_LINE) *context : msg->current_part->hdr_lines; + + if (l) + { + *context = l->next ? (void *) (l->next) : (void *) msg; + return l->line; + } + *context = msg; /* Mark end of list. */ + return NULL; +} + + + +/**************** + * Find a header field. If the Name does end in an asterisk this is meant + * to be a wildcard. + * + * which -1 : Retrieve the last field + * >0 : Retrieve the n-th field + + * RPREV may be used to return the predecessor of the returned field; + * which may be NULL for the very first one. It has to be initialzed + * to either NULL in which case the search start at the first header line, + * or it may point to a headerline, where the search should start + */ +static HDR_LINE +find_header (rfc822parse_t msg, const char *name, int which, HDR_LINE *rprev) +{ + HDR_LINE hdr, prev = NULL, mark = NULL; + unsigned char *p; + size_t namelen, n; + int found = 0; + int glob = 0; + + if (!msg->current_part) + return NULL; + + namelen = strlen (name); + if (namelen && name[namelen - 1] == '*') + { + namelen--; + glob = 1; + } + + hdr = msg->current_part->hdr_lines; + if (rprev && *rprev) + { + /* spool forward to the requested starting place. + * we cannot simply set this as we have to return + * the previous list element too */ + for (; hdr && hdr != *rprev; prev = hdr, hdr = hdr->next) + ; + } + + for (; hdr; prev = hdr, hdr = hdr->next) + { + if (hdr->cont) + continue; + if (!(p = strchr (hdr->line, ':'))) + continue; /* invalid header, just skip it. */ + n = p - hdr->line; + if (!n) + continue; /* invalid name */ + if ((glob ? (namelen <= n) : (namelen == n)) + && !memcmp (hdr->line, name, namelen)) + { + found++; + if (which == -1) + mark = hdr; + else if (found == which) + { + if (rprev) + *rprev = prev; + return hdr; + } + } + } + if (mark && rprev) + *rprev = prev; + return mark; +} + + + +static const char * +skip_ws (const char *s) +{ + while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n') + s++; + return s; +} + + +static void +release_token_list (TOKEN t) +{ + while (t) + { + TOKEN t2 = t->next; + /* fixme: If we have owner_pantry, put the token back to + * this pantry so that it can be reused later */ + free (t); + t = t2; + } +} + + +static TOKEN +new_token (enum token_type type, const char *buf, size_t length) +{ + TOKEN t; + + /* fixme: look through our pantries to find a suitable + * token for reuse */ + t = malloc (sizeof *t + length); + if (t) + { + t->next = NULL; + t->type = type; + memset (&t->flags, 0, sizeof (t->flags)); + t->data[0] = 0; + if (buf) + { + memcpy (t->data, buf, length); + t->data[length] = 0; /* Make sure it is a C string. */ + } + else + t->data[0] = 0; + } + return t; +} + +static TOKEN +append_to_token (TOKEN old, const char *buf, size_t length) +{ + size_t n = strlen (old->data); + TOKEN t; + + t = malloc (sizeof *t + n + length); + if (t) + { + t->next = old->next; + t->type = old->type; + t->flags = old->flags; + memcpy (t->data, old->data, n); + memcpy (t->data + n, buf, length); + t->data[n + length] = 0; + old->next = NULL; + release_token_list (old); + } + return t; +} + + + +/* + Parse a field into tokens as defined by rfc822. + */ +static TOKEN +parse_field (HDR_LINE hdr) +{ + static const char specials[] = "<>@.,;:\\[]\"()"; + static const char specials2[] = "<>@.,;:"; + static const char tspecials[] = "/?=<>@,;:\\[]\"()"; + static const char tspecials2[] = "/?=<>@.,;:"; + static struct + { + const unsigned char *name; + size_t namelen; + } tspecial_header[] = { + { "Content-Type", 12}, + { "Content-Transfer-Encoding", 25}, + { NULL, 0} + }; + const char *delimiters; + const char *delimiters2; + const unsigned char *line, *s, *s2; + size_t n; + int i, invalid = 0; + TOKEN t, tok, *tok_tail; + + errno = 0; + if (!hdr) + return NULL; + + tok = NULL; + tok_tail = &tok; + + line = hdr->line; + if (!(s = strchr (line, ':'))) + return NULL; /* oops */ + + n = s - line; + if (!n) + return NULL; /* oops: invalid name */ + + delimiters = specials; + delimiters2 = specials2; + for (i = 0; tspecial_header[i].name; i++) + { + if (n == tspecial_header[i].namelen + && !memcmp (line, tspecial_header[i].name, n)) + { + delimiters = tspecials; + delimiters2 = tspecials2; + break; + } + } + + s++; /* Move over the colon. */ + for (;;) + { + if (!*s) + { + if (!hdr->next || !hdr->next->cont) + break; + hdr = hdr->next; + s = hdr->line; + } + + if (*s == '(') + { + int level = 1; + int in_quote = 0; + + invalid = 0; + for (s++;; s++) + { + if (!*s) + { + if (!hdr->next || !hdr->next->cont) + break; + hdr = hdr->next; + s = hdr->line; + } + + if (in_quote) + { + if (*s == '\"') + in_quote = 0; + else if (*s == '\\' && s[1]) /* what about continuation? */ + s++; + } + else if (*s == ')') + { + if (!--level) + break; + } + else if (*s == '(') + level++; + else if (*s == '\"') + in_quote = 1; + } + if (!*s) + ; /* Actually this is an error, but we don't care about it. */ + else + s++; + } + else if (*s == '\"' || *s == '[') + { + /* We do not check for non-allowed nesting of domainliterals */ + int term = *s == '\"' ? '\"' : ']'; + invalid = 0; + s++; + t = NULL; + + for (;;) + { + for (s2 = s; *s2; s2++) + { + if (*s2 == term) + break; + else if (*s2 == '\\' && s2[1]) /* what about continuation? */ + s2++; + } + + t = (t + ? append_to_token (t, s, s2 - s) + : new_token (term == '\"'? tQUOTED : tDOMAINLIT, s, s2 - s)); + if (!t) + goto failure; + + if (*s2 || !hdr->next || !hdr->next->cont) + break; + hdr = hdr->next; + s = hdr->line; + } + *tok_tail = t; + tok_tail = &t->next; + s = s2; + if (*s) + s++; /* skip the delimiter */ + } + else if ((s2 = strchr (delimiters2, *s))) + { /* Special characters which are not handled above. */ + invalid = 0; + t = new_token (tSPECIAL, s, 1); + if (!t) + goto failure; + *tok_tail = t; + tok_tail = &t->next; + s++; + } + else if (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n') + { + invalid = 0; + s = skip_ws (s + 1); + } + else if (*s > 0x20 && !(*s & 128)) + { /* Atom. */ + invalid = 0; + for (s2 = s + 1; *s2 > 0x20 + && !(*s2 & 128) && !strchr (delimiters, *s2); s2++) + ; + t = new_token (tATOM, s, s2 - s); + if (!t) + goto failure; + *tok_tail = t; + tok_tail = &t->next; + s = s2; + } + else + { /* Invalid character. */ + if (!invalid) + { /* For parsing we assume only one space. */ + t = new_token (tSPACE, NULL, 0); + if (!t) + goto failure; + *tok_tail = t; + tok_tail = &t->next; + invalid = 1; + } + s++; + } + } + + return tok; + + failure: + { + int save = errno; + release_token_list (tok); + errno = save; + } + return NULL; +} + + + + +/**************** + * Find and parse a header field. + * WHICH indicates what to do if there are multiple instance of the same + * field (like "Received"); the following value are defined: + * -1 := Take the last occurence + * 0 := Reserved + * n := Take the n-th one. + * Returns a handle for further operations on the parse context of the field + * or NULL if the field was not found. + */ +rfc822parse_field_t +rfc822parse_parse_field (rfc822parse_t msg, const char *name, int which) +{ + HDR_LINE hdr; + + if (!which) + return NULL; + + hdr = find_header (msg, name, which, NULL); + if (!hdr) + return NULL; + return parse_field (hdr); +} + +void +rfc822parse_release_field (rfc822parse_field_t ctx) +{ + if (ctx) + release_token_list (ctx); +} + + + +/**************** + * Check whether T points to a parameter. + * A parameter starts with a semicolon and it is assumed that t + * points to exactly this one. + */ +static int +is_parameter (TOKEN t) +{ + t = t->next; + if (!t || t->type != tATOM) + return 0; + t = t->next; + if (!t || !(t->type == tSPECIAL && t->data[0] == '=')) + return 0; + t = t->next; + if (!t) + return 1; /* We assume that an non existing value is an empty one. */ + return t->type == tQUOTED || t->type == tATOM; +} + +/* + Some header (Content-type) have a special syntax where attribute=value + pairs are used after a leading semicolon. The parse_field code + knows about these fields and changes the parsing to the one defined + in RFC2045. + Returns a pointer to the value which is valid as long as the + parse context is valid; NULL is returned in case that attr is not + defined in the header, a missing value is reppresented by an empty string. + + With LOWER_VALUE set to true, a matching field valuebe be + lowercased. + + Note, that ATTR should be lowercase. + */ +const char * +rfc822parse_query_parameter (rfc822parse_field_t ctx, const char *attr, + int lower_value) +{ + TOKEN t, a; + + for (t = ctx; t; t = t->next) + { + /* skip to the next semicolon */ + for (; t && !(t->type == tSPECIAL && t->data[0] == ';'); t = t->next) + ; + if (!t) + return NULL; + if (is_parameter (t)) + { /* Look closer. */ + a = t->next; /* We know that this is an atom */ + if ( !a->flags.lowered ) + { + lowercase_string (a->data); + a->flags.lowered = 1; + } + if (!strcmp (a->data, attr)) + { /* found */ + t = a->next->next; + /* Either T is now an atom, a quoted string or NULL in + * which case we return an empty string. */ + + if ( lower_value && t && !t->flags.lowered ) + { + lowercase_string (t->data); + t->flags.lowered = 1; + } + return t ? t->data : ""; + } + } + } + return NULL; +} + +/**************** + * This function may be used for the Content-Type header to figure out + * the media type and subtype. Note, that the returned strings are + * guaranteed to be lowercase as required by MIME. + * + * Returns: a pointer to the media type and if subtype is not NULL, + * a pointer to the subtype. + */ +const char * +rfc822parse_query_media_type (rfc822parse_field_t ctx, const char **subtype) +{ + TOKEN t = ctx; + const char *type; + + if (t->type != tATOM) + return NULL; + if (!t->flags.lowered) + { + lowercase_string (t->data); + t->flags.lowered = 1; + } + type = t->data; + t = t->next; + if (!t || t->type != tSPECIAL || t->data[0] != '/') + return NULL; + t = t->next; + if (!t || t->type != tATOM) + return NULL; + + if (subtype) + { + if (!t->flags.lowered) + { + lowercase_string (t->data); + t->flags.lowered = 1; + } + *subtype = t->data; + } + return type; +} + + + + + +#ifdef TESTING + +/* Internal debug function to print the structure of the message. */ +static void +dump_structure (rfc822parse_t msg, part_t part, int indent) +{ + if (!part) + { + printf ("*** Structure of this message:\n"); + part = msg->parts; + } + + for (; part; part = part->right) + { + rfc822parse_field_t ctx; + part_t save_part; /* ugly hack - we should have a function to + get part inforation. */ + const char *s; + + save_part = msg->current_part; + msg->current_part = part; + ctx = rfc822parse_parse_field (msg, "Content-Type", -1); + msg->current_part = save_part; + if (ctx) + { + const char *s1, *s2; + s1 = rfc822parse_query_media_type (ctx, &s2); + if (s1) + printf ("*** %*s %s/%s", indent*2, "", s1, s2); + else + printf ("*** %*s [not found]", indent*2, ""); + + s = rfc822parse_query_parameter (ctx, "boundary", 0); + if (s) + printf (" (boundary=\"%s\")", s); + rfc822parse_release_field (ctx); + } + else + printf ("*** %*s text/plain [assumed]", indent*2, ""); + putchar('\n'); + + if (part->down) + dump_structure (msg, part->down, indent + 1); + } + +} + + + +static void +show_param (rfc822parse_field_t ctx, const char *name) +{ + const char *s; + + if (!ctx) + return; + s = rfc822parse_query_parameter (ctx, name, 0); + if (s) + printf ("*** %s: `%s'\n", name, s); +} + + + +static void +show_event (rfc822parse_event_t event) +{ + const char *s; + + switch (event) + { + case RFC822PARSE_OPEN: s= "Open"; break; + case RFC822PARSE_CLOSE: s= "Close"; break; + case RFC822PARSE_CANCEL: s= "Cancel"; break; + case RFC822PARSE_T2BODY: s= "T2Body"; break; + case RFC822PARSE_FINISH: s= "Finish"; break; + case RFC822PARSE_RCVD_SEEN: s= "Rcvd_Seen"; break; + case RFC822PARSE_BOUNDARY: s= "Boundary"; break; + case RFC822PARSE_LAST_BOUNDARY: s= "Last_Boundary"; break; + default: s= "***invalid event***"; break; + } + printf ("*** got RFC822 event %s\n", s); +} + +static int +msg_cb (void *dummy_arg, rfc822parse_event_t event, rfc822parse_t msg) +{ + show_event (event); + if (event == RFC822PARSE_T2BODY) + { + rfc822parse_field_t ctx; + void *ectx; + const char *line; + + for (ectx=NULL; (line = rfc822parse_enum_header_lines (msg, &ectx)); ) + { + printf ("*** HDR: %s\n", line); + } + rfc822parse_enum_header_lines (NULL, &ectx); /* Close enumerator. */ + + ctx = rfc822parse_parse_field (msg, "Content-Type", -1); + if (ctx) + { + const char *s1, *s2; + s1 = rfc822parse_query_media_type (ctx, &s2); + if (s1) + printf ("*** media: `%s/%s'\n", s1, s2); + else + printf ("*** media: [not found]\n"); + show_param (ctx, "boundary"); + show_param (ctx, "protocol"); + rfc822parse_release_field (ctx); + } + else + printf ("*** media: text/plain [assumed]\n"); + + } + + + return 0; +} + + + +int +main (int argc, char **argv) +{ + char line[5000]; + size_t length; + rfc822parse_t msg; + + msg = rfc822parse_open (msg_cb, NULL); + if (!msg) + abort (); + + while (fgets (line, sizeof (line), stdin)) + { + length = strlen (line); + if (length && line[length - 1] == '\n') + line[--length] = 0; + if (length && line[length - 1] == '\r') + line[--length] = 0; + if (rfc822parse_insert (msg, line, length)) + abort (); + } + + dump_structure (msg, NULL, 0); + + rfc822parse_close (msg); + return 0; +} +#endif + +/* +Local Variables: +compile-command: "gcc -Wall -g -DTESTING -o rfc822parse rfc822parse.c" +End: +*/ diff --git a/tools/rfc822parse.h b/tools/rfc822parse.h new file mode 100644 index 000000000..1293117ac --- /dev/null +++ b/tools/rfc822parse.h @@ -0,0 +1,79 @@ +/* rfc822parse.h - Simple mail and MIME parser + * Copyright (C) 1999 Werner Koch, Duesseldorf + * Copyright (C) 2003, g10 Code GmbH + * + * This program 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. + * + * This program 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 RFC822PARSE_H +#define RFC822PARSE_H + +struct rfc822parse_context; +typedef struct rfc822parse_context *rfc822parse_t; + +typedef enum + { + RFC822PARSE_OPEN = 1, + RFC822PARSE_CLOSE, + RFC822PARSE_CANCEL, + RFC822PARSE_T2BODY, + RFC822PARSE_FINISH, + RFC822PARSE_RCVD_SEEN, + RFC822PARSE_LEVEL_DOWN, + RFC822PARSE_LEVEL_UP, + RFC822PARSE_BOUNDARY, + RFC822PARSE_LAST_BOUNDARY, + RFC822PARSE_BEGIN_HEADER, + RFC822PARSE_PREAMBLE, + RFC822PARSE_EPILOGUE + } +rfc822parse_event_t; + +struct rfc822parse_field_context; +typedef struct rfc822parse_field_context *rfc822parse_field_t; + + +typedef int (*rfc822parse_cb_t) (void *opaque, + rfc822parse_event_t event, + rfc822parse_t msg); + + +rfc822parse_t rfc822parse_open (rfc822parse_cb_t cb, void *opaque_value); + +void rfc822parse_close (rfc822parse_t msg); + +void rfc822parse_cancel (rfc822parse_t msg); +int rfc822parse_finish (rfc822parse_t msg); + +int rfc822parse_insert (rfc822parse_t msg, + const unsigned char *line, size_t length); + +char *rfc822parse_get_field (rfc822parse_t msg, const char *name, int which); + +const char *rfc822parse_enum_header_lines (rfc822parse_t msg, void **context); + +rfc822parse_field_t rfc822parse_parse_field (rfc822parse_t msg, + const char *name, + int which); + +void rfc822parse_release_field (rfc822parse_field_t field); + +const char *rfc822parse_query_parameter (rfc822parse_field_t ctx, + const char *attr, int lower_value); + +const char *rfc822parse_query_media_type (rfc822parse_field_t ctx, + const char **subtype); + +#endif /*RFC822PARSE_H */ -- cgit From de43297298225f2a2846b2b9d379a14921941cec Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 18 Feb 2004 17:00:11 +0000 Subject: * sc-investigate.c (main): Setup the used character set. * scdaemon.c (main): Ditto. * scdaemon.c (set_debug): New. Add option --debug-level. (main): Add option --gpgconf-list. --- scd/ChangeLog | 8 +++++ scd/sc-investigate.c | 4 +++ scd/scdaemon.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 92 insertions(+), 7 deletions(-) (limited to 'scd/scdaemon.c') diff --git a/scd/ChangeLog b/scd/ChangeLog index 002ad1efc..66ceebd22 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,11 @@ +2004-02-18 Werner Koch + + * sc-investigate.c (main): Setup the used character set. + * scdaemon.c (main): Ditto. + + * scdaemon.c (set_debug): New. Add option --debug-level. + (main): Add option --gpgconf-list. + 2004-02-12 Werner Koch * Makefile.am: Include cmacros.am for common flags. diff --git a/scd/sc-investigate.c b/scd/sc-investigate.c index acef86ead..6f8b330b1 100644 --- a/scd/sc-investigate.c +++ b/scd/sc-investigate.c @@ -145,6 +145,9 @@ main (int argc, char **argv ) gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); log_set_prefix ("sc-investigate", 1); + /* Try to auto set the character set. */ + set_native_charset (NULL); + /* check that the libraries are suitable. Do it here because the option parsing may need services of the library */ if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) ) @@ -153,6 +156,7 @@ main (int argc, char **argv ) NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) ); } + gcry_set_log_handler (my_gcry_logger, NULL); /* FIXME? gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);*/ diff --git a/scd/scdaemon.c b/scd/scdaemon.c index c6652c8dc..5665237ca 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -1,5 +1,5 @@ /* scdaemon.c - The GnuPG Smartcard Daemon - * Copyright (C) 2001, 2002 Free Software Foundation, Inc. + * Copyright (C) 2001, 2002, 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -54,9 +54,11 @@ enum cmd_and_opt_values oVerbose = 'v', oNoVerbose = 500, + aGPGConfList, oOptions, oDebug, oDebugAll, + oDebugLevel, oDebugWait, oDebugSC, oNoGreeting, @@ -81,6 +83,8 @@ aTest }; static ARGPARSE_OPTS opts[] = { + + { aGPGConfList, "gpgconf-list", 256, "@" }, { 301, NULL, 0, N_("@Options:\n ") }, @@ -91,8 +95,9 @@ static ARGPARSE_OPTS opts[] = { { oSh, "sh", 0, N_("sh-style command output") }, { oCsh, "csh", 0, N_("csh-style command output") }, { oOptions, "options" , 2, N_("read options from file")}, - { oDebug, "debug" ,4|16, N_("set debugging flags")}, - { oDebugAll, "debug-all" ,0, N_("enable full debugging")}, + { oDebug, "debug" ,4|16, "@"}, + { oDebugAll, "debug-all" ,0, "@"}, + { oDebugLevel, "debug-level" ,2, "@"}, { oDebugWait,"debug-wait",1, "@"}, { oDebugSC, "debug-sc", 1, N_("|N|set OpenSC debug level to N")}, { oNoDetach, "no-detach" ,0, N_("do not detach from the console")}, @@ -191,6 +196,47 @@ my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr) } +/* Setup the debugging. With a LEVEL of NULL only the active debug + flags are propagated to the subsystems. With LEVEL set, a specific + set of debug flags is set; thus overriding all flags already + set. */ +static void +set_debug (const char *level) +{ + if (!level) + ; + else if (!strcmp (level, "none")) + opt.debug = 0; + else if (!strcmp (level, "basic")) + opt.debug = DBG_ASSUAN_VALUE; + else if (!strcmp (level, "advanced")) + opt.debug = DBG_ASSUAN_VALUE|DBG_COMMAND_VALUE; + else if (!strcmp (level, "expert")) + opt.debug = (DBG_ASSUAN_VALUE|DBG_COMMAND_VALUE + |DBG_CACHE_VALUE|DBG_CARD_IO_VALUE); + else if (!strcmp (level, "guru")) + opt.debug = ~0; + else + { + log_error (_("invalid debug-level `%s' given\n"), level); + scd_exit(2); + } + + + if (opt.debug && !opt.verbose) + opt.verbose = 1; + if (opt.debug && opt.quiet) + opt.quiet = 0; + + if (opt.debug & DBG_MPI_VALUE) + gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 2); + if (opt.debug & DBG_CRYPTO_VALUE ) + gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1); + gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); +} + + + static void cleanup (void) { @@ -245,6 +291,7 @@ main (int argc, char **argv ) const char *shell; unsigned configlineno; int parse_debug = 0; + const char *debug_level = NULL; int default_config =1; int greeting = 0; int nogreeting = 0; @@ -254,6 +301,7 @@ main (int argc, char **argv ) int csh_style = 0; char *logfile = NULL; int debug_wait = 0; + int gpgconf_list = 0; set_strusage (my_strusage); gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); @@ -261,6 +309,9 @@ main (int argc, char **argv ) when adding any stuff between here and the call to INIT_SECMEM() somewhere after the option parsing */ log_set_prefix ("scdaemon", 1|4); + /* Try to auto set the character set. */ + set_native_charset (NULL); + i18n_init (); /* check that the libraries are suitable. Do it here because @@ -368,12 +419,14 @@ main (int argc, char **argv ) { switch (pargs.r_opt) { + case aGPGConfList: gpgconf_list = 1; break; case oQuiet: opt.quiet = 1; break; case oVerbose: opt.verbose++; break; case oBatch: opt.batch=1; break; case oDebug: opt.debug |= pargs.r.ret_ulong; break; case oDebugAll: opt.debug = ~0; break; + case oDebugLevel: debug_level = pargs.r.ret_str; break; case oDebugWait: debug_wait = pargs.r.ret_int; break; case oDebugSC: opt.debug_sc = pargs.r.ret_int; break; @@ -434,7 +487,7 @@ main (int argc, char **argv ) log_info ("NOTE: this is a development version!\n"); #endif - + if (atexit (cleanup)) { log_error ("atexit failed\n"); @@ -442,7 +495,8 @@ main (int argc, char **argv ) exit (1); } - + set_debug (debug_level); + if (debug_wait && pipe_server) { log_debug ("waiting for debugger - my pid is %u .....\n", @@ -451,6 +505,25 @@ main (int argc, char **argv ) log_debug ("... okay\n"); } + if (gpgconf_list) + { /* List options and default values in the GPG Conf format. */ + char *filename; + + filename = make_filename (opt.homedir, "scdaemon.conf", NULL); + printf ("gpgconf-scdaemon.conf:\"%s\n", filename); + xfree (filename); + + printf ("verbose:\n" + "quiet:\n" + "debug-level:none\n" + "log-file:\n" + "force:\n" + "faked-system-time:\n" + "no-greeting:\n"); + + scd_exit (0); + } + /* now start with logging to a file if this is desired */ if (logfile) { @@ -649,10 +722,10 @@ main (int argc, char **argv ) void scd_exit (int rc) { - #if 0 +#if 0 #warning no update_random_seed_file update_random_seed_file(); - #endif +#endif #if 0 /* at this time a bit annoying */ if (opt.debug & DBG_MEMSTAT_VALUE) -- cgit From e209ea3c39e7110d94e4420513f397d8656a75b7 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 16 Mar 2004 18:59:21 +0000 Subject: * app-dinsig.c: Implemented. Based on app-nks.c and card-dinsig.c * app-nks.c (get_length_of_cert): Removed. * app-help.c: New. (app_help_read_length_of_cert): New. Code taken from above. New optional arg R_CERTOFF. * card-dinsig.c: Removed. * card.c (card_get_serial_and_stamp): Do not bind to the old and never finsiged card-dinsig.c. * iso7816.c (iso7816_read_binary): Allow for an NMAX > 254. --- scd/ChangeLog | 14 +++ scd/Makefile.am | 14 +-- scd/app-common.h | 7 ++ scd/app-dinsig.c | 314 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- scd/app-help.c | 157 +++++++++++++++++++++++++++ scd/app-nks.c | 66 +----------- scd/app-openpgp.c | 2 +- scd/card.c | 2 - scd/iso7816.c | 20 +++- scd/scdaemon.c | 67 +++++++++--- 10 files changed, 561 insertions(+), 102 deletions(-) create mode 100644 scd/app-help.c (limited to 'scd/scdaemon.c') diff --git a/scd/ChangeLog b/scd/ChangeLog index a9296cbbb..679f867fe 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,17 @@ +2004-03-16 Werner Koch + + * app-dinsig.c: Implemented. Based on app-nks.c and card-dinsig.c + * app-nks.c (get_length_of_cert): Removed. + * app-help.c: New. + (app_help_read_length_of_cert): New. Code taken from above. New + optional arg R_CERTOFF. + + * card-dinsig.c: Removed. + * card.c (card_get_serial_and_stamp): Do not bind to the old and + never finsiged card-dinsig.c. + + * iso7816.c (iso7816_read_binary): Allow for an NMAX > 254. + 2004-03-11 Werner Koch * scdaemon.h (out_of_core): Removed. Replaced callers by standard diff --git a/scd/Makefile.am b/scd/Makefile.am index 89ebf4e05..da59a1997 100644 --- a/scd/Makefile.am +++ b/scd/Makefile.am @@ -34,12 +34,12 @@ scdaemon_SOURCES = \ scdaemon.c scdaemon.h \ command.c card.c \ card-common.h \ - card-p15.c card-dinsig.c \ + card-p15.c \ apdu.c apdu.h \ ccid-driver.c ccid-driver.h \ iso7816.c iso7816.h \ tlv.c tlv.h \ - app.c app-common.h $(card_apps) + app.c app-common.h app-help.c $(card_apps) scdaemon_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \ @@ -53,12 +53,12 @@ sc_investigate_SOURCES = \ iso7816.c iso7816.h \ tlv.c tlv.h \ atr.c atr.h \ - app.c app-common.h $(card_apps) + app.c app-common.h app-help.c $(card_apps) sc_investigate_LDADD = \ ../jnlib/libjnlib.a ../common/libcommon.a \ - $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(LIBUSB_LIBS) @INTLLIBS@ \ - -lgpg-error -ldl + $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBUSB_LIBS) \ + @INTLLIBS@ -lgpg-error -ldl sc_copykeys_SOURCES = \ @@ -68,10 +68,10 @@ sc_copykeys_SOURCES = \ iso7816.c iso7816.h \ tlv.c tlv.h \ atr.c atr.h \ - app.c app-common.h $(card_apps) + app.c app-common.h app-help.c $(card_apps) sc_copykeys_LDADD = \ ../jnlib/libjnlib.a ../common/libcommon.a \ ../common/libsimple-pwquery.a \ - $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(LIBUSB_LIBS) \ + $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBUSB_LIBS) \ -lgpg-error @INTLLIBS@ -ldl diff --git a/scd/app-common.h b/scd/app-common.h index cda17700f..c16a15719 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -21,6 +21,8 @@ #ifndef GNUPG_SCD_APP_COMMON_H #define GNUPG_SCD_APP_COMMON_H +#include + struct app_ctx_s { int initialized; /* The application has been initialied and the function pointers may be used. Note that for @@ -80,6 +82,11 @@ struct app_ctx_s { int app_select_openpgp (app_t app); int app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp); #else +/*-- app-help.c --*/ +gpg_error_t app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip); +size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff); + + /*-- app.c --*/ app_t select_application (ctrl_t ctrl, int slot, const char *name); void release_application (app_t app); diff --git a/scd/app-dinsig.c b/scd/app-dinsig.c index 4b5b517eb..38fbc79ee 100644 --- a/scd/app-dinsig.c +++ b/scd/app-dinsig.c @@ -1,5 +1,5 @@ /* app-dinsig.c - The DINSIG (DIN V 66291-1) card application. - * Copyright (C) 2004 Free Software Foundation, Inc. + * Copyright (C) 2002, 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -65,9 +65,6 @@ The letters in brackets indicate optional or mandatory files: The first for card terminals under full control and the second for "business" card terminals. - - FIXME: Needs a lot more explanation. - */ @@ -85,17 +82,317 @@ #include "iso7816.h" #include "app-common.h" +#include "tlv.h" + + +static int +do_learn_status (app_t app, ctrl_t ctrl) +{ + gpg_error_t err; + char ct_buf[100], id_buf[100]; + char hexkeygrip[41]; + size_t len, certoff; + unsigned char *der; + size_t derlen; + ksba_cert_t cert; + int fid; + + /* Return the certificate of the card holder. */ + fid = 0xC000; + len = app_help_read_length_of_cert (app->slot, fid, &certoff); + if (!len) + return 0; /* Card has not been personalized. */ + + sprintf (ct_buf, "%d", 101); + sprintf (id_buf, "DINSIG.%04X", fid); + send_status_info (ctrl, "CERTINFO", + ct_buf, strlen (ct_buf), + id_buf, strlen (id_buf), + NULL, (size_t)0); + + /* Now we need to read the certificate, so that we can get the + public key out of it. */ + err = iso7816_read_binary (app->slot, certoff, len-certoff, &der, &derlen); + if (err) + { + log_info ("error reading entire certificate from FID 0x%04X: %s\n", + fid, gpg_strerror (err)); + return 0; + } + + err = ksba_cert_new (&cert); + if (err) + { + xfree (der); + return err; + } + err = ksba_cert_init_from_mem (cert, der, derlen); + xfree (der); der = NULL; + if (err) + { + log_error ("failed to parse the certificate at FID 0x%04X: %s\n", + fid, gpg_strerror (err)); + ksba_cert_release (cert); + return err; + } + err = app_help_get_keygrip_string (cert, hexkeygrip); + if (err) + { + log_error ("failed to calculate the keygrip for FID 0x%04X\n", fid); + ksba_cert_release (cert); + return gpg_error (GPG_ERR_CARD); + } + ksba_cert_release (cert); + + sprintf (id_buf, "DINSIG.%04X", fid); + send_status_info (ctrl, "KEYPAIRINFO", + hexkeygrip, 40, + id_buf, strlen (id_buf), + NULL, (size_t)0); + return 0; +} + + +/* Read the certificate with id CERTID (as returned by learn_status in + the CERTINFO status lines) and return it in the freshly allocated + buffer put into CERT and the length of the certificate put into + CERTLEN. + FIXME: This needs some cleanups and caching with do_learn_status. +*/ static int -do_learn_status (APP app, CTRL ctrl) +do_readcert (app_t app, const char *certid, + unsigned char **cert, size_t *certlen) { + int fid; + gpg_error_t err; + unsigned char *buffer; + const unsigned char *p; + size_t buflen, n; + int class, tag, constructed, ndef; + size_t totobjlen, objlen, hdrlen; + int rootca = 0; + + *cert = NULL; + *certlen = 0; + if (strncmp (certid, "DINSIG.", 7) ) + return gpg_error (GPG_ERR_INV_ID); + certid += 7; + if (!hexdigitp (certid) || !hexdigitp (certid+1) + || !hexdigitp (certid+2) || !hexdigitp (certid+3) + || certid[4]) + return gpg_error (GPG_ERR_INV_ID); + fid = xtoi_4 (certid); + if (fid != 0xC000 ) + return gpg_error (GPG_ERR_NOT_FOUND); + + /* Read the entire file. fixme: This could be optimized by first + reading the header to figure out how long the certificate + actually is. */ + err = iso7816_select_file (app->slot, fid, 0, NULL, NULL); + if (err) + { + log_error ("error selecting FID 0x%04X: %s\n", fid, gpg_strerror (err)); + return err; + } + + err = iso7816_read_binary (app->slot, 0, 0, &buffer, &buflen); + if (err) + { + log_error ("error reading certificate from FID 0x%04X: %s\n", + fid, gpg_strerror (err)); + return err; + } + + if (!buflen || *buffer == 0xff) + { + log_info ("no certificate contained in FID 0x%04X\n", fid); + err = gpg_error (GPG_ERR_NOT_FOUND); + goto leave; + } + + /* Now figure something out about the object. */ + p = buffer; + n = buflen; + err = parse_ber_header (&p, &n, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (err) + goto leave; + if ( class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed ) + ; + else if ( class == CLASS_UNIVERSAL && tag == TAG_SET && constructed ) + rootca = 1; + else + return gpg_error (GPG_ERR_INV_OBJ); + totobjlen = objlen + hdrlen; + assert (totobjlen <= buflen); + + err = parse_ber_header (&p, &n, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (err) + goto leave; + + if (rootca) + ; + else if (class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !constructed) + { + const unsigned char *save_p; + + /* The certificate seems to be contained in a userCertificate + container. Skip this and assume the following sequence is + the certificate. */ + if (n < objlen) + { + err = gpg_error (GPG_ERR_INV_OBJ); + goto leave; + } + p += objlen; + n -= objlen; + save_p = p; + err = parse_ber_header (&p, &n, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (err) + goto leave; + if ( !(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed) ) + return gpg_error (GPG_ERR_INV_OBJ); + totobjlen = objlen + hdrlen; + assert (save_p + totobjlen <= buffer + buflen); + memmove (buffer, save_p, totobjlen); + } + + *cert = buffer; + buffer = NULL; + *certlen = totobjlen; + + leave: + xfree (buffer); + return err; +} + + +/* Verify the PIN if required. */ +static int +verify_pin (app_t app, + int (pincb)(void*, const char *, char **), + void *pincb_arg) +{ + if (!app->did_chv1 || app->force_chv1 ) + { + char *pinvalue; + int rc; + + rc = pincb (pincb_arg, "PIN", &pinvalue); + if (rc) + { + log_info ("PIN callback returned error: %s\n", gpg_strerror (rc)); + return rc; + } + + /* We require the PIN to be at least 6 and at max 8 bytes. + According to the specs, this should all be ASCII but we don't + check this. */ + if (strlen (pinvalue) < 6) + { + log_error ("PIN is too short; minimum length is 6\n"); + xfree (pinvalue); + return gpg_error (GPG_ERR_BAD_PIN); + } + else if (strlen (pinvalue) > 8) + { + log_error ("PIN is too large; maximum length is 8\n"); + xfree (pinvalue); + return gpg_error (GPG_ERR_BAD_PIN); + } + + rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue)); + if (rc) + { + log_error ("verify PIN failed\n"); + xfree (pinvalue); + return rc; + } + app->did_chv1 = 1; + xfree (pinvalue); + } + return 0; } +/* Create the signature and return the allocated result in OUTDATA. + If a PIN is required the PINCB will be used to ask for the PIN; + that callback should return the PIN in an allocated buffer and + store that in the 3rd argument. */ +static int +do_sign (app_t app, const char *keyidstr, int hashalgo, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen ) +{ + static unsigned char sha1_prefix[15] = /* Object ID is 1.3.14.3.2.26 */ + { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, + 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 }; + static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */ + { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03, + 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 }; + int rc; + int fid; + unsigned char data[35]; /* Must be large enough for a SHA-1 digest + + the largest OID _prefix above. */ + + if (!keyidstr || !*keyidstr) + return gpg_error (GPG_ERR_INV_VALUE); + if (indatalen != 20 && indatalen != 16 && indatalen != 35) + return gpg_error (GPG_ERR_INV_VALUE); + + /* Check that the provided ID is vaid. This is not really needed + but we do it to to enforce correct usage by the caller. */ + if (strncmp (keyidstr, "DINSIG.", 7) ) + return gpg_error (GPG_ERR_INV_ID); + keyidstr += 7; + if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1) + || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3) + || keyidstr[4]) + return gpg_error (GPG_ERR_INV_ID); + fid = xtoi_4 (keyidstr); + if (fid != 0xC000) + return gpg_error (GPG_ERR_NOT_FOUND); + + /* Prepare the DER object from INDATA. */ + if (indatalen == 35) + { + /* Alright, the caller was so kind to send us an already + prepared DER object. Check that it is what we want and that + it matches the hash algorithm. */ + if (hashalgo == GCRY_MD_SHA1 && !memcmp (indata, sha1_prefix, 15)) + ; + else if (hashalgo == GCRY_MD_RMD160 && !memcmp (indata, rmd160_prefix,15)) + ; + else + return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + memcpy (data, indata, indatalen); + } + else + { + if (hashalgo == GCRY_MD_SHA1) + memcpy (data, sha1_prefix, 15); + else if (hashalgo == GCRY_MD_RMD160) + memcpy (data, rmd160_prefix, 15); + else + return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + memcpy (data+15, indata, indatalen); + } + + rc = verify_pin (app, pincb, pincb_arg); + if (!rc) + rc = iso7816_compute_ds (app->slot, data, 35, outdata, outdatalen); + return rc; +} + /* Select the DINSIG application on the card in SLOT. This function @@ -113,17 +410,18 @@ app_select_dinsig (APP app) app->apptype = "DINSIG"; app->fnc.learn_status = do_learn_status; + app->fnc.readcert = do_readcert; app->fnc.getattr = NULL; app->fnc.setattr = NULL; app->fnc.genkey = NULL; - app->fnc.sign = NULL; + app->fnc.sign = do_sign; app->fnc.auth = NULL; app->fnc.decipher = NULL; app->fnc.change_pin = NULL; app->fnc.check_pin = NULL; + + app->force_chv1 = 1; } return rc; } - - diff --git a/scd/app-help.c b/scd/app-help.c new file mode 100644 index 000000000..c6695635f --- /dev/null +++ b/scd/app-help.c @@ -0,0 +1,157 @@ +/* app-help.c - Application helper functions + * Copyright (C) 2004 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 "scdaemon.h" +#include "app-common.h" +#include "iso7816.h" +#include "tlv.h" + +/* Return the KEYGRIP for the certificate CERT as an hex encoded + string in the user provided buffer HEXKEYGRIP which must be of at + least 41 bytes. */ +gpg_error_t +app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip) +{ + gpg_error_t err; + gcry_sexp_t s_pkey; + ksba_sexp_t p; + size_t n; + unsigned char array[20]; + int i; + + p = ksba_cert_get_public_key (cert); + if (!p) + return gpg_error (GPG_ERR_BUG); + n = gcry_sexp_canon_len (p, 0, NULL, NULL); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + err = gcry_sexp_sscan (&s_pkey, NULL, p, n); + xfree (p); + if (err) + return err; /* Can't parse that S-expression. */ + if (!gcry_pk_get_keygrip (s_pkey, array)) + { + gcry_sexp_release (s_pkey); + return gpg_error (GPG_ERR_GENERAL); /* Failed to calculate the keygrip.*/ + } + gcry_sexp_release (s_pkey); + + for (i=0; i < 20; i++) + sprintf (hexkeygrip+i*2, "%02X", array[i]); + + return 0; +} + + + +/* Given the SLOT and the File ID FID, return the length of the + certificate contained in that file. Returns 0 if the file does not + exists or does not contain a certificate. If R_CERTOFF is not + NULL, the length the header will be stored at this address; thus to + parse the X.509 certificate a read should start at that offset. + + On success the file is still selected. +*/ +size_t +app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff) +{ + gpg_error_t err; + unsigned char *buffer; + const unsigned char *p; + size_t buflen, n; + int class, tag, constructed, ndef; + size_t resultlen, objlen, hdrlen; + + err = iso7816_select_file (slot, fid, 0, NULL, NULL); + if (err) + { + log_info ("error selecting FID 0x%04X: %s\n", fid, gpg_strerror (err)); + return 0; + } + + err = iso7816_read_binary (slot, 0, 32, &buffer, &buflen); + if (err) + { + log_info ("error reading certificate from FID 0x%04X: %s\n", + fid, gpg_strerror (err)); + return 0; + } + + if (!buflen || *buffer == 0xff) + { + log_info ("no certificate contained in FID 0x%04X\n", fid); + xfree (buffer); + return 0; + } + + p = buffer; + n = buflen; + err = parse_ber_header (&p, &n, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (err) + { + log_info ("error parsing certificate in FID 0x%04X: %s\n", + fid, gpg_strerror (err)); + xfree (buffer); + return 0; + } + + /* All certificates should commence with a SEQUENCE except for the + special ROOT CA which are enclosed in a SET. */ + if ( !(class == CLASS_UNIVERSAL && constructed + && (tag == TAG_SEQUENCE || tag == TAG_SET))) + { + log_info ("contents of FID 0x%04X does not look like a certificate\n", + fid); + return 0; + } + + resultlen = objlen + hdrlen; + if (r_certoff) + { + /* The callers want the offset to the actual certificate. */ + *r_certoff = hdrlen; + + err = parse_ber_header (&p, &n, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (err) + return 0; + + if (class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !constructed) + { + /* The certificate seems to be contained in a + userCertificate container. Assume the following sequence + is the certificate. */ + *r_certoff += hdrlen + objlen; + if (*r_certoff > resultlen) + return 0; /* That should never happen. */ + } + } + + return resultlen; +} + + diff --git a/scd/app-nks.c b/scd/app-nks.c index a4b6e3a15..e69b59879 100644 --- a/scd/app-nks.c +++ b/scd/app-nks.c @@ -53,68 +53,6 @@ static struct { -/* Given the slot and the File Id FID, return the length of the - certificate contained in that file. Returns 0 if the file does not - exists or does not contain a certificate. */ -static size_t -get_length_of_cert (int slot, int fid) -{ - gpg_error_t err; - unsigned char *buffer; - const unsigned char *p; - size_t buflen, n; - int class, tag, constructed, ndef; - size_t objlen, hdrlen; - - err = iso7816_select_file (slot, fid, 0, NULL, NULL); - if (err) - { - log_info ("error selecting FID 0x%04X: %s\n", fid, gpg_strerror (err)); - return 0; - } - - err = iso7816_read_binary (slot, 0, 32, &buffer, &buflen); - if (err) - { - log_info ("error reading certificate from FID 0x%04X: %s\n", - fid, gpg_strerror (err)); - return 0; - } - - if (!buflen || *buffer == 0xff) - { - log_info ("no certificate contained in FID 0x%04X\n", fid); - xfree (buffer); - return 0; - } - - p = buffer; - n = buflen; - err = parse_ber_header (&p, &n, &class, &tag, &constructed, - &ndef, &objlen, &hdrlen); - if (err) - { - log_info ("error parsing certificate in FID 0x%04X: %s\n", - fid, gpg_strerror (err)); - xfree (buffer); - return 0; - } - - /* All certificates should commence with a SEQUENCE expect fro the - special ROOT CA which are enclosed in a SET. */ - if ( !(class == CLASS_UNIVERSAL && constructed - && (tag == TAG_SEQUENCE || tag == TAG_SET))) - { - log_info ("contents of FID 0x%04X does not look like a certificate\n", - fid); - return 0; - } - - return objlen + hdrlen; -} - - - /* Read the file with FID, assume it contains a public key and return its keygrip in the caller provided 41 byte buffer R_GRIPSTR. */ static gpg_error_t @@ -191,8 +129,10 @@ do_learn_status (APP app, CTRL ctrl) { if (filelist[i].certtype) { - size_t len = get_length_of_cert (app->slot, filelist[i].fid); + size_t len; + len = app_help_read_length_of_cert (app->slot, + filelist[i].fid, NULL); if (len) { /* FIXME: We should store the length in the application's diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 7782b8e1c..af5f5a27f 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -1201,7 +1201,7 @@ app_select_openpgp (APP app) /* The OpenPGP card returns the serial number as part of the AID; because we prefer to use OpenPGP serial numbers, we - repalce a possibly already set one from a EF.GDO with this + replace a possibly already set one from a EF.GDO with this one. Note, that for current OpenPGP cards, no EF.GDO exists and thus it won't matter at all. */ rc = iso7816_get_data (slot, 0x004F, &buffer, &buflen); diff --git a/scd/card.c b/scd/card.c index 8366dcb1c..9ec2a52c5 100644 --- a/scd/card.c +++ b/scd/card.c @@ -325,8 +325,6 @@ card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp) } if (card->p15card) card_p15_bind (card); - else - card_dinsig_bind (card); card->fnc.initialized = 1; } diff --git a/scd/iso7816.c b/scd/iso7816.c index d7d3c126b..24361d148 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -488,6 +488,8 @@ iso7816_read_binary (int slot, size_t offset, size_t nmax, int sw; unsigned char *buffer; size_t bufferlen; + int read_all = !nmax; + size_t n; if (!result || !resultlen) return gpg_error (GPG_ERR_INV_VALUE); @@ -496,18 +498,22 @@ iso7816_read_binary (int slot, size_t offset, size_t nmax, /* We can only encode 15 bits in p0,p1 to indicate an offset. Thus we check for this limit. */ - if (offset > 32767 || nmax > 254) + if (offset > 32767) return gpg_error (GPG_ERR_INV_VALUE); do { buffer = NULL; bufferlen = 0; - /* Fixme: Either the ccid driver of the TCOS cards have problems + /* Fixme: Either the ccid driver or the TCOS cards have problems with an Le of 0. */ + if (read_all || nmax > 254) + n = 254; + else + n = nmax; sw = apdu_send_le (slot, 0x00, CMD_READ_BINARY, - ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL, - nmax? nmax : 254, &buffer, &bufferlen); + ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL, + n, &buffer, &bufferlen); if (sw != SW_SUCCESS && sw != SW_EOF_REACHED) { @@ -545,8 +551,12 @@ iso7816_read_binary (int slot, size_t offset, size_t nmax, if (offset > 32767) break; /* We simply truncate the result for too large files. */ + if (nmax > bufferlen) + nmax -= bufferlen; + else + nmax = 0; } - while (!nmax && sw != SW_EOF_REACHED); + while ((read_all && sw != SW_EOF_REACHED) || (!read_all && nmax)); return 0; } diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 5665237ca..93746ec35 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -103,8 +103,8 @@ static ARGPARSE_OPTS opts[] = { { 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", 2, N_("|N|connect to reader at port N")}, - { octapiDriver, "ctapi-driver", 2, N_("NAME|use NAME as ct-API driver")}, - { opcscDriver, "pcsc-driver", 2, N_("NAME|use NAME as PC/SC driver")}, + { octapiDriver, "ctapi-driver", 2, N_("|NAME|use NAME as ct-API driver")}, + { opcscDriver, "pcsc-driver", 2, N_("|NAME|use NAME as PC/SC driver")}, { oDisableCCID, "disable-ccid", 0, #ifdef HAVE_LIBUSB N_("do not use the internal CCID driver") @@ -126,6 +126,9 @@ static ARGPARSE_OPTS opts[] = { }; +#define DEFAULT_PCSC_DRIVER "libpcsclite.so" + + static volatile int caught_fatal_sig = 0; /* It is possible that we are currently running under setuid permissions */ @@ -302,6 +305,7 @@ main (int argc, char **argv ) char *logfile = NULL; int debug_wait = 0; int gpgconf_list = 0; + const char *config_filename = NULL; set_strusage (my_strusage); gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); @@ -334,7 +338,7 @@ main (int argc, char **argv ) may_coredump = disable_core_dumps (); /* Set default options. */ - opt.pcsc_driver = "libpcsclite.so"; + opt.pcsc_driver = DEFAULT_PCSC_DRIVER; shell = getenv ("SHELL"); @@ -466,7 +470,8 @@ main (int argc, char **argv ) { fclose( configfp ); configfp = NULL; - xfree(configname); + /* Keep a copy of the config name for use by --gpgconf-list. */ + config_filename = configname; configname = NULL; goto next_pass; } @@ -507,19 +512,49 @@ main (int argc, char **argv ) if (gpgconf_list) { /* List options and default values in the GPG Conf format. */ - char *filename; - filename = make_filename (opt.homedir, "scdaemon.conf", NULL); - printf ("gpgconf-scdaemon.conf:\"%s\n", filename); - xfree (filename); - - printf ("verbose:\n" - "quiet:\n" - "debug-level:none\n" - "log-file:\n" - "force:\n" - "faked-system-time:\n" - "no-greeting:\n"); + /* The following list is taken from gnupg/tools/gpgconf-comp.c. */ + /* Option flags. YOU MUST NOT CHANGE THE NUMBERS OF THE EXISTING + FLAGS, AS THEY ARE PART OF THE EXTERNAL INTERFACE. */ +#define GC_OPT_FLAG_NONE 0UL + /* The RUNTIME flag for an option indicates that the option can be + changed at runtime. */ +#define GC_OPT_FLAG_RUNTIME (1UL << 3) + /* The DEFAULT flag for an option indicates that the option has a + default value. */ +#define GC_OPT_FLAG_DEFAULT (1UL << 4) + /* The DEF_DESC flag for an option indicates that the option has a + default, which is described by the value of the default field. */ +#define GC_OPT_FLAG_DEF_DESC (1UL << 5) + /* The NO_ARG_DESC flag for an option indicates that the argument has + a default, which is described by the value of the ARGDEF field. */ +#define GC_OPT_FLAG_NO_ARG_DESC (1UL << 6) + + printf ("gpgconf-scdaemon.conf:%lu:\"%s\"\n", + GC_OPT_FLAG_DEFAULT, + config_filename?config_filename:"/dev/null"); + + printf ("verbose:%lu:\n" + "quiet:%lu:\n" + "debug-level:%lu:\"none\":\n" + "log-file:%lu:\n", + GC_OPT_FLAG_NONE, + GC_OPT_FLAG_NONE, + GC_OPT_FLAG_DEFAULT, + GC_OPT_FLAG_NONE ); + + printf ("reader-port:%lu:\n", GC_OPT_FLAG_NONE ); + printf ("ctapi-driver:%lu:\n", GC_OPT_FLAG_NONE ); + printf ("pcsc-driver:%lu:\"%s\":\n", + GC_OPT_FLAG_DEFAULT, DEFAULT_PCSC_DRIVER ); +#ifdef HAVE_LIBUSB + printf ("disable-ccid:%lu:\n", GC_OPT_FLAG_NONE ); +#endif +#ifdef HAVE_LIBUSB + printf ("disable-opensc:%lu:\n", GC_OPT_FLAG_NONE ); +#endif + printf ("allow-admin:%lu:\n", GC_OPT_FLAG_NONE ); + scd_exit (0); } -- cgit From d714e81e58f835f7a85e04049d19981ceeb426fc Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 17 Mar 2004 13:01:16 +0000 Subject: * gpg-agent.c (main) : Fixed default value quoting. * scdaemon.c (main) : Fixed default value quoting. * gpgsm.c (main) : Fixed default value quoting. --- agent/ChangeLog | 4 ++++ agent/gpg-agent.c | 2 +- scd/ChangeLog | 4 ++++ scd/scdaemon.c | 4 ++-- sm/ChangeLog | 4 ++++ sm/gpgsm.c | 4 ++-- 6 files changed, 17 insertions(+), 5 deletions(-) (limited to 'scd/scdaemon.c') diff --git a/agent/ChangeLog b/agent/ChangeLog index 7516cddb8..484a5ffd7 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,7 @@ +2004-03-17 Werner Koch + + * gpg-agent.c (main) : Fixed default value quoting. + 2004-03-16 Werner Koch * gpg-agent.c (parse_rereadable_options): Use the new diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 12bdb85ea..2051d9080 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -612,7 +612,7 @@ main (int argc, char **argv ) printf ("verbose:%lu:\n" "quiet:%lu:\n" - "debug-level:%lu:\"none\":\n" + "debug-level:%lu:\"none:\n" "log-file:%lu:\n", GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME, GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME, diff --git a/scd/ChangeLog b/scd/ChangeLog index 679f867fe..9b2ddf79c 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,7 @@ +2004-03-17 Werner Koch + + * scdaemon.c (main) : Fixed default value quoting. + 2004-03-16 Werner Koch * app-dinsig.c: Implemented. Based on app-nks.c and card-dinsig.c diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 93746ec35..9f1e409e4 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -530,13 +530,13 @@ main (int argc, char **argv ) a default, which is described by the value of the ARGDEF field. */ #define GC_OPT_FLAG_NO_ARG_DESC (1UL << 6) - printf ("gpgconf-scdaemon.conf:%lu:\"%s\"\n", + printf ("gpgconf-scdaemon.conf:%lu:\"%s\n", GC_OPT_FLAG_DEFAULT, config_filename?config_filename:"/dev/null"); printf ("verbose:%lu:\n" "quiet:%lu:\n" - "debug-level:%lu:\"none\":\n" + "debug-level:%lu:\"none:\n" "log-file:%lu:\n", GC_OPT_FLAG_NONE, GC_OPT_FLAG_NONE, diff --git a/sm/ChangeLog b/sm/ChangeLog index 210448643..08793b810 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,3 +1,7 @@ +2004-03-17 Werner Koch + + * gpgsm.c (main) : Fixed default value quoting. + 2004-03-16 Werner Koch * gpgsm.c (main): Implemented --gpgconf-list. diff --git a/sm/gpgsm.c b/sm/gpgsm.c index 6b8a2ea49..761aefcee 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -1279,13 +1279,13 @@ main ( int argc, char **argv) a default, which is described by the value of the ARGDEF field. */ #define GC_OPT_FLAG_NO_ARG_DESC (1UL << 6) - printf ("gpgconf-gpgsm.conf:%lu:\"%s\"\n", + printf ("gpgconf-gpgsm.conf:%lu:\"%s\n", GC_OPT_FLAG_DEFAULT, config_filename?config_filename:"/dev/null"); printf ("verbose:%lu:\n" "quiet:%lu:\n" - "debug-level:%lu:\"none\":\n" + "debug-level:%lu:\"none:\n" "log-file:%lu:\n", GC_OPT_FLAG_NONE, GC_OPT_FLAG_NONE, -- cgit From 0c36e10633eb9dee9b44a2f773c8242c5cb1ea61 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 23 Mar 2004 12:33:20 +0000 Subject: (main) : Fixed output for pcsc_driver. --- scd/ChangeLog | 7 +++++++ scd/scdaemon.c | 2 +- scd/tlv.c | 17 +++-------------- 3 files changed, 11 insertions(+), 15 deletions(-) (limited to 'scd/scdaemon.c') diff --git a/scd/ChangeLog b/scd/ChangeLog index 9b2ddf79c..895aae966 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,5 +1,12 @@ +2004-03-23 Werner Koch + + * scdaemon.c (main) : Fixed output for pcsc_driver. + 2004-03-17 Werner Koch + * tlv.c (parse_ber_header): Do not check for tag overflow - it + does not make sense. Simplified the check for length overflow. + * scdaemon.c (main) : Fixed default value quoting. 2004-03-16 Werner Koch diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 9f1e409e4..955724b3f 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -545,7 +545,7 @@ main (int argc, char **argv ) printf ("reader-port:%lu:\n", GC_OPT_FLAG_NONE ); printf ("ctapi-driver:%lu:\n", GC_OPT_FLAG_NONE ); - printf ("pcsc-driver:%lu:\"%s\":\n", + printf ("pcsc-driver:%lu:\"%s:\n", GC_OPT_FLAG_DEFAULT, DEFAULT_PCSC_DRIVER ); #ifdef HAVE_LIBUSB printf ("disable-ccid:%lu:\n", GC_OPT_FLAG_NONE ); diff --git a/scd/tlv.c b/scd/tlv.c index dbcd24546..5b9d0d6b9 100644 --- a/scd/tlv.c +++ b/scd/tlv.c @@ -157,12 +157,6 @@ parse_ber_header (unsigned char const **buffer, size_t *size, tag = 0; do { - /* Simple check against overflow. We limit our maximim tag - value more than needed but that should not be a problem - because I have nver encountered such large value. We - assume at least 32 bit integers. */ - if (tag > (1 << 24)) - return gpg_error (GPG_ERR_TOO_LARGE); tag <<= 7; if (!length) return gpg_error (GPG_ERR_EOF); @@ -190,16 +184,11 @@ parse_ber_header (unsigned char const **buffer, size_t *size, unsigned long len = 0; int count = c & 0x7f; + if (count > sizeof (len) || count > sizeof (size_t)) + return gpg_error (GPG_ERR_BAD_BER); + for (; count; count--) { - /* Simple check against overflow. We limit our maximim - length more than needed but that should not be a problem - because I have never encountered such large value and - well they are managed in memory and thus we would run - into memory problems anyway. We assume at least 32 bit - integers. */ - if (len > (1 << 24)) - return gpg_error (GPG_ERR_TOO_LARGE); len <<= 8; if (!length) return gpg_error (GPG_ERR_EOF); -- cgit From d84d632583c1c61180d40ce9b91f185160ab02d6 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 6 Apr 2004 10:01:04 +0000 Subject: * configure.ac: Require libgcrypt 1.1.94. Introduce PACKAGE_GT and set it to gnupg2. * gpg-agent.c (main): Use new libgcrypt thread library register scheme. * Makevars (DOMAIN): Init from PACKAGE_GT --- ChangeLog | 3 +- NEWS | 3 + agent/gpg-agent.c | 8 +- agent/protect-tool.c | 6 +- configure.ac | 6 +- g10/g10.c | 6 +- g10/gpgv.c | 6 +- g10/passphrase.c | 12 +- kbx/kbxutil.c | 16 +- po/ChangeLog | 4 + po/LINGUAS | 19 +- po/LINUGAS | 4 - po/Makevars | 2 +- po/POTFILES.in | 3 + po/de.po | 878 +++++++++++++++++++++++++++++++-------------------- scd/scdaemon.c | 6 +- sm/certdump.c | 6 +- sm/gpgsm.c | 20 +- sm/import.c | 2 +- tools/gpgconf.c | 6 +- 20 files changed, 609 insertions(+), 407 deletions(-) delete mode 100644 po/LINUGAS (limited to 'scd/scdaemon.c') diff --git a/ChangeLog b/ChangeLog index 8686c54ea..11c4093a1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ 2004-04-06 Werner Koch * configure.ac: Require libgcrypt 1.1.94. + Introduce PACKAGE_GT and set it to gnupg2. 2004-03-23 Werner Koch @@ -414,7 +415,7 @@ * configure.ac (HAVE_JNLIB_LOGGING): always define it. - Copyright 2001, 2002 Free Software Foundation, Inc. + Copyright 2001, 2002, 2004 Free Software Foundation, Inc. This file is free software; as a special exception the author gives unlimited permission to copy and/or distribute it, with or without diff --git a/NEWS b/NEWS index b118f9c8b..a4c909fa3 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,9 @@ Noteworthy changes in version 1.9.7 * Include the smimeCapabilities attribute with signed messages. + * Now uses the gettext domain "gnupg2" to avoid conflicts with gnupg + versions < 1.9. + Noteworthy changes in version 1.9.6 (2004-03-06) ------------------------------------------------ diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index dd35bfcdf..0fc1bb8bf 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -197,12 +197,12 @@ static void i18n_init (void) { #ifdef USE_SIMPLE_GETTEXT - set_gettext_file( PACKAGE ); + set_gettext_file( PACKAGE_GT ); #else #ifdef ENABLE_NLS setlocale (LC_ALL, ""); - bindtextdomain (PACKAGE, LOCALEDIR); - textdomain (PACKAGE); + bindtextdomain (PACKAGE_GT, LOCALEDIR); + textdomain (PACKAGE_GT); #endif #endif } @@ -649,7 +649,7 @@ main (int argc, char **argv ) easier to switch only once instead of for every message and it actually helps when more then one thread is active (avoids an extra copy step). */ - bind_textdomain_codeset (PACKAGE, "UTF-8"); + bind_textdomain_codeset (PACKAGE_GT, "UTF-8"); #endif /* now start with logging to a file if this is desired */ diff --git a/agent/protect-tool.c b/agent/protect-tool.c index 1bc7144be..c62c649ad 100644 --- a/agent/protect-tool.c +++ b/agent/protect-tool.c @@ -141,12 +141,12 @@ static void i18n_init (void) { #ifdef USE_SIMPLE_GETTEXT - set_gettext_file( PACKAGE ); + set_gettext_file( PACKAGE_GT ); #else #ifdef ENABLE_NLS setlocale (LC_ALL, ""); - bindtextdomain (PACKAGE, LOCALEDIR); - textdomain (PACKAGE); + bindtextdomain (PACKAGE_GT, LOCALEDIR); + textdomain (PACKAGE_GT); #endif #endif } diff --git a/configure.ac b/configure.ac index bd64976be..8c167f9e2 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac - for GnuPG 1.9 # Copyright (C) 1998, 1999, 2000, 2001, 2002, -# 2003 Free Software Foundation, Inc. +# 2003, 2004 Free Software Foundation, Inc. # # This file is part of GnuPG. # @@ -43,6 +43,7 @@ NEED_OPENSC_VERSION=0.8.0 PACKAGE=$PACKAGE_NAME +PACKAGE_GT=${PACKAGE_NAME}2 VERSION=$PACKAGE_VERSION AC_CONFIG_AUX_DIR(scripts) @@ -68,8 +69,11 @@ GNUPG_BUILD_PROGRAM(scdaemon, yes) AC_SUBST(PACKAGE) +AC_SUBST(PACKAGE_GT) AC_SUBST(VERSION) AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of this package]) +AC_DEFINE_UNQUOTED(PACKAGE_GT, "$PACKAGE_GT", + [Name of this package for gettext]) AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version of this package]) AC_DEFINE_UNQUOTED(PACKAGE_BUGREPORT, "$PACKAGE_BUGREPORT", [Bug report address]) diff --git a/g10/g10.c b/g10/g10.c index 2fbc7e625..006e88ad2 100644 --- a/g10/g10.c +++ b/g10/g10.c @@ -783,12 +783,12 @@ static void i18n_init(void) { #ifdef USE_SIMPLE_GETTEXT - set_gettext_file( PACKAGE ); + set_gettext_file( PACKAGE_GT ); #else #ifdef ENABLE_NLS setlocale( LC_ALL, "" ); - bindtextdomain( PACKAGE, LOCALEDIR ); - textdomain( PACKAGE ); + bindtextdomain( PACKAGE_GT, LOCALEDIR ); + textdomain( PACKAGE_GT ); #endif #endif } diff --git a/g10/gpgv.c b/g10/gpgv.c index d020bda62..9bd954c6f 100644 --- a/g10/gpgv.c +++ b/g10/gpgv.c @@ -116,7 +116,7 @@ static void i18n_init(void) { #ifdef USE_SIMPLE_GETTEXT - set_gettext_file( PACKAGE ); + set_gettext_file( PACKAGE_GT ); #else #ifdef ENABLE_NLS #ifdef HAVE_LC_MESSAGES @@ -125,8 +125,8 @@ i18n_init(void) #else setlocale( LC_ALL, "" ); #endif - bindtextdomain( PACKAGE, LOCALEDIR ); - textdomain( PACKAGE ); + bindtextdomain( PACKAGE_GT, LOCALEDIR ); + textdomain( PACKAGE_GT ); #endif #endif } diff --git a/g10/passphrase.c b/g10/passphrase.c index 27ffcad97..986070a16 100644 --- a/g10/passphrase.c +++ b/g10/passphrase.c @@ -649,8 +649,8 @@ agent_get_passphrase ( u32 *keyid, int mode, const char *tryagain_text, } #ifdef ENABLE_NLS - /* The Assuan agent protol requires us to trasnmit utf-8 strings */ - orig_codeset = bind_textdomain_codeset (PACKAGE, NULL); + /* The Assuan agent protocol requires us to transmit utf-8 strings */ + orig_codeset = bind_textdomain_codeset (PACKAGE_GT, NULL); #ifdef HAVE_LANGINFO_CODESET if (!orig_codeset) orig_codeset = nl_langinfo (CODESET); @@ -658,7 +658,7 @@ agent_get_passphrase ( u32 *keyid, int mode, const char *tryagain_text, if (orig_codeset) { /* We only switch when we are able to restore the codeset later. */ orig_codeset = xstrdup (orig_codeset); - if (!bind_textdomain_codeset (PACKAGE, "utf-8")) + if (!bind_textdomain_codeset (PACKAGE_GT, "utf-8")) orig_codeset = NULL; } #endif @@ -765,7 +765,7 @@ agent_get_passphrase ( u32 *keyid, int mode, const char *tryagain_text, free_public_key( pk ); #ifdef ENABLE_NLS if (orig_codeset) - bind_textdomain_codeset (PACKAGE, orig_codeset); + bind_textdomain_codeset (PACKAGE_GT, orig_codeset); #endif xfree (orig_codeset); return pw; @@ -857,7 +857,7 @@ agent_get_passphrase ( u32 *keyid, int mode, const char *tryagain_text, free_public_key( pk ); #ifdef ENABLE_NLS if (orig_codeset) - bind_textdomain_codeset (PACKAGE, orig_codeset); + bind_textdomain_codeset (PACKAGE_GT, orig_codeset); #endif xfree (orig_codeset); return pw; @@ -880,7 +880,7 @@ agent_get_passphrase ( u32 *keyid, int mode, const char *tryagain_text, failure: #ifdef ENABLE_NLS if (orig_codeset) - bind_textdomain_codeset (PACKAGE, orig_codeset); + bind_textdomain_codeset (PACKAGE_GT, orig_codeset); #endif xfree (atext); if ( fd != -1 ) diff --git a/kbx/kbxutil.c b/kbx/kbxutil.c index abca4faa9..76678619c 100644 --- a/kbx/kbxutil.c +++ b/kbx/kbxutil.c @@ -117,20 +117,20 @@ my_strusage( int level ) static void i18n_init(void) { - #ifdef USE_SIMPLE_GETTEXT - set_gettext_file( PACKAGE ); - #else - #ifdef ENABLE_NLS +#ifdef USE_SIMPLE_GETTEXT + set_gettext_file( PACKAGE_GT ); +#else +#ifdef ENABLE_NLS #ifdef HAVE_LC_MESSAGES setlocale( LC_TIME, "" ); setlocale( LC_MESSAGES, "" ); #else setlocale( LC_ALL, "" ); #endif - bindtextdomain( PACKAGE, LOCALEDIR ); - textdomain( PACKAGE ); - #endif - #endif + bindtextdomain( PACKAGE_GT, LOCALEDIR ); + textdomain( PACKAGE_GT ); +#endif +#endif } diff --git a/po/ChangeLog b/po/ChangeLog index 682f086f3..97e68c730 100644 --- a/po/ChangeLog +++ b/po/ChangeLog @@ -1,3 +1,7 @@ +2004-04-06 Werner Koch + + * Makevars (DOMAIN): Init from PACKAGE_GT + 2003-12-09 Werner Koch * Makevars (MSGID_BUGS_ADDRESS): New. diff --git a/po/LINGUAS b/po/LINGUAS index b418cf26d..2d1cafec8 100644 --- a/po/LINGUAS +++ b/po/LINGUAS @@ -1,18 +1,5 @@ # Set of available languages. -# we have disabled most of them for now. -en@quot en@boldquot -#ca cs -#da de -#eo el es et -#fi fr -#gl -#hu -#id it -#ja -#nl -#pl -#pt_BR pt -#sk sv -#tr -#zh_TW" + + + diff --git a/po/LINUGAS b/po/LINUGAS deleted file mode 100644 index eaa0fea22..000000000 --- a/po/LINUGAS +++ /dev/null @@ -1,4 +0,0 @@ -# Set of available languages. -de - - diff --git a/po/Makevars b/po/Makevars index 20a50fc7d..b99cd466c 100644 --- a/po/Makevars +++ b/po/Makevars @@ -1,7 +1,7 @@ # Makefile variables for PO directory in any package using GNU gettext. # Usually the message domain is the same as the package name. -DOMAIN = $(PACKAGE) +DOMAIN = gnupg2 # These two variables depend on the location of this directory. subdir = po diff --git a/po/POTFILES.in b/po/POTFILES.in index 3bf6300b5..ae54716ac 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -31,3 +31,6 @@ sm/keydb.c sm/keylist.c sm/sign.c sm/verify.c + +tools/gpgconf.c +tools/gpgconf-comp.c diff --git a/po/de.po b/po/de.po index a7febc6c3..dafe41232 100644 --- a/po/de.po +++ b/po/de.po @@ -1,19 +1,24 @@ -# German translationn for NewPG which will soon be merged with GnuPG. -# Copyright (C) 2002 Free Software Foundation, Inc. +# German translation for GnuPG 1.9.x +# Copyright (C) 2002, 2004 Free Software Foundation, Inc. # Werner Koch , 2002. # +# +# Note that we use "gnupg2" as the domain to avoid conflicts with +# already installed domains "gnupg" from GnuPG < 1.9. +# msgid "" msgstr "" -"Project-Id-Version: NewPG 0.9.3\n" -"POT-Creation-Date: 2003-04-29 12:08+0200\n" -"PO-Revision-Date: 2002-08-21 13:13+0200\n" +"Project-Id-Version: gnupg2 1.9.7\n" +"Report-Msgid-Bugs-To: translations@gnupg.org\n" +"POT-Creation-Date: 2004-04-06 12:06+0200\n" +"PO-Revision-Date: 2004-04-06 11:47+0200\n" "Last-Translator: Werner Koch \n" "Language-Team: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=iso-8859-1\n" "Content-Transfer-Encoding: 8bit\n" -#: agent/gpg-agent.c:92 agent/protect-tool.c:85 scd/scdaemon.c:78 +#: agent/gpg-agent.c:94 agent/protect-tool.c:92 scd/scdaemon.c:89 msgid "" "@Options:\n" " " @@ -21,81 +26,74 @@ msgstr "" "@Optionen:\n" " " -#: agent/gpg-agent.c:94 scd/scdaemon.c:80 +#: agent/gpg-agent.c:96 scd/scdaemon.c:91 msgid "run in server mode (foreground)" msgstr "Im Server Modus ausführen" -#: agent/gpg-agent.c:95 scd/scdaemon.c:81 +#: agent/gpg-agent.c:97 scd/scdaemon.c:92 msgid "run in daemon mode (background)" msgstr "Im Daemon Modus ausführen" -#: agent/gpg-agent.c:96 kbx/kbxutil.c:72 scd/scdaemon.c:82 sm/gpgsm.c:287 +#: agent/gpg-agent.c:98 kbx/kbxutil.c:73 scd/scdaemon.c:93 sm/gpgsm.c:301 +#: tools/gpgconf.c:62 msgid "verbose" msgstr "ausführlich" -#: agent/gpg-agent.c:97 kbx/kbxutil.c:73 scd/scdaemon.c:83 sm/gpgsm.c:288 +#: agent/gpg-agent.c:99 kbx/kbxutil.c:74 scd/scdaemon.c:94 sm/gpgsm.c:302 msgid "be somewhat more quiet" msgstr "etwas weniger Aussageb erzeugen" -#: agent/gpg-agent.c:98 scd/scdaemon.c:84 +#: agent/gpg-agent.c:100 scd/scdaemon.c:95 msgid "sh-style command output" msgstr "Ausgabe für /bin/sh" -#: agent/gpg-agent.c:99 scd/scdaemon.c:85 +#: agent/gpg-agent.c:101 scd/scdaemon.c:96 msgid "csh-style command output" msgstr "Ausgabe für /bin/csh" -#: agent/gpg-agent.c:100 scd/scdaemon.c:86 sm/gpgsm.c:307 -msgid "read options from file" -msgstr "Konfigurationsoptionen aus Datei lesen" - -#: agent/gpg-agent.c:101 kbx/kbxutil.c:76 scd/scdaemon.c:87 -msgid "set debugging flags" -msgstr "Debug Flags setzen" - -#: agent/gpg-agent.c:102 kbx/kbxutil.c:77 scd/scdaemon.c:88 -msgid "enable full debugging" -msgstr "Alle Debug Flags setzen" +#: agent/gpg-agent.c:102 +msgid "|FILE|read options from FILE" +msgstr "|DATEI|Konfigurationsoptionen aus DATEI lesen" -#: agent/gpg-agent.c:104 scd/scdaemon.c:91 +#: agent/gpg-agent.c:107 scd/scdaemon.c:103 msgid "do not detach from the console" msgstr "Im Vordergrund laufen lassen" -#: agent/gpg-agent.c:105 +#: agent/gpg-agent.c:108 msgid "do not grab keyboard and mouse" msgstr "Tastatur und Maus nicht \"grabben\"" -#: agent/gpg-agent.c:106 scd/scdaemon.c:92 sm/gpgsm.c:290 +#: agent/gpg-agent.c:109 scd/scdaemon.c:104 sm/gpgsm.c:304 msgid "use a log file for the server" msgstr "Logausgaben in eine Datei umlenken" -#: agent/gpg-agent.c:107 +#: agent/gpg-agent.c:110 msgid "do not allow multiple connections" msgstr "Nicht mehr als eine Verbindung erlauben" -#: agent/gpg-agent.c:121 +#: agent/gpg-agent.c:124 msgid "ignore requests to change the TTY" -msgstr "" +msgstr "Ignoriere Anfragen, das TTY zu wechseln" -#: agent/gpg-agent.c:123 +#: agent/gpg-agent.c:126 msgid "ignore requests to change the X display" -msgstr "" +msgstr "Ignoriere Anfragen, das X-Display zu wechseln" -#: agent/gpg-agent.c:169 agent/protect-tool.c:113 scd/scdaemon.c:117 -#: sm/gpgsm.c:447 +#: agent/gpg-agent.c:180 agent/protect-tool.c:124 scd/scdaemon.c:150 +#: sm/gpgsm.c:468 tools/gpgconf.c:85 msgid "Please report bugs to <" msgstr "Fehlerberichte bitte an <" -#: agent/gpg-agent.c:169 agent/protect-tool.c:113 scd/scdaemon.c:117 -#: sm/gpgsm.c:447 +#: agent/gpg-agent.c:180 agent/protect-tool.c:124 scd/scdaemon.c:150 +#: sm/gpgsm.c:468 tools/gpgconf.c:85 msgid ">.\n" msgstr ">.\n" -#: agent/gpg-agent.c:172 +#: agent/gpg-agent.c:183 msgid "Usage: gpg-agent [options] (-h for help)" msgstr "Gebrauch: gpg-agent [Optionen] (-h für Hilfe)" -#: agent/gpg-agent.c:174 +#: agent/gpg-agent.c:185 msgid "" "Syntax: gpg-agent [options] [command [args]]\n" "Secret key management for GnuPG\n" @@ -103,50 +101,55 @@ msgstr "" "Syntax: gpg-agent [Optionen] [Kommando [Argumente]]\n" "Verwaltung von geheimen Schlüssel für GnuPG\n" -#: agent/gpg-agent.c:343 agent/protect-tool.c:822 kbx/kbxutil.c:229 -#: scd/scdaemon.c:245 sm/gpgsm.c:628 +#: agent/gpg-agent.c:256 scd/scdaemon.c:224 sm/gpgsm.c:591 +#, c-format +msgid "invalid debug-level `%s' given\n" +msgstr "ungültige Debugebene `%s' angegeben\n" + +#: agent/gpg-agent.c:414 agent/protect-tool.c:1035 kbx/kbxutil.c:230 +#: scd/scdaemon.c:325 sm/gpgsm.c:714 #, c-format msgid "libgcrypt is too old (need %s, have %s)\n" msgstr "" "Die Bibliothek \"libgcrypt\" is zu alt (benötigt wird %s, vorhanden ist %s)\n" -#: agent/gpg-agent.c:414 scd/scdaemon.c:316 sm/gpgsm.c:722 +#: agent/gpg-agent.c:487 scd/scdaemon.c:405 sm/gpgsm.c:812 #, c-format msgid "NOTE: no default option file `%s'\n" msgstr "Notiz: Voreingestellte Konfigurationsdatei `%s' fehlt\n" -#: agent/gpg-agent.c:419 agent/gpg-agent.c:829 scd/scdaemon.c:321 -#: sm/gpgsm.c:726 +#: agent/gpg-agent.c:492 agent/gpg-agent.c:947 scd/scdaemon.c:410 +#: sm/gpgsm.c:816 #, c-format msgid "option file `%s': %s\n" msgstr "Konfigurationsdatei `%s': %s\n" -#: agent/gpg-agent.c:427 scd/scdaemon.c:329 sm/gpgsm.c:733 +#: agent/gpg-agent.c:500 scd/scdaemon.c:418 sm/gpgsm.c:823 #, c-format msgid "reading options from `%s'\n" msgstr "Optionen werden aus `%s' gelesen\n" -#: agent/gpg-agent.c:519 scd/scdaemon.c:424 +#: agent/gpg-agent.c:641 scd/scdaemon.c:576 msgid "please use the option `--daemon' to run the program in the background\n" msgstr "" "Bitte die Option `--daemon' nutzen um das Programm im Hintergund " "auszuführen\n" -#: agent/gpg-agent.c:861 agent/gpg-agent.c:895 +#: agent/gpg-agent.c:980 agent/gpg-agent.c:1014 #, c-format msgid "can't create directory `%s': %s\n" msgstr "Das Verzeichniss `%s' kann nicht erstell werden: %s\n" -#: agent/gpg-agent.c:864 agent/gpg-agent.c:900 +#: agent/gpg-agent.c:983 agent/gpg-agent.c:1019 #, c-format msgid "directory `%s' created\n" msgstr "Verzeichniss `%s' wurde erstellt\n" -#: agent/protect-tool.c:116 +#: agent/protect-tool.c:127 msgid "Usage: gpg-protect-tool [options] (-h for help)\n" msgstr "Gebrauch: gpg-protect-tool [Optionen] (-h für Hilfe)\n" -#: agent/protect-tool.c:118 +#: agent/protect-tool.c:129 msgid "" "Syntax: gpg-protect-tool [options] [args]]\n" "Secret key maintenance tool\n" @@ -154,11 +157,7 @@ msgstr "" "Syntax: gpg-protect-tool [Optionen] [Argumente]\n" "Werkzeug zum Bearbeiten von geheimen Schlüsseln\n" -#: agent/protect-tool.c:902 -msgid "Enter passphrase:" -msgstr "Bitte das Mantra (Passphrase) eingeben:" - -#: agent/protect-tool.c:903 +#: agent/protect-tool.c:1142 msgid "" "Please enter the passphrase or the PIN\n" "needed to complete this operation." @@ -166,64 +165,34 @@ msgstr "" "Die Eingabe des Mantras (Passphrase) bzw. der PIN\n" "wird benötigt um diese Aktion auszuführen." -#: agent/simple-pwquery.c:272 -msgid "gpg-agent is not available in this session\n" -msgstr "Der gpg-agent ist nicht verfügbar\n" - -#: agent/simple-pwquery.c:281 sm/call-agent.c:195 -msgid "malformed GPG_AGENT_INFO environment variable\n" -msgstr "Die Variable GPG_AGENT_INFO ist fehlerhaft\n" - -#: agent/simple-pwquery.c:293 sm/call-agent.c:207 -#, c-format -msgid "gpg-agent protocol version %d is not supported\n" -msgstr "Das gpg-agent Protocol %d wird nicht unterstützt\n" +#: agent/protect-tool.c:1145 +msgid "Passphrase:" +msgstr "Passphrase:" -#: agent/simple-pwquery.c:315 -#, c-format -msgid "can't connect to `%s': %s\n" -msgstr "Verbindung zu `%s' kann nicht aufgebaut werden: %s\n" - -#: agent/simple-pwquery.c:326 -msgid "communication problem with gpg-agent\n" -msgstr "Kommunikationsproblem mit gpg-agent\n" - -#: agent/simple-pwquery.c:336 -msgid "problem setting the gpg-agent options\n" -msgstr "Beim setzen der gpg-agent Optionen ist ein problem aufgetreten\n" - -#: agent/simple-pwquery.c:466 -msgid "canceled by user\n" -msgstr "Vom Benutzer abgebrochen\n" - -#: agent/simple-pwquery.c:473 -msgid "problem with the agent\n" -msgstr "Problem mit dem Agenten\n" - -#: agent/divert-scd.c:192 +#: agent/divert-scd.c:200 #, c-format msgid "Please enter the PIN%s%s%s to unlock the card" msgstr "Bitte geben Sie die PIN%s%s%s ein um die Karte zu entsperren" -#: agent/genkey.c:82 -msgid "does not match - try again" -msgstr "Keine Übereinstimmung - bitte nochmal versuchen" - -#: agent/genkey.c:109 +#: agent/genkey.c:108 #, c-format msgid "Please enter the passphrase to%0Ato protect your new key" msgstr "" "Bitte geben Sie das Mantra (Passphrase) ein%0Aum Ihren Schlüssel zu schützen" -#: agent/genkey.c:111 agent/genkey.c:212 +#: agent/genkey.c:110 agent/genkey.c:224 msgid "Please re-enter this passphrase" msgstr "Bitte geben Sie das Mantra (Passphrase) noch einmal ein:" -#: agent/genkey.c:211 +#: agent/genkey.c:131 agent/genkey.c:244 +msgid "does not match - try again" +msgstr "Keine Übereinstimmung - bitte nochmal versuchen" + +#: agent/genkey.c:223 msgid "Please enter the new passphrase" msgstr "Bitte geben Sie das Mantra (Passphrase) ein:" -#: agent/query.c:255 +#: agent/query.c:268 msgid "" "Please enter your PIN, so that the secret key can be unlocked for this " "session" @@ -231,7 +200,7 @@ msgstr "" "Bitte geben Sie Ihre PIN ein, so daß der geheime Schlüssel benutzt werden " "kann" -#: agent/query.c:258 +#: agent/query.c:271 msgid "" "Please enter your passphrase, so that the secret key can be unlocked for " "this session" @@ -239,31 +208,31 @@ msgstr "" "Bitte geben Sie Ihr Mantra (Passphrase) ein, so daß der geheime Schlüssel " "benutzt werden kann" -#: agent/query.c:301 agent/query.c:313 +#: agent/query.c:326 agent/query.c:338 msgid "PIN too long" msgstr "Die PIN ist zu lang" -#: agent/query.c:302 +#: agent/query.c:327 msgid "Passphrase too long" msgstr "Das Matra (Passphrase) ist zu lang" -#: agent/query.c:310 +#: agent/query.c:335 msgid "Invalid characters in PIN" msgstr "Ungültige Zeichen in der PIN" -#: agent/query.c:315 +#: agent/query.c:340 msgid "PIN too short" msgstr "Die PIN ist zu kurz" -#: agent/query.c:326 +#: agent/query.c:352 msgid "Bad PIN" msgstr "Falsche PIN" -#: agent/query.c:327 +#: agent/query.c:353 msgid "Bad Passphrase" msgstr "Falsches Mantra (Passphrase)" -#: agent/query.c:366 +#: agent/query.c:392 msgid "Passphrase" msgstr "Mantra" @@ -274,22 +243,56 @@ msgstr "" "Das Erstellen eines Speicherabzugs (core-dump) kann nicht verhindert werden: " "%s\n" -#: common/sysutils.c:158 +#: common/sysutils.c:159 #, c-format msgid "Warning: unsafe ownership on %s \"%s\"\n" msgstr "WARNUNG: Unsichere Besitzrechte für %s \"%s\"\n" -#: common/sysutils.c:190 +#: common/sysutils.c:191 #, c-format msgid "Warning: unsafe permissions on %s \"%s\"\n" msgstr "WARNUNG: Unsichere Zugriffsrechte für %s \"%s\"\n" -#: jnlib/logging.c:343 +#: common/simple-pwquery.c:272 +msgid "gpg-agent is not available in this session\n" +msgstr "Der gpg-agent ist nicht verfügbar\n" + +#: common/simple-pwquery.c:286 sm/call-agent.c:126 +msgid "malformed GPG_AGENT_INFO environment variable\n" +msgstr "Die Variable GPG_AGENT_INFO ist fehlerhaft\n" + +#: common/simple-pwquery.c:298 sm/call-agent.c:138 +#, c-format +msgid "gpg-agent protocol version %d is not supported\n" +msgstr "Das gpg-agent Protocol %d wird nicht unterstützt\n" + +#: common/simple-pwquery.c:320 +#, c-format +msgid "can't connect to `%s': %s\n" +msgstr "Verbindung zu `%s' kann nicht aufgebaut werden: %s\n" + +#: common/simple-pwquery.c:331 +msgid "communication problem with gpg-agent\n" +msgstr "Kommunikationsproblem mit gpg-agent\n" + +#: common/simple-pwquery.c:341 +msgid "problem setting the gpg-agent options\n" +msgstr "Beim setzen der gpg-agent Optionen ist ein problem aufgetreten\n" + +#: common/simple-pwquery.c:471 +msgid "canceled by user\n" +msgstr "Vom Benutzer abgebrochen\n" + +#: common/simple-pwquery.c:478 +msgid "problem with the agent\n" +msgstr "Problem mit dem Agenten\n" + +#: jnlib/logging.c:516 #, c-format msgid "you found a bug ... (%s:%d)\n" msgstr "Sie haben einen Bug (Softwarefehler) gefunden ... (%s:%d)\n" -#: kbx/kbxutil.c:61 sm/gpgsm.c:208 +#: kbx/kbxutil.c:62 sm/gpgsm.c:218 tools/gpgconf.c:53 msgid "" "@Commands:\n" " " @@ -297,7 +300,7 @@ msgstr "" "@Kommandos:\n" " " -#: kbx/kbxutil.c:67 sm/gpgsm.c:237 +#: kbx/kbxutil.c:68 sm/gpgsm.c:248 tools/gpgconf.c:59 msgid "" "@\n" "Options:\n" @@ -307,31 +310,39 @@ msgstr "" "Optionen:\n" " " -#: kbx/kbxutil.c:69 sm/gpgsm.c:239 +#: kbx/kbxutil.c:70 sm/gpgsm.c:250 msgid "create ascii armored output" msgstr "Ausgabe mit ASCII Hülle wird erzeugt" -#: kbx/kbxutil.c:71 sm/gpgsm.c:286 +#: kbx/kbxutil.c:72 sm/gpgsm.c:300 tools/gpgconf.c:61 msgid "use as output file" msgstr "als Ausgabedatei benutzen" -#: kbx/kbxutil.c:74 sm/gpgsm.c:295 +#: kbx/kbxutil.c:75 sm/gpgsm.c:309 tools/gpgconf.c:64 msgid "do not make any changes" msgstr "Keine Änderungen durchführen" -#: kbx/kbxutil.c:98 +#: kbx/kbxutil.c:77 +msgid "set debugging flags" +msgstr "Debug Flags setzen" + +#: kbx/kbxutil.c:78 +msgid "enable full debugging" +msgstr "Alle Debug Flags setzen" + +#: kbx/kbxutil.c:99 msgid "Please report bugs to " msgstr "Bite richten sie Berichte über Bugs (Softwarefehler) an " -#: kbx/kbxutil.c:98 +#: kbx/kbxutil.c:99 msgid ".\n" msgstr ".\n" -#: kbx/kbxutil.c:102 +#: kbx/kbxutil.c:103 msgid "Usage: kbxutil [options] [files] (-h for help)" msgstr "Gebrauch: kbxutil [Optionen] [Dateien] (-h für Hilfe)" -#: kbx/kbxutil.c:105 +#: kbx/kbxutil.c:106 msgid "" "Syntax: kbxutil [options] [files]\n" "list, export, import Keybox data\n" @@ -339,15 +350,43 @@ msgstr "" "Syntax: kbxutil [Optionen] [Dateien]\n" "Anlistem exportieren und Importieren von KeyBox Dateien\n" -#: scd/scdaemon.c:90 +#: scd/scdaemon.c:97 sm/gpgsm.c:321 +msgid "read options from file" +msgstr "Konfigurationsoptionen aus Datei lesen" + +#: scd/scdaemon.c:102 msgid "|N|set OpenSC debug level to N" msgstr "|N|Den OpenSC Debugstufe auf N setzen" -#: scd/scdaemon.c:120 +#: scd/scdaemon.c:105 +msgid "|N|connect to reader at port N" +msgstr "|N|Verbinde mit dem Leser auf Port N" + +#: scd/scdaemon.c:106 +msgid "|NAME|use NAME as ct-API driver" +msgstr "|NAME|Benutze NAME als CT-API Treiber" + +#: scd/scdaemon.c:107 +msgid "|NAME|use NAME as PC/SC driver" +msgstr "|NAME|Benutze NAME als PC/SC Treiber" + +#: scd/scdaemon.c:110 +msgid "do not use the internal CCID driver" +msgstr "Den internen CCID Treiber nicht benutzen" + +#: scd/scdaemon.c:117 +msgid "do not use the OpenSC layer" +msgstr "Den OpenSC basierten Kartenzugriff nicht nutzen" + +#: scd/scdaemon.c:122 +msgid "allow the use of admin card commands" +msgstr "Erlaube die Benuztung von \"Admin\" Kommandos" + +#: scd/scdaemon.c:153 msgid "Usage: scdaemon [options] (-h for help)" msgstr "Gebrauch: scdaemon [Optionen] (-h für Hilfe)" -#: scd/scdaemon.c:122 +#: scd/scdaemon.c:155 msgid "" "Syntax: scdaemon [options] [command [args]]\n" "Smartcard daemon for GnuPG\n" @@ -355,194 +394,236 @@ msgstr "" "Synatx: scdaemon [Optionen] [Kommando [Argumente]]\n" "Smartcard Daemon für GnuPG\n" -#: sm/base64.c:295 +#: sm/base64.c:315 #, c-format msgid "invalid radix64 character %02x skipped\n" msgstr "Ungültiges Basis-64 Zeichen %02X wurde übergangen\n" -#: sm/call-agent.c:158 +#: sm/call-agent.c:88 msgid "no running gpg-agent - starting one\n" msgstr "Kein aktiver gpg-agent - es wird einer gestarted\n" -#: sm/call-agent.c:218 +#: sm/call-agent.c:149 msgid "can't connect to the agent - trying fall back\n" msgstr "Verbindung zum gpg-agent nicht möglich - Ersatzmethode wird versucht\n" -#: sm/call-dirmngr.c:153 +#: sm/call-dirmngr.c:162 msgid "no running dirmngr - starting one\n" msgstr "Kein aktiver Dirmngr - es wird einer gestartet\n" -#: sm/call-dirmngr.c:190 +#: sm/call-dirmngr.c:200 msgid "malformed DIRMNGR_INFO environment variable\n" msgstr "Die Variable DIRMNGR_INFO ist fehlerhaft\n" -#: sm/call-dirmngr.c:202 +#: sm/call-dirmngr.c:212 #, c-format msgid "dirmngr protocol version %d is not supported\n" msgstr "Die Dirmngr Protokollversion %d wird nicht unterstützt\n" -#: sm/call-dirmngr.c:213 +#: sm/call-dirmngr.c:223 msgid "can't connect to the dirmngr - trying fall back\n" msgstr "" "Verbindung zum Dirmngr kann nicht aufgebaut werden - Ersatzmethode wird " "versucht\n" -#: sm/certdump.c:51 sm/certdump.c:99 +#: sm/certdump.c:59 sm/certdump.c:142 msgid "none" msgstr "keine" -#: sm/certdump.c:119 +#: sm/certdump.c:151 msgid "[none]" msgstr "[keine]" -#: sm/certdump.c:121 -msgid "[error]" -msgstr "[Fehler]" - -#: sm/certdump.c:424 +#: sm/certdump.c:489 msgid "[Error - No name]" msgstr "[Fehler - Kein Name]" -#: sm/certdump.c:433 +#: sm/certdump.c:498 msgid "[Error - unknown encoding]" msgstr "[Fehler - Unbekannte Kodierung]" -#: sm/certdump.c:437 +#: sm/certdump.c:502 msgid "[Error - invalid encoding]" msgstr "[Fehler - Ungültige Kodierung]" -#: sm/certdump.c:442 +#: sm/certdump.c:507 msgid "[Error - invalid DN]" msgstr "[Fehler - Ungültiger DN]" -#: sm/certlist.c:52 -msgid "no key usage specified - accepted for encryption\n" +#: sm/certdump.c:651 +#, c-format +msgid "" +"Please enter the passphrase to unlock the secret key for:\n" +"\"%s\"\n" +"S/N %s, ID %08lX, created %s" msgstr "" -"Schlüsselverwendungszweck nicht vorhanden - wird zum Verschlüsseln " -"akzeptiert\n" - -#: sm/certlist.c:53 -msgid "no key usage specified - accepted for signing\n" +"Bitte geben Sie die Passphrase an, um den \n" +"geheimen Schlüssels von\n" +"\"%s\"\n" +"S/N %s, ID %08lX, erzeugt %s\n" +"zu entsperren" + +#: sm/certlist.c:113 +msgid "no key usage specified - assuming all usages\n" msgstr "" -"Schlüsselverwendungszweck nicht vorhanden - wird zum Signieren akzeptiert\n" +"Schlüsselverwendungszweck nicht vorhanden - für alle Zwecke akzeptiert\n" -#: sm/certlist.c:58 sm/keylist.c:82 +#: sm/certlist.c:123 sm/keylist.c:108 #, c-format msgid "error getting key usage information: %s\n" msgstr "Fehler beim holen der Schlüsselbenutzungsinformationen: %s\n" -#: sm/certlist.c:67 -msgid "certificate should have not been used certification\n" -msgstr "Das Zertifikat hatte nicht zum Zertifizieren benutzt werden sollen\n" +#: sm/certlist.c:133 +msgid "certificate should have not been used for certification\n" +msgstr "Das Zertifikat hätte nicht zum Zertifizieren benutzt werden sollen\n" -#: sm/certlist.c:76 +#: sm/certlist.c:144 msgid "certificate should have not been used for encryption\n" msgstr "Das Zertifikat hatte nicht zum Verschlüsseln benutzt werden sollen\n" -#: sm/certlist.c:77 +#: sm/certlist.c:145 msgid "certificate should have not been used for signing\n" msgstr "Das Zertifikat hatte nicht zum Signieren benutzt werden sollen\n" -#: sm/certlist.c:78 +#: sm/certlist.c:146 msgid "certificate is not usable for encryption\n" msgstr "Das Zertifikat kann nicht zum Verschlüsseln benutzt werden\n" -#: sm/certlist.c:79 +#: sm/certlist.c:147 msgid "certificate is not usable for signing\n" msgstr "Das Zertifikat kann nicht zum Signieren benutzt werden\n" -#: sm/certchain.c:59 +#: sm/certchain.c:109 #, c-format -msgid "critical certificate extension %s is not supported\n" -msgstr "Die kritische Zertifikaterweiterung %s wird nicht unterstützt\n" +msgid "critical certificate extension %s is not supported" +msgstr "Die kritische Zertifikaterweiterung %s wird nicht unterstützt" -#: sm/certchain.c:81 -msgid "issuer certificate is not marked as a CA\n" -msgstr "Das Herausgeberzertifikat ist für eine CA nicht zulässig\n" +#: sm/certchain.c:131 +msgid "issuer certificate is not marked as a CA" +msgstr "Das Herausgeberzertifikat ist nicht für eine CA gekennzeichnet" -#: sm/certchain.c:151 -msgid "note: certificate policy not allowed\n" -msgstr "Notiz: Die Zertifikatrichtilinie ist nicht erlaubt\n" +#: sm/certchain.c:169 +msgid "critical marked policy without configured policies" +msgstr "kritische Richtlinie ohne konfigurierte Richtlinien" -#: sm/certchain.c:154 -msgid "certificate policy not allowed\n" -msgstr "Die Zertifikatrichtilinie ist nicht erlaubt\n" +#: sm/certchain.c:185 sm/certchain.c:214 +msgid "note: non-critical certificate policy not allowed" +msgstr "Notiz: Die unkritische Zertifikatrichtlinie ist nicht erlaubt" -#: sm/certchain.c:280 +#: sm/certchain.c:189 sm/certchain.c:218 +msgid "certificate policy not allowed" +msgstr "Die Zertifikatrichtlinie ist nicht erlaubt" + +#: sm/certchain.c:349 msgid "looking up issuer at external location\n" msgstr "Der Herausgeber wird von einer externen Stelle gesucht\n" -#: sm/certchain.c:298 +#: sm/certchain.c:367 #, c-format msgid "number of issuers matching: %d\n" msgstr "Anzahl der übereinstimmenden Heruasgeber: %d\n" -#: sm/certchain.c:334 sm/certchain.c:433 sm/certchain.c:719 sm/decrypt.c:263 -#: sm/encrypt.c:377 sm/sign.c:315 sm/verify.c:116 +#: sm/certchain.c:403 sm/certchain.c:561 sm/certchain.c:909 sm/decrypt.c:260 +#: sm/encrypt.c:341 sm/sign.c:324 sm/verify.c:107 msgid "failed to allocated keyDB handle\n" msgstr "Ein keyDB Handle konnte nicht bereitgestellt werden\n" -#: sm/certchain.c:519 -msgid "the certificate has been revoked\n" -msgstr "Das Zertifikat wurde widerrufen\n" +#: sm/certchain.c:492 +msgid "certificate has been revoked" +msgstr "Das Zertifikat wurde widerrufen" -#: sm/certchain.c:523 -msgid "no CRL found for certificate\n" -msgstr "Keine CRL für das Zertifikat vorhanden\n" +#: sm/certchain.c:501 +msgid "no CRL found for certificate" +msgstr "Keine CRL für das Zertifikat gefunden" -#: sm/certchain.c:527 -msgid "the available CRL is too old\n" -msgstr "Die vorhandene CRL ist zu alt\n" +#: sm/certchain.c:505 +msgid "the available CRL is too old" +msgstr "Die vorhandene CRL ist zu alt" -#: sm/certchain.c:528 +#: sm/certchain.c:507 msgid "please make sure that the \"dirmngr\" is properly installed\n" msgstr "" "Bite vergewissern Sie sich das der \"dirmngr\" richtig installierrt ist\n" -#: sm/certchain.c:533 +#: sm/certchain.c:512 #, c-format -msgid "checking the CRL failed: %s\n" -msgstr "Die CRL konnte nicht geprüft werden: %s\n" +msgid "checking the CRL failed: %s" +msgstr "Die CRL konnte nicht geprüft werden: %s" -#: sm/certchain.c:562 -msgid "root certificate is not marked trusted\n" -msgstr "Das Wurzelzertifikat ist nicht als vertrauenswürdig markiert\n" +#: sm/certchain.c:581 +msgid "no issuer found in certificate" +msgstr "Im Zertifikat ist kein Herausgeber enthalten" -#: sm/certchain.c:563 +#: sm/certchain.c:594 +#, c-format +msgid "certificate with invalid validity: %s" +msgstr "Zertifikat mit unzulässiger Gültigkeit: %s" + +#: sm/certchain.c:610 +msgid "certificate not yet valid" +msgstr "Das Zertifikat ist noch nicht gültig" + +#: sm/certchain.c:623 +msgid "certificate has expired" +msgstr "Das Zertifikat ist abgelaufen" + +#: sm/certchain.c:660 +msgid "selfsigned certificate has a BAD signature" +msgstr "Das eigenbeglaubigte Zertifikat hat eine FALSCHE Signatur" + +#: sm/certchain.c:674 +msgid "root certificate is not marked trusted" +msgstr "Das Wurzelzertifikat ist nicht als vertrauenswürdig markiert" + +#: sm/certchain.c:680 #, c-format msgid "fingerprint=%s\n" msgstr "Fingerprint=%s\n" -#: sm/certchain.c:568 +#: sm/certchain.c:685 msgid "root certificate has now been marked as trusted\n" msgstr "Das Wurzelzertifikat wurde nun als vertrauenswürdig markiert\n" -#: sm/certchain.c:582 +#: sm/certchain.c:700 #, c-format msgid "checking the trust list failed: %s\n" msgstr "Fehler beim Prüfen der vertrauenswürdigen Zertifikate: %s\n" -#: sm/certchain.c:592 sm/import.c:127 +#: sm/certchain.c:721 sm/import.c:145 msgid "certificate chain too long\n" msgstr "Der Zertifikatkette ist zu lang\n" -#: sm/certchain.c:643 +#: sm/certchain.c:733 +msgid "issuer certificate not found" +msgstr "Herausgeberzertifikat nicht gefunden" + +#: sm/certchain.c:766 +msgid "certificate has a BAD signature" +msgstr "Das Zertifikat hat eine FALSCHE Signatur" + +#: sm/certchain.c:789 +msgid "found another possible matching CA certificate - trying again" +msgstr "" +"Eine anderes möglicherweise passendes CA-Zertifikat gefunden - versuche " +"nochmal" + +#: sm/certchain.c:812 #, c-format -msgid "certificate chain longer than allowed by CA (%d)\n" -msgstr "Die Zertifikatkette ist länger als von der CA erlaubt (%d)\n" +msgid "certificate chain longer than allowed by CA (%d)" +msgstr "Die Zertifikatkette ist länger als von der CA erlaubt (%d)" -#: sm/decrypt.c:128 +#: sm/decrypt.c:127 msgid "" "WARNING: message was encrypted with a weak key in the symmetric cipher.\n" msgstr "" "WARNUNG: Die Nachricht wurde mich einem schwachen Schlüssel (Weak Key) " "erzeugt\n" -#: sm/decrypt.c:333 +#: sm/decrypt.c:325 msgid "(this is the RC2 algorithm)\n" msgstr "(Dies ist der RC-2 Algorithmus)\n" -#: sm/decrypt.c:335 +#: sm/decrypt.c:327 msgid "(this does not seem to be an encrypted message)\n" msgstr "(dies is wahrscheinlich keine verschlüsselte Nachricht)\n" @@ -551,246 +632,255 @@ msgstr "(dies is wahrscheinlich keine verschl msgid "certificate `%s' not found: %s\n" msgstr "Zertifikat `%s' nicht gefunden: %s\n" -#: sm/delete.c:126 +#: sm/delete.c:112 sm/keydb.c:1388 +#, c-format +msgid "error locking keybox: %s\n" +msgstr "Fehler beim Sperren der Keybox: %s\n" + +#: sm/delete.c:133 #, c-format msgid "duplicated certificate `%s' deleted\n" msgstr "Doppeltes Zertifikat `%s' gelöscht\n" -#: sm/delete.c:128 +#: sm/delete.c:135 #, c-format msgid "certificate `%s' deleted\n" msgstr "Zertifikat `%s' gelöscht\n" -#: sm/delete.c:158 +#: sm/delete.c:165 #, c-format msgid "deleting certificate \"%s\" failed: %s\n" msgstr "Fehler beim Löschen des Zertifikats \"%s\": %s\n" -#: sm/encrypt.c:105 +#: sm/encrypt.c:120 msgid "weak key created - retrying\n" msgstr "Schwacher Schlüssel - es wird erneut versucht\n" -#: sm/encrypt.c:368 +#: sm/encrypt.c:332 msgid "no valid recipients given\n" msgstr "Keine gültigen Empfänger angegeben\n" -#: sm/gpgsm.c:210 -msgid "|[file]|make a signature" -msgstr "|DATEI|Erzeufe eine Signatur" +#: sm/gpgsm.c:220 +msgid "|[FILE]|make a signature" +msgstr "|[DATEI]|Erzeuge eine Signatur" -#: sm/gpgsm.c:211 -msgid "|[file]|make a clear text signature" -msgstr "|DATEI|Erzeuge eine Klartextsignatur" +#: sm/gpgsm.c:221 +msgid "|[FILE]|make a clear text signature" +msgstr "|[DATEI]|Erzeuge eine Klartextsignatur" -#: sm/gpgsm.c:212 +#: sm/gpgsm.c:222 msgid "make a detached signature" msgstr "Erzeuge eine abgetrennte Signatur" -#: sm/gpgsm.c:213 +#: sm/gpgsm.c:223 msgid "encrypt data" msgstr "Verschlüssele die Daten" -#: sm/gpgsm.c:214 +#: sm/gpgsm.c:224 msgid "encryption only with symmetric cipher" msgstr "Verschlüsselung nur mit symmetrischem Algrithmus" -#: sm/gpgsm.c:215 +#: sm/gpgsm.c:225 msgid "decrypt data (default)" msgstr "Enschlüssele die Daten" -#: sm/gpgsm.c:216 +#: sm/gpgsm.c:226 msgid "verify a signature" msgstr "Überprüfen einer Signatur" -#: sm/gpgsm.c:218 +#: sm/gpgsm.c:228 msgid "list keys" msgstr "Schlüssel anzeigen" -#: sm/gpgsm.c:219 +#: sm/gpgsm.c:229 msgid "list external keys" msgstr "Externe Schlüssel anzeigen" -#: sm/gpgsm.c:220 +#: sm/gpgsm.c:230 msgid "list secret keys" msgstr "Geheime Schlüssel anzeigen" -#: sm/gpgsm.c:221 +#: sm/gpgsm.c:231 msgid "list certificate chain" msgstr "Schlüssel mit Zertifikatekette anzeigen" -#: sm/gpgsm.c:223 +#: sm/gpgsm.c:233 msgid "list keys and fingerprints" msgstr "Schlüssel und Fingerprint anzeigen" -#: sm/gpgsm.c:224 +#: sm/gpgsm.c:234 msgid "generate a new key pair" msgstr "Neues Schlüsselpaar erzeugen" -#: sm/gpgsm.c:225 +#: sm/gpgsm.c:235 msgid "remove key from the public keyring" msgstr "Schlüssel aus dem öffentlichen Schlüsselbund löschen" -#: sm/gpgsm.c:226 +#: sm/gpgsm.c:236 msgid "export keys to a key server" msgstr "Schlüssen an eine Schlüsselserver exportieren" -#: sm/gpgsm.c:227 +#: sm/gpgsm.c:237 msgid "import keys from a key server" msgstr "Schlüssel von einem Schlüsselserver importieren" -#: sm/gpgsm.c:228 +#: sm/gpgsm.c:238 msgid "import certificates" msgstr "Zertifikate importieren" -#: sm/gpgsm.c:229 +#: sm/gpgsm.c:239 msgid "export certificates" msgstr "Zertifikate exportieren" -#: sm/gpgsm.c:230 +#: sm/gpgsm.c:240 msgid "register a smartcard" msgstr "Smartcard registrieren" -#: sm/gpgsm.c:231 +#: sm/gpgsm.c:241 msgid "run in server mode" msgstr "Im Server Modus ausführen" -#: sm/gpgsm.c:232 +#: sm/gpgsm.c:242 msgid "pass a command to the dirmngr" -msgstr "" +msgstr "Das Kommand an den Dirmngr durchreichen" -#: sm/gpgsm.c:234 +#: sm/gpgsm.c:244 msgid "invoke gpg-protect-tool" -msgstr "" +msgstr "Rufe das gpg-protect-tool auf" -#: sm/gpgsm.c:235 +#: sm/gpgsm.c:245 msgid "change a passphrase" msgstr "Das Mantra (Passphrase) ändern" -#: sm/gpgsm.c:241 +#: sm/gpgsm.c:252 msgid "create base-64 encoded output" msgstr "Ausgabe im Basis-64 format erzeugen" -#: sm/gpgsm.c:243 +#: sm/gpgsm.c:254 msgid "assume input is in PEM format" msgstr "Eingabedaten sind im PEM Format" -#: sm/gpgsm.c:245 +#: sm/gpgsm.c:256 msgid "assume input is in base-64 format" msgstr "Eingabedaten sin im Basis-64 Format" -#: sm/gpgsm.c:247 +#: sm/gpgsm.c:258 msgid "assume input is in binary format" msgstr "Eingabedaten sind im Binärformat" -#: sm/gpgsm.c:249 +#: sm/gpgsm.c:260 msgid "|NAME|encrypt for NAME" msgstr "|NAME|Verschlüsseln für NAME" -#: sm/gpgsm.c:252 +#: sm/gpgsm.c:263 msgid "never consult a CRL" msgstr "Niemals eine CRL konsultieren" -#: sm/gpgsm.c:256 +#: sm/gpgsm.c:267 +msgid "check validity using OCSP" +msgstr "Die Gültigkeit mittels OCSP prüfen" + +#: sm/gpgsm.c:270 msgid "|N|number of certificates to include" msgstr "|N|Sende N Zertifikate mit" -#: sm/gpgsm.c:259 +#: sm/gpgsm.c:273 msgid "|FILE|take policy information from FILE" msgstr "|DATEI|Richtlinieninformationen DATEI entnehmen" -#: sm/gpgsm.c:262 +#: sm/gpgsm.c:276 msgid "do not check certificate policies" msgstr "Zertikikatrichtlinien nicht überprüfen" -#: sm/gpgsm.c:266 +#: sm/gpgsm.c:280 msgid "fetch missing issuer certificates" msgstr "Fehlende Zertifikate automatisch holen" -#: sm/gpgsm.c:270 +#: sm/gpgsm.c:284 msgid "|NAME|use NAME as default recipient" msgstr "|NAME|Benutze NAME als voreingestellten Empfänger" -#: sm/gpgsm.c:272 +#: sm/gpgsm.c:286 msgid "use the default key as default recipient" msgstr "Benuzte voreingestellten Schlüssel als Standardempfänger" -#: sm/gpgsm.c:278 +#: sm/gpgsm.c:292 msgid "use this user-id to sign or decrypt" msgstr "Benuzte diese Benutzer ID zum Signieren oder Entschlüsseln" -#: sm/gpgsm.c:281 +#: sm/gpgsm.c:295 msgid "|N|set compress level N (0 disables)" msgstr "|N|Benutze Komprimierungsstufe N" -#: sm/gpgsm.c:283 +#: sm/gpgsm.c:297 msgid "use canonical text mode" -msgstr "" +msgstr "Kanonischen Textmodus benutzen" -#: sm/gpgsm.c:289 +#: sm/gpgsm.c:303 msgid "don't use the terminal at all" -msgstr "" +msgstr "Das Terminal überhaupt nicht benutzen" -#: sm/gpgsm.c:292 +#: sm/gpgsm.c:306 msgid "force v3 signatures" -msgstr "" +msgstr "Version 3 Signaturen erzwingen" -#: sm/gpgsm.c:293 +#: sm/gpgsm.c:307 msgid "always use a MDC for encryption" -msgstr "" +msgstr "Immer das MDC Verfahren zum verschlüsseln mitbenutzen" -#: sm/gpgsm.c:298 +#: sm/gpgsm.c:312 msgid "batch mode: never ask" msgstr "Stapelverarbeitungs Modus: Nie nachfragen" -#: sm/gpgsm.c:299 +#: sm/gpgsm.c:313 msgid "assume yes on most questions" -msgstr "" +msgstr "\"Ja\" auf die meisten Anfragen annehmen" -#: sm/gpgsm.c:300 +#: sm/gpgsm.c:314 msgid "assume no on most questions" -msgstr "" +msgstr "\"Nein\" auf die meisten Anfragen annehmen" -#: sm/gpgsm.c:302 +#: sm/gpgsm.c:316 msgid "add this keyring to the list of keyrings" -msgstr "" +msgstr "Diesen Keyring in die Liste der Keyrings aufnehmen" -#: sm/gpgsm.c:303 +#: sm/gpgsm.c:317 msgid "add this secret keyring to the list" -msgstr "" +msgstr "Diese geheimen Keyring in die Liste aufnehmen" -#: sm/gpgsm.c:304 +#: sm/gpgsm.c:318 msgid "|NAME|use NAME as default secret key" -msgstr "" +msgstr "|NAME|Benutze NAME als voreingestellten Schlüssel" -#: sm/gpgsm.c:305 +#: sm/gpgsm.c:319 msgid "|HOST|use this keyserver to lookup keys" -msgstr "" +msgstr "|HOST|Benutze HOST als Schlüsselserver" -#: sm/gpgsm.c:306 +#: sm/gpgsm.c:320 msgid "|NAME|set terminal charset to NAME" -msgstr "" +msgstr "|NAME|Den Zeichensatz für das Terminal auf NAME setzen" -#: sm/gpgsm.c:313 +#: sm/gpgsm.c:329 msgid "|FD|write status info to this FD" msgstr "|FD|Statusinformationen auf Dateidescriptor FD schreiben" -#: sm/gpgsm.c:320 +#: sm/gpgsm.c:336 msgid "|FILE|load extension module FILE" -msgstr "" +msgstr "|DATEI|Das Erweiterungsmodul DATEI laden" -#: sm/gpgsm.c:326 +#: sm/gpgsm.c:342 msgid "|NAME|use cipher algorithm NAME" msgstr "|NAME|Den Verhsclüsselungsalgrithmus NAME benutzen" -#: sm/gpgsm.c:328 +#: sm/gpgsm.c:344 msgid "|NAME|use message digest algorithm NAME" msgstr "|NAME|Den Hashalgorithmus NAME benutzen" -#: sm/gpgsm.c:330 +#: sm/gpgsm.c:346 msgid "|N|use compress algorithm N" msgstr "|N|Den Kompressionsalgorithmus Nummer N benutzen" -#: sm/gpgsm.c:336 +#: sm/gpgsm.c:354 msgid "" "@\n" "(See the man page for a complete listing of all commands and options)\n" @@ -798,7 +888,7 @@ msgstr "" "@\n" "(Die \"man\" Seite beschreibt alle Kommands und Optionen)\n" -#: sm/gpgsm.c:339 +#: sm/gpgsm.c:357 msgid "" "@\n" "Examples:\n" @@ -809,12 +899,20 @@ msgid "" " --list-keys [names] show keys\n" " --fingerprint [names] show fingerprints\n" msgstr "" +"@\n" +"Beispiele:\n" +"\n" +" -se -r Bob [Datei] Signieren und verschlüsseln für Benutzer Bob\\n\n" +" --clearsign [Datei] Eine Klartextsignatur erzeugen\\n\n" +" --detach-sign [Datei] Eine abgetrennte Signatur erzeugen\\n\n" +" --list-keys [Namen] Schlüssel anzeigenn\n" +" --fingerprint [Namen] \"Fingerabdrücke\" anzeigen\\n\n" -#: sm/gpgsm.c:450 +#: sm/gpgsm.c:471 msgid "Usage: gpgsm [options] [files] (-h for help)" msgstr "Gebrauch: gpgsm [Optionen] [Dateien] (-h für Hilfe)" -#: sm/gpgsm.c:453 +#: sm/gpgsm.c:474 msgid "" "Syntax: gpgsm [options] [files]\n" "sign, check, encrypt or decrypt using the S/MIME protocol\n" @@ -823,167 +921,273 @@ msgstr "" "Gebrauch: gpgsm [Optionen] [Dateien]\n" "Signieren, prüfen, ver- und entschlüsseln mittels S/MIME protocol\n" -#: sm/gpgsm.c:460 +#: sm/gpgsm.c:481 msgid "" "\n" "Supported algorithms:\n" msgstr "" +"\n" +"Unterstützte Algorithmen:\n" -#: sm/gpgsm.c:541 +#: sm/gpgsm.c:562 msgid "usage: gpgsm [options] " msgstr "Gebrauch: gpgsm [Optionen] " -#: sm/gpgsm.c:574 +#: sm/gpgsm.c:628 msgid "conflicting commands\n" msgstr "Widersprechende Kommandos\n" -#: sm/gpgsm.c:633 +#: sm/gpgsm.c:644 +#, c-format +msgid "can't encrypt to `%s': %s\n" +msgstr "Verschlüsseln für `%s' nicht möglich: %s\n" + +#: sm/gpgsm.c:719 #, c-format msgid "libksba is too old (need %s, have %s)\n" msgstr "Die Bibliothek Libksba is nicht aktuell (benötige %s, habe %s)\n" -#: sm/gpgsm.c:1027 +#: sm/gpgsm.c:1151 msgid "WARNING: program may create a core file!\n" -msgstr "" +msgstr "WARNUNG: Programm könnte eine core-dump-Datei schreiben!\n" -#: sm/gpgsm.c:1037 +#: sm/gpgsm.c:1163 msgid "WARNING: running with faked system time: " -msgstr "" +msgstr "WARNUNG: Ausführung mit gefälschter Systemzeit: " -#: sm/gpgsm.c:1056 +#: sm/gpgsm.c:1183 msgid "selected cipher algorithm is invalid\n" -msgstr "" +msgstr "Das ausgewählte Verschlüsselungsverfahren ist ungültig\n" -#: sm/gpgsm.c:1064 +#: sm/gpgsm.c:1191 msgid "selected digest algorithm is invalid\n" -msgstr "" +msgstr "Das ausgewählte Hashverfahren ist ungültig\n" -#: sm/gpgsm.c:1096 +#: sm/gpgsm.c:1221 #, c-format msgid "can't sign using `%s': %s\n" msgstr "Signieren mit `%s' nicht möglich: %s\n" -#: sm/gpgsm.c:1118 -#, c-format -msgid "can't encrypt to `%s': %s\n" -msgstr "Verschlüsseln für `%s' nicht möglich: %s\n" - -#: sm/gpgsm.c:1156 -msgid "--call-dirmngr {args}" -msgstr "" - -#: sm/gpgsm.c:1172 -msgid "--encrypt [datafile]" -msgstr "" - -#: sm/gpgsm.c:1186 -msgid "--sign [datafile]" -msgstr "" +#: sm/gpgsm.c:1386 +msgid "this command has not yet been implemented\n" +msgstr "Diee Kommando wurde noch nicht implementiert\n" -#: sm/gpgsm.c:1213 -msgid "--verify [signature [detached_data]]" -msgstr "" - -#: sm/gpgsm.c:1230 -msgid "--decrypt [filename]" -msgstr "" - -#: sm/gpgsm.c:1408 sm/gpgsm.c:1441 +#: sm/gpgsm.c:1579 sm/gpgsm.c:1612 #, c-format msgid "can't open `%s': %s\n" msgstr "Datei `%s' kann nicht geöffnet werden: %s\n" -#: sm/import.c:96 +#: sm/import.c:114 #, c-format msgid "total number processed: %lu\n" msgstr "gesamte verarbeitete Anzahl: %lu\n" -#: sm/import.c:99 +#: sm/import.c:117 #, c-format msgid " imported: %lu" msgstr " importiert: %lu" -#: sm/import.c:103 +#: sm/import.c:121 #, c-format msgid " unchanged: %lu\n" msgstr " nicht geändert: %lu\n" -#: sm/import.c:105 +#: sm/import.c:123 #, c-format msgid " not imported: %lu\n" msgstr " nicht importiert: %lu\n" -#: sm/import.c:174 +#: sm/import.c:204 msgid "error storing certificate\n" msgstr "Fehler beim speichern des Zertifikats\n" -#: sm/import.c:181 +#: sm/import.c:211 msgid "basic certificate checks failed - not imported\n" msgstr "Grundlegende Zertifikatprüfungen fehlgeschlagen - nicht importiert\n" -#: sm/import.c:313 sm/import.c:345 +#: sm/import.c:396 sm/import.c:428 #, c-format msgid "error importing certificate: %s\n" msgstr "Fehler beim Importieren des Zertifikats: %s\n" -#: sm/keydb.c:188 +#: sm/import.c:457 +#, c-format +msgid "error creating a pipe: %s\n" +msgstr "Fehler beim Erzeugen einer \"Pipe\": %s\n" + +#: sm/import.c:465 +#, c-format +msgid "error forking process: %s\n" +msgstr "Fehler beim \"Forken\" des Prozess: %s\n" + +#: sm/import.c:560 sm/import.c:585 +#, c-format +msgid "error creating temporary file: %s\n" +msgstr "Fehler beim Erstellen einer temporären Datei: %s\n" + +#: sm/import.c:568 +#, c-format +msgid "error writing to temporary file: %s\n" +msgstr "Fehler beim Schreiben auf eine temporäre Datei: %s\n" + +#: sm/import.c:577 +#, c-format +msgid "error reading input: %s\n" +msgstr "Fehler beim Lesen der Eingabe: %s\n" + +#: sm/import.c:647 +#, c-format +msgid "waiting for protect-tool to terminate failed: %s\n" +msgstr "" +"Das Warten auf die Beendigung des protect-tools ist fehlgeschlagen: %s\n" + +#: sm/import.c:650 +#, c-format +msgid "error running `%s': probably not installed\n" +msgstr "Feler bei Ausführung von `%s': wahrscheinlich nicht installiert\n" + +#: sm/import.c:652 +#, c-format +msgid "error running `%s': exit status %d\n" +msgstr "Fehler bei Ausführung von `%s': Endestatus %d\n" + +#: sm/import.c:655 +#, c-format +msgid "error running `%s': terminated\n" +msgstr "Fehler beim Ausführen von `%s': beendet\n" + +#: sm/keydb.c:189 #, c-format msgid "error creating keybox `%s': %s\n" msgstr "Die \"Keybox\" `%s' konnte nicht erstellt werden: %s\n" -#: sm/keydb.c:195 +#: sm/keydb.c:192 +msgid "you may want to start the gpg-agent first\n" +msgstr "Sie sollten zuerst den gpg-agent starten\n" + +#: sm/keydb.c:197 #, c-format msgid "keybox `%s' created\n" msgstr "Die \"Keybox\" `%s' wurde erstellt\n" -#: sm/keydb.c:218 +#: sm/keydb.c:220 #, c-format msgid "can't create lock for `%s'\n" msgstr "Datei `%s' konnte nicht gesperrt werden\n" -#: sm/keydb.c:1233 +#: sm/keydb.c:1312 sm/keydb.c:1374 msgid "failed to get the fingerprint\n" -msgstr "" +msgstr "Kann den Fingerprint nicht ermitteln\n" -#: sm/keydb.c:1240 +#: sm/keydb.c:1319 sm/keydb.c:1381 msgid "failed to allocate keyDB handle\n" -msgstr "" +msgstr "Kann keinen KeyDB Handler bereitstellen\n" -#: sm/keydb.c:1257 +#: sm/keydb.c:1336 #, c-format msgid "problem looking for existing certificate: %s\n" -msgstr "" +msgstr "Problem bei der Suche nach vorhandenem Zertifikat: %s\n" -#: sm/keydb.c:1265 +#: sm/keydb.c:1344 #, c-format msgid "error finding writable keyDB: %s\n" -msgstr "" +msgstr "Fehler bei der Suche nach einer schreibbaren KeyDB: %s\n" -#: sm/keydb.c:1273 +#: sm/keydb.c:1352 #, c-format msgid "error storing certificate: %s\n" -msgstr "" +msgstr "Fehler beim Speichern des Zertifikats: %s\n" -#: sm/verify.c:382 +#: sm/keydb.c:1396 +#, c-format +msgid "problem re-searching certificate: %s\n" +msgstr "Problem bei Wiederfinden des Zertifikats: %s\n" + +#: sm/keydb.c:1405 +#, c-format +msgid "error getting stored flags: %s\n" +msgstr "Fehler beim Holen der gespeicherten Flags: %s\n" + +#: sm/keydb.c:1414 +#, c-format +msgid "error storing flags: %s\n" +msgstr "Fehler beim Speichern der Flags: %s\n" + +#: sm/verify.c:387 msgid "Signature made " msgstr "Signatur erzeugt am " -#: sm/verify.c:386 +#: sm/verify.c:391 msgid "[date not given]" msgstr "[Datum nicht vorhanden]" -#: sm/verify.c:387 +#: sm/verify.c:392 #, c-format msgid " using certificate ID %08lX\n" msgstr "mittels Zertifikat ID %08lX\n" -#: sm/verify.c:497 +#: sm/verify.c:505 msgid "Good signature from" msgstr "Korrekte Signatur von" -#: sm/verify.c:498 +#: sm/verify.c:506 msgid " aka" msgstr " alias" +#: tools/gpgconf.c:55 +msgid "list all components" +msgstr "Liste aller Komponenten" + +#: tools/gpgconf.c:56 +msgid "|COMPONENT|list options" +msgstr "|KOMPONENTE|Zeige die Optionen an" + +#: tools/gpgconf.c:57 +msgid "|COMPONENT|change options" +msgstr "|KOMPONENTE|Ändere die Optionen" + +#: tools/gpgconf.c:63 +msgid "quiet" +msgstr "Weniger Ausgaben" + +#: tools/gpgconf.c:65 +msgid "activate changes at runtime, if possible" +msgstr "Aktiviere Änderungen zur Laufzeit; falls möglich" + +#: tools/gpgconf.c:88 +msgid "Usage: gpgconf [options] (-h for help)" +msgstr "Gebrauch: gpgconf [Optionen] (-h für Hilfe)" + +#: tools/gpgconf.c:91 +msgid "" +"Syntax: gpgconf [options]\n" +"Manage configuration options for tools of the GnuPG system\n" +msgstr "" +"Syntax: gpgconf {Optionen]\n" +"Verwalte Konfigurationsoptionen für Programme des GnuPG Systems\n" + +#: tools/gpgconf.c:180 +msgid "usage: gpgconf [options] " +msgstr "Gebrauch: gpgconf [Optionen] " + +#: tools/gpgconf.c:181 +msgid "Need one component argument" +msgstr "Benötige ein Komponenten Argument" + +#: tools/gpgconf.c:190 +msgid "Component not found" +msgstr "Komponente nicht gefunden" + +#~ msgid "Enter passphrase:" +#~ msgstr "Bitte das Mantra (Passphrase) eingeben:" + +#~ msgid "[error]" +#~ msgstr "[Fehler]" + +#~ msgid "no key usage specified - accepted for encryption\n" +#~ msgstr "" +#~ "Schlüsselverwendungszweck nicht vorhanden - wird zum Verschlüsseln " +#~ "akzeptiert\n" + #~ msgid " skipped new keys: %lu\n" #~ msgstr " übersprungene Schlüssel: %lu\n" diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 955724b3f..bc9d90b72 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -167,12 +167,12 @@ static void i18n_init (void) { #ifdef USE_SIMPLE_GETTEXT - set_gettext_file( PACKAGE ); + set_gettext_file( PACKAGE_GT ); #else #ifdef ENABLE_NLS setlocale (LC_ALL, ""); - bindtextdomain (PACKAGE, LOCALEDIR); - textdomain (PACKAGE); + bindtextdomain (PACKAGE_GT, LOCALEDIR); + textdomain (PACKAGE_GT); #endif #endif } diff --git a/sm/certdump.c b/sm/certdump.c index 30cd0bd53..aefaf27ef 100644 --- a/sm/certdump.c +++ b/sm/certdump.c @@ -633,7 +633,7 @@ gpgsm_format_keydesc (ksba_cert_t cert) #ifdef ENABLE_NLS /* The Assuan agent protol requires us to transmit utf-8 strings */ - orig_codeset = bind_textdomain_codeset (PACKAGE, NULL); + orig_codeset = bind_textdomain_codeset (PACKAGE_GT, NULL); #ifdef HAVE_LANGINFO_CODESET if (!orig_codeset) orig_codeset = nl_langinfo (CODESET); @@ -641,7 +641,7 @@ gpgsm_format_keydesc (ksba_cert_t cert) if (orig_codeset) { /* We only switch when we are able to restore the codeset later. */ orig_codeset = xstrdup (orig_codeset); - if (!bind_textdomain_codeset (PACKAGE, "utf-8")) + if (!bind_textdomain_codeset (PACKAGE_GT, "utf-8")) orig_codeset = NULL; } #endif @@ -659,7 +659,7 @@ gpgsm_format_keydesc (ksba_cert_t cert) #ifdef ENABLE_NLS if (orig_codeset) - bind_textdomain_codeset (PACKAGE, orig_codeset); + bind_textdomain_codeset (PACKAGE_GT, orig_codeset); #endif xfree (orig_codeset); diff --git a/sm/gpgsm.c b/sm/gpgsm.c index 761aefcee..047b356dd 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -540,7 +540,7 @@ static void i18n_init(void) { #ifdef USE_SIMPLE_GETTEXT - set_gettext_file (PACKAGE); + set_gettext_file (PACKAGE_GT); #else # ifdef ENABLE_NLS # ifdef HAVE_LC_MESSAGES @@ -549,8 +549,8 @@ i18n_init(void) # else setlocale (LC_ALL, "" ); # endif - bindtextdomain (PACKAGE, LOCALEDIR); - textdomain (PACKAGE); + bindtextdomain (PACKAGE_GT, LOCALEDIR); + textdomain (PACKAGE_GT); # endif #endif } @@ -1318,7 +1318,7 @@ main ( int argc, char **argv) case aCallDirmngr: if (!argc) - wrong_args (_("--call-dirmngr {args}")); + wrong_args ("--call-dirmngr {args}"); else if (gpgsm_dirmngr_run_command (&ctrl, *argv, argc-1, argv+1)) gpgsm_exit (1); @@ -1334,7 +1334,7 @@ main ( int argc, char **argv) else if (argc == 1) gpgsm_encrypt (&ctrl, recplist, open_read (*argv), stdout); /* from file */ else - wrong_args (_("--encrypt [datafile]")); + wrong_args ("--encrypt [datafile]"); break; case aSign: /* sign the given file */ @@ -1348,7 +1348,7 @@ main ( int argc, char **argv) gpgsm_sign (&ctrl, signerlist, open_read (*argv), detached_sig, stdout); /* from file */ else - wrong_args (_("--sign [datafile]")); + wrong_args ("--sign [datafile]"); break; case aSignEncr: /* sign and encrypt the given file */ @@ -1375,7 +1375,7 @@ main ( int argc, char **argv) else if (argc == 2) /* detached signature (sig, detached) */ gpgsm_verify (&ctrl, open_read (*argv), open_read (argv[1]), NULL); else - wrong_args (_("--verify [signature [detached_data]]")); + wrong_args ("--verify [signature [detached_data]]"); if (fp && fp != stdout) fclose (fp); @@ -1383,7 +1383,7 @@ main ( int argc, char **argv) break; case aVerifyFiles: - log_error ("this command has not yet been implemented\n"); + log_error (_("this command has not yet been implemented\n")); break; case aDecrypt: @@ -1392,7 +1392,7 @@ main ( int argc, char **argv) else if (argc == 1) gpgsm_decrypt (&ctrl, open_read (*argv), stdout); /* from file */ else - wrong_args (_("--decrypt [filename]")); + wrong_args ("--decrypt [filename]"); break; case aDeleteKey: @@ -1445,7 +1445,7 @@ main ( int argc, char **argv) if (argc == 1) gpgsm_p12_export (&ctrl, *argv, stdout); else - wrong_args (_("--export-secret-key-p12 KEY-ID")); + wrong_args ("--export-secret-key-p12 KEY-ID"); break; case aSendKeys: diff --git a/sm/import.c b/sm/import.c index 02d2e629f..c54579b7d 100644 --- a/sm/import.c +++ b/sm/import.c @@ -644,7 +644,7 @@ parse_p12 (ksba_reader_t reader, FILE **retfp) while ( (i=waitpid (pid, &status, 0)) == -1 && errno == EINTR) ; if (i == -1) - log_error (_("waiting for protect-tools to terminate failed: %s\n"), + log_error (_("waiting for protect-tool to terminate failed: %s\n"), strerror (errno)); else if (WIFEXITED (status) && WEXITSTATUS (status) == 31) log_error (_("error running `%s': probably not installed\n"), pgmname); diff --git a/tools/gpgconf.c b/tools/gpgconf.c index aab1397c7..7aca335fa 100644 --- a/tools/gpgconf.c +++ b/tools/gpgconf.c @@ -103,7 +103,7 @@ static void i18n_init(void) { #ifdef USE_SIMPLE_GETTEXT - set_gettext_file (PACKAGE); + set_gettext_file (PACKAGE_GT); #else # ifdef ENABLE_NLS # ifdef HAVE_LC_MESSAGES @@ -112,8 +112,8 @@ i18n_init(void) # else setlocale (LC_ALL, "" ); # endif - bindtextdomain (PACKAGE, LOCALEDIR); - textdomain (PACKAGE); + bindtextdomain (PACKAGE_GT, LOCALEDIR); + textdomain (PACKAGE_GT); # endif #endif } -- cgit From e950b01ff56c86d8e04b75a7bac4234afc939199 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 20 Apr 2004 14:17:10 +0000 Subject: * pcsc-wrapper.c: New. * Makefile.am (pkglib_PROGRAMS): Install it here. * apdu.c (writen, readn): New. (open_pcsc_reader, pcsc_send_apdu, close_pcsc_reader): Use the pcsc-wrapper if we are using Pth. --- scd/ChangeLog | 54 +++++ scd/Makefile.am | 16 +- scd/apdu.c | 688 ++++++++++++++++++++++++++++++++++++++++++++++++++++- scd/apdu.h | 11 +- scd/ccid-driver.c | 276 +++++++++++++++++---- scd/ccid-driver.h | 1 + scd/command.c | 69 ++++-- scd/iso7816.c | 3 + scd/pcsc-wrapper.c | 631 ++++++++++++++++++++++++++++++++++++++++++++++++ scd/scdaemon.c | 154 +++++++++++- scd/scdaemon.h | 2 + 11 files changed, 1827 insertions(+), 78 deletions(-) create mode 100644 scd/pcsc-wrapper.c (limited to 'scd/scdaemon.c') diff --git a/scd/ChangeLog b/scd/ChangeLog index 895aae966..9b04d5e78 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,57 @@ +2004-04-20 Werner Koch + + * pcsc-wrapper.c: New. + * Makefile.am (pkglib_PROGRAMS): Install it here. + * apdu.c (writen, readn): New. + (open_pcsc_reader, pcsc_send_apdu, close_pcsc_reader): Use the + pcsc-wrapper if we are using Pth. + +2004-04-19 Werner Koch + + * ccid-driver.c (parse_ccid_descriptor): Store some of the reader + features away. New arg HANDLE + (read_device_info): New arg HANDLE. Changed caller. + (bulk_in): Handle time extension requests. + (ccid_get_atr): Setup parameters and the IFSD. + (compute_edc): New. Factored out code. + (ccid_transceive): Use default NADs when required. + +2004-04-14 Werner Koch + + * scdaemon.h (server_control_s): Add member READER_SLOT. + * scdaemon.c (scd_init_default_ctrl): Initialize READER_SLOT to -1. + * command.c (open_card): Reuse an open slot. + (reset_notify): Just reset the slot if supported by the reader. + (do_reset): Factored code from above out. + (scd_command_handler): Use it for cleanup. + + * apdu.h: New pseudo stati SW_HOST_NOT_SUPPORTED, + SW_HOST_LOCKING_FAILED and SW_HOST_BUSY. + * iso7816.c (map_sw): Map it. + + * ccid-driver.c (ccid_slot_status): Add arg STATUSBITS. + * apdu.c (apdu_get_status): New. + (ct_get_status, pcsc_get_status, ocsc_get_status): New stubs. + (get_status_ccid): New. + (apdu_reset): New. + (reset_ct_reader, reset_pcsc_reader, reset_osc_reader): New stubs. + (reset_ccid_reader): New. + (apdu_enum_reader): New. + + * apdu.c (lock_slot, trylock_slot, unlock_slot): New helpers. + (new_reader_slot) [USE_GNU_PTH]: Init mutex. + (apdu_reset, apdu_get_status, apdu_send_le): Run functions + in locked mode. + + * command.c (scd_update_reader_status_file): New. + * scdaemon.c (handle_tick): Call it. + +2004-04-13 Werner Koch + + * scdaemon.c: Convert to a Pth application. + (handle_signal, ticker_thread, handle_tick): New. + (main): Fire up the ticker thread in server mode. + 2004-03-23 Werner Koch * scdaemon.c (main) : Fixed output for pcsc_driver. diff --git a/scd/Makefile.am b/scd/Makefile.am index da59a1997..c3c603d28 100644 --- a/scd/Makefile.am +++ b/scd/Makefile.am @@ -19,13 +19,14 @@ ## Process this file with automake to produce Makefile.in bin_PROGRAMS = scdaemon sc-investigate sc-copykeys +pkglib_PROGRAMS = pcsc-wrapper AM_CPPFLAGS = -I$(top_srcdir)/intl -I$(top_srcdir)/common include $(top_srcdir)/am/cmacros.am AM_CFLAGS = $(OPENSC_CFLAGS) $(LIBGCRYPT_CFLAGS) \ - $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) + $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) $(PTH_CFLAGS) card_apps = app-openpgp.c app-nks.c app-dinsig.c @@ -43,7 +44,8 @@ scdaemon_SOURCES = \ scdaemon_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \ - $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) \ + $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(PTH_LIBS) \ + $(KSBA_LIBS) $(LIBASSUAN_LIBS) \ $(LIBUSB_LIBS) -lgpg-error @INTLLIBS@ -ldl sc_investigate_SOURCES = \ @@ -57,7 +59,8 @@ sc_investigate_SOURCES = \ sc_investigate_LDADD = \ ../jnlib/libjnlib.a ../common/libcommon.a \ - $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBUSB_LIBS) \ + $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(PTH_LIBS) \ + $(KSBA_LIBS) $(LIBUSB_LIBS) \ @INTLLIBS@ -lgpg-error -ldl @@ -73,5 +76,10 @@ sc_copykeys_SOURCES = \ sc_copykeys_LDADD = \ ../jnlib/libjnlib.a ../common/libcommon.a \ ../common/libsimple-pwquery.a \ - $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBUSB_LIBS) \ + $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(PTH_LIBS) \ + $(KSBA_LIBS) $(LIBUSB_LIBS) \ -lgpg-error @INTLLIBS@ -ldl + +pcsc_wrapper_SOURCES = pcsc-wrapper.c +pcsc_wrapper_LDADD = -ldl +pcsc_wrapper_CFLAGS = \ No newline at end of file diff --git a/scd/apdu.c b/scd/apdu.c index 7843fd566..1b5ebc375 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -1,5 +1,5 @@ /* apdu.c - ISO 7816 APDU functions and low level I/O - * Copyright (C) 2003 Free Software Foundation, Inc. + * Copyright (C) 2003, 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -24,6 +24,11 @@ #include #include #include +#ifdef USE_GNU_PTH +# include +# include +# include +#endif #ifdef HAVE_OPENSC # include #endif @@ -48,6 +53,11 @@ #include "dynload.h" #include "ccid-driver.h" +#ifdef USE_GNU_PTH +#define NEED_PCSC_WRAPPER 1 +#endif + + #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). */ @@ -59,6 +69,12 @@ #define DLSTDCALL #endif +#ifdef _POSIX_OPEN_MAX +#define MAX_OPEN_FDS _POSIX_OPEN_MAX +#else +#define MAX_OPEN_FDS 20 +#endif + /* A structure to collect information pertaining to one reader slot. */ @@ -74,6 +90,11 @@ struct reader_table_s { unsigned long context; unsigned long card; unsigned long protocol; +#ifdef NEED_PCSC_WRAPPER + int req_fd; + int rsp_fd; + pid_t pid; +#endif /*NEED_PCSC_WRAPPER*/ } pcsc; #ifdef HAVE_OPENSC int is_osc; /* We are using the OpenSC driver layer. */ @@ -85,6 +106,11 @@ struct reader_table_s { int status; unsigned char atr[33]; size_t atrlen; + unsigned int change_counter; +#ifdef USE_GNU_PTH + int lock_initialized; + pth_mutex_t lock; +#endif }; typedef struct reader_table_s *reader_table_t; @@ -185,11 +211,27 @@ new_reader_slot (void) log_error ("new_reader_slot: out of slots\n"); return -1; } +#ifdef USE_GNU_PTH + if (!reader_table[reader].lock_initialized) + { + if (!pth_mutex_init (&reader_table[reader].lock)) + { + log_error ("error initializing mutex: %s\n", strerror (errno)); + return -1; + } + reader_table[reader].lock_initialized = 1; + } +#endif /*USE_GNU_PTH*/ 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; + reader_table[reader].pcsc.pid = (pid_t)(-1); #endif return reader; } @@ -370,6 +412,18 @@ close_ct_reader (int slot) 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 @@ -397,6 +451,66 @@ ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen, +#ifdef NEED_PCSC_WRAPPER +static int +writen (int fd, const void *buf, size_t nbytes) +{ + size_t nleft = nbytes; + int nwritten; + +/* log_printhex (" writen:", buf, nbytes); */ + + while (nleft > 0) + { +#ifdef USE_GNU_PTH + nwritten = pth_write (fd, buf, nleft); +#else + nwritten = write (fd, buf, nleft); +#endif + if (nwritten < 0 && errno == EINTR) + continue; + if (nwritten < 0) + return -1; + nleft -= nwritten; + buf = (const char*)buf + nwritten; + } + return 0; +} + +/* Read up to BUFLEN bytes from FD and return the number of bytes + actually read in NREAD. Returns -1 on error or 0 on success. */ +static int +readn (int fd, void *buf, size_t buflen, size_t *nread) +{ + size_t nleft = buflen; + int n; +/* void *orig_buf = buf; */ + + while (nleft > 0) + { +#ifdef USE_GNU_PTH + n = pth_read (fd, buf, nleft); +#else + n = read (fd, buf, nleft); +#endif + if (n < 0 && errno == EINTR) + continue; + if (n < 0) + return -1; /* read error. */ + if (!n) + break; /* EOF */ + nleft -= n; + buf = (char*)buf + n; + } + if (nread) + *nread = buflen - nleft; + +/* log_printhex (" readn:", orig_buf, *nread); */ + + return 0; +} +#endif /*NEED_PCSC_WRAPPER*/ + static const char * pcsc_error_string (long err) { @@ -457,6 +571,172 @@ pcsc_error_string (long err) static int open_pcsc_reader (const char *portstr) { +#ifdef NEED_PCSC_WRAPPER +/* Open the PC/SC reader using the pcsc_wrapper program. This is + needed to cope with different thread models and other peculiarities + of libpcsclite. */ + int slot; + reader_table_t slotp; + int fd, rp[2], wp[2]; + int n, i; + pid_t pid; + size_t len; + unsigned char msgbuf[9]; + int err; + + slot = new_reader_slot (); + if (slot == -1) + return -1; + slotp = reader_table + slot; + + /* Fire up the pcsc wrapper. We don't use any fork/exec code from + the common directy but implement it direclty so that this file + may still be source copied. */ + + if (pipe (rp) == -1) + { + log_error ("error creating a pipe: %s\n", strerror (errno)); + slotp->used = 0; + return -1; + } + if (pipe (wp) == -1) + { + log_error ("error creating a pipe: %s\n", strerror (errno)); + close (rp[0]); + close (rp[1]); + slotp->used = 0; + return -1; + } + + pid = fork (); + if (pid == -1) + { + log_error ("error forking process: %s\n", strerror (errno)); + close (rp[0]); + close (rp[1]); + close (wp[0]); + close (wp[1]); + slotp->used = 0; + return -1; + } + slotp->pcsc.pid = pid; + + if (!pid) + { /* + === Child === + */ + + /* Double fork. */ + pid = fork (); + if (pid == -1) + _exit (31); + if (pid) + _exit (0); /* Immediate exit this parent, so that the child + gets cleaned up by the init process. */ + + /* Connect our pipes. */ + if (wp[0] != 0 && dup2 (wp[0], 0) == -1) + log_fatal ("dup2 stdin failed: %s\n", strerror (errno)); + if (rp[1] != 1 && dup2 (rp[1], 1) == -1) + log_fatal ("dup2 stdout failed: %s\n", strerror (errno)); + + /* Send stderr to the bit bucket. */ + fd = open ("/dev/null", O_WRONLY); + if (fd == -1) + log_fatal ("can't open `/dev/null': %s", strerror (errno)); + if (fd != 2 && dup2 (fd, 2) == -1) + log_fatal ("dup2 stderr failed: %s\n", strerror (errno)); + + /* Close all other files. */ + n = sysconf (_SC_OPEN_MAX); + if (n < 0) + n = MAX_OPEN_FDS; + for (i=3; i < n; i++) + close(i); + errno = 0; + + execl (GNUPG_LIBDIR "/pcsc-wrapper", + "pcsc-wrapper", + "--", + "1", /* API version */ + opt.pcsc_driver, /* Name of the PC/SC library. */ + NULL); + _exit (31); + } + + /* + === Parent === + */ + close (wp[0]); + close (rp[1]); + slotp->pcsc.req_fd = wp[1]; + slotp->pcsc.rsp_fd = rp[0]; + + /* Wait for the intermediate child to terminate. */ + while ( (i=pth_waitpid (pid, NULL, 0)) == -1 && errno == EINTR) + ; + + /* Now send the open request. */ + msgbuf[0] = 0x01; /* OPEN command. */ + len = portstr? strlen (portstr):0; + msgbuf[1] = (len >> 24); + msgbuf[2] = (len >> 16); + msgbuf[3] = (len >> 8); + msgbuf[4] = (len ); + if ( writen (slotp->pcsc.req_fd, msgbuf, 5) + || (portstr && writen (slotp->pcsc.req_fd, portstr, len))) + { + log_error ("error sending PC/SC OPEN 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 OPEN response: %s\n", + i? strerror (errno) : "premature EOF"); + goto command_failed; + } + len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4]; + if (msgbuf[0] != 0x81 || len < 4) + { + log_error ("invalid response header from PC/SC received\n"); + goto command_failed; + } + len -= 4; /* Already read the error code. */ + if (len > DIM (slotp->atr)) + { + log_error ("PC/SC returned a too large ATR (len=%x)\n", len); + goto command_failed; + } + err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8]; + if (err) + { + log_error ("PC/SC OPEN failed: %s\n", pcsc_error_string (err)); + goto command_failed; + } + n = len; + if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n) + { + log_error ("error receiving PC/SC OPEN response: %s\n", + i? strerror (errno) : "premature EOF"); + goto command_failed; + } + slotp->atrlen = len; + + dump_reader_status (slot); + return slot; + + 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; int slot; char *list = NULL; @@ -559,9 +839,16 @@ open_pcsc_reader (const char *portstr) dump_reader_status (slot); return slot; +#endif /*!NEED_PCSC_WRAPPER */ } +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. */ @@ -569,6 +856,108 @@ 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; @@ -591,14 +980,87 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, 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; } @@ -661,6 +1123,46 @@ close_ccid_reader (int slot) } +static int +reset_ccid_reader (int slot) +{ + int err; + reader_table_t slotp = reader_table + slot; + unsigned char atr[33]; + size_t atrlen; + + err = ccid_get_atr (slotp->ccid.handle, atr, sizeof atr, &atrlen); + if (err) + return -1; + /* If the reset was successful, update the ATR. */ + assert (sizeof slotp->atr >= sizeof atr); + slotp->atrlen = atrlen; + memcpy (slotp->atr, atr, atrlen); + dump_reader_status (slot); + return 0; +} + + +static int +get_status_ccid (int slot, unsigned int *status) +{ + int rc; + int bits; + + rc = ccid_slot_status (reader_table[slot].ccid.handle, &bits); + if (rc) + return -1; + + if (bits == 0) + *status = 1|2|4; + else if (bits == 1) + *status = 2; + else + *status = 0; + + return 0; +} + /* Actually send the APDU of length APDULEN to SLOT and return a maximum of *BUFLEN data in BUFFER, the actual returned size will be @@ -798,6 +1300,18 @@ close_osc_reader (int slot) return 0; } +static int +reset_osc_reader (int slot) +{ + return SW_HOST_NOT_SUPPORTED; +} + + +static int +ocsc_get_status (int slot, unsigned int *status) +{ + return SW_HOST_NOT_SUPPORTED; +} /* Actually send the APDU of length APDULEN to SLOT and return a @@ -896,6 +1410,45 @@ osc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, Driver Access */ + +static int +lock_slot (int slot) +{ +#ifdef USE_GNU_PTH + if (!pth_mutex_acquire (&reader_table[slot].lock, 0, NULL)) + { + log_error ("failed to acquire apdu lock: %s\n", strerror (errno)); + return SW_HOST_LOCKING_FAILED; + } +#endif /*USE_GNU_PTH*/ + return 0; +} + +static int +trylock_slot (int slot) +{ +#ifdef USE_GNU_PTH + if (!pth_mutex_acquire (&reader_table[slot].lock, TRUE, NULL)) + { + if (errno == EBUSY) + return SW_HOST_BUSY; + log_error ("failed to acquire apdu lock: %s\n", strerror (errno)); + return SW_HOST_LOCKING_FAILED; + } +#endif /*USE_GNU_PTH*/ + return 0; +} + +static void +unlock_slot (int slot) +{ +#ifdef USE_GNU_PTH + if (!pth_mutex_release (&reader_table[slot].lock)) + log_error ("failed to release apdu lock: %s\n", strerror (errno)); +#endif /*USE_GNU_PTH*/ +} + + /* 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 PC/SC the first listed reader). If @@ -937,7 +1490,7 @@ apdu_open_reader (const char *portstr) handle = dlopen (opt.ctapi_driver, RTLD_LAZY); if (!handle) { - log_error ("apdu_open_reader: failed to open driver: %s", + log_error ("apdu_open_reader: failed to open driver: %s\n", dlerror ()); return -1; } @@ -959,12 +1512,13 @@ apdu_open_reader (const char *portstr) /* No ctAPI configured, so lets try the PC/SC API */ if (!pcsc_api_loaded) { +#ifndef NEED_PCSC_WRAPPER void *handle; handle = dlopen (opt.pcsc_driver, RTLD_LAZY); if (!handle) { - log_error ("apdu_open_reader: failed to open driver `%s': %s", + log_error ("apdu_open_reader: failed to open driver `%s': %s\n", opt.pcsc_driver, dlerror ()); return -1; } @@ -1020,9 +1574,10 @@ apdu_open_reader (const char *portstr) dlclose (handle); return -1; } +#endif /*!NEED_PCSC_WRAPPER*/ pcsc_api_loaded = 1; } - + return open_pcsc_reader (portstr); } @@ -1046,6 +1601,47 @@ apdu_close_reader (int slot) return close_pcsc_reader (slot); } +/* Enumerate all readers and return information on whether this reader + is in use. The caller should start with SLOT set to 0 and + increment it with each call until an error is returned. */ +int +apdu_enum_reader (int slot, int *used) +{ + if (slot < 0 || slot >= MAX_READER) + return SW_HOST_NO_DRIVER; + *used = reader_table[slot].used; + return 0; +} + +/* Do a reset for the card in reader at SLOT. */ +int +apdu_reset (int slot) +{ + int sw; + + if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) + return SW_HOST_NO_DRIVER; + + 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); + + unlock_slot (slot); + return sw; +} + unsigned char * apdu_get_atr (int slot, size_t *atrlen) @@ -1062,6 +1658,7 @@ apdu_get_atr (int slot, size_t *atrlen) *atrlen = reader_table[slot].atrlen; return buf; } + static const char * @@ -1084,7 +1681,62 @@ error_string (int slot, long rc) } -/* Dispatcher for the actual send_apdu fucntion. */ +/* Retrieve the status for SLOT. The function does obnly wait fot the + card to become available if HANG is set to true. On success the + bits in STATUS will be set to + + bit 0 = card present and usable + bit 1 = card present + bit 2 = card active + bit 3 = card access locked [not yet implemented] + + For must application, tetsing 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 + change. +*/ +int +apdu_get_status (int slot, int hang, + unsigned int *status, unsigned int *changed) +{ + int sw; + unsigned int s; + + if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) + return SW_HOST_NO_DRIVER; + + 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); + + unlock_slot (slot); + + if (sw) + return sw; + + if (status) + *status = s; + if (changed) + *changed = reader_table[slot].change_counter; + return 0; +} + + +/* Dispatcher for the actual send_apdu function. Note, that this + function should be called in locked state. */ static int send_apdu (int slot, unsigned char *apdu, size_t apdulen, unsigned char *buffer, size_t *buflen) @@ -1126,6 +1778,9 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, int sw; long rc; /* we need a long here due to PC/SC. */ + if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) + return SW_HOST_NO_DRIVER; + if (DBG_CARD_IO) log_debug ("send apdu: c=%02X i=%02X p0=%02X p1=%02X lc=%d le=%d\n", class, ins, p0, p1, lc, le); @@ -1137,6 +1792,9 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, if ((!data && lc != -1) || (data && lc == -1)) return SW_HOST_INV_VALUE; + if ((sw = lock_slot (slot))) + return sw; + apdulen = 0; apdu[apdulen++] = class; apdu[apdulen++] = ins; @@ -1158,6 +1816,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, { log_error ("apdu_send_simple(%d) failed: %s\n", slot, error_string (slot, rc)); + unlock_slot (slot); return SW_HOST_INCOMPLETE_CARD_RESPONSE; } sw = (result[resultlen-2] << 8) | result[resultlen-1]; @@ -1176,7 +1835,10 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, { *retbuf = xtrymalloc (resultlen? resultlen : 1); if (!*retbuf) - return SW_HOST_OUT_OF_CORE; + { + unlock_slot (slot); + return SW_HOST_OUT_OF_CORE; + } *retbuflen = resultlen; memcpy (*retbuf, result, resultlen); } @@ -1192,7 +1854,10 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, { *retbuf = p = xtrymalloc (bufsize); if (!*retbuf) - return SW_HOST_OUT_OF_CORE; + { + unlock_slot (slot); + return SW_HOST_OUT_OF_CORE; + } assert (resultlen < bufsize); memcpy (p, result, resultlen); p += resultlen; @@ -1216,6 +1881,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, { log_error ("apdu_send_simple(%d) for get response failed: %s\n", slot, error_string (slot, rc)); + unlock_slot (slot); return SW_HOST_INCOMPLETE_CARD_RESPONSE; } sw = (result[resultlen-2] << 8) | result[resultlen-1]; @@ -1236,7 +1902,10 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, bufsize += resultlen > 4096? resultlen: 4096; tmp = xtryrealloc (*retbuf, bufsize); if (!tmp) - return SW_HOST_OUT_OF_CORE; + { + unlock_slot (slot); + return SW_HOST_OUT_OF_CORE; + } p = tmp + (p - *retbuf); *retbuf = tmp; } @@ -1259,6 +1928,9 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, *retbuf = tmp; } } + + unlock_slot (slot); + if (DBG_CARD_IO && retbuf && sw == SW_SUCCESS) log_printhex (" dump: ", *retbuf, *retbuflen); diff --git a/scd/apdu.h b/scd/apdu.h index fd7634f13..f74bab7fe 100644 --- a/scd/apdu.h +++ b/scd/apdu.h @@ -48,9 +48,12 @@ enum { those values can't be issued by a card. */ SW_HOST_OUT_OF_CORE = 0x10001, /* No way yet to differentiate between errnos on a failed malloc. */ - SW_HOST_INV_VALUE = 0x10002, + SW_HOST_INV_VALUE = 0x10002, SW_HOST_INCOMPLETE_CARD_RESPONSE = 0x10003, - SW_HOST_NO_DRIVER = 0x10004 + SW_HOST_NO_DRIVER = 0x10004, + SW_HOST_NOT_SUPPORTED = 0x10005, + SW_HOST_LOCKING_FAILED= 0x10006, + SW_HOST_BUSY = 0x10007 }; @@ -58,10 +61,14 @@ enum { /* Note , that apdu_open_reader returns no status word but -1 on error. */ int apdu_open_reader (const char *portstr); int apdu_close_reader (int slot); +int apdu_enum_reader (int slot, int *used); unsigned char *apdu_get_atr (int slot, size_t *atrlen); /* The apdu send functions do return status words. */ +int apdu_reset (int slot); +int apdu_get_status (int slot, int hang, + unsigned int *status, unsigned int *changed); int apdu_send_simple (int slot, int class, int ins, int p0, int p1, int lc, const char *data); int apdu_send (int slot, int class, int ins, int p0, int p1, diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c index b398e3ce3..1c8167869 100644 --- a/scd/ccid-driver.c +++ b/scd/ccid-driver.c @@ -1,5 +1,5 @@ /* ccid-driver.c - USB ChipCardInterfaceDevices driver - * Copyright (C) 2003 Free Software Foundation, Inc. + * Copyright (C) 2003, 2004 Free Software Foundation, Inc. * Written by Werner Koch. * * This file is part of GnuPG. @@ -108,7 +108,10 @@ /* Disable all debugging output for now. */ #undef DBG_CARD_IO -#define DBG_CARD_IO 0 +#define DBG_CARD_IO 1 + +/* Define to print information pertaining the T=1 protocol. */ +#undef DEBUG_T1 # define DEBUGOUT(t) do { if (DBG_CARD_IO) \ @@ -145,8 +148,6 @@ #endif /* This source not used by scdaemon. */ -/* Define to print information pertaining the T=1 protocol. */ -#undef DEBUG_T1 @@ -184,9 +185,15 @@ struct ccid_driver_s { int seqno; unsigned char t1_ns; unsigned char t1_nr; + int nonnull_nad; + int auto_ifsd; + int max_ifsd; + int ifsd; }; +static unsigned int compute_edc (const unsigned char *data, size_t datalen, + int use_crc); static int bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen); static int bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, size_t *nread, int expected_type, int seqno); @@ -220,13 +227,18 @@ set_msg_len (unsigned char *msg, unsigned int length) Note, that this code is based on the one in lsusb.c of the usb-utils package, I wrote on 2003-09-01. -wk. */ static int -parse_ccid_descriptor (const unsigned char *buf, size_t buflen) +parse_ccid_descriptor (ccid_driver_t handle, + const unsigned char *buf, size_t buflen) { unsigned int i; unsigned int us; int have_t1 = 0, have_tpdu=0, have_auto_conf = 0; + handle->nonnull_nad = 0; + handle->auto_ifsd = 0; + handle->max_ifsd = 32; + handle->ifsd = 0; if (buflen < 54 || buf[0] < 54) { DEBUGOUT ("CCID device descriptor is too short\n"); @@ -272,6 +284,7 @@ parse_ccid_descriptor (const unsigned char *buf, size_t buflen) us = convert_le_u32(buf+28); DEBUGOUT_1 (" dwMaxIFSD %5u\n", us); + handle->max_ifsd = us; us = convert_le_u32(buf+32); DEBUGOUT_1 (" dwSyncProtocols %08X ", us); @@ -320,9 +333,15 @@ parse_ccid_descriptor (const unsigned char *buf, size_t buflen) if ((us & 0x0100)) DEBUGOUT (" CCID can set ICC in clock stop mode\n"); if ((us & 0x0200)) - DEBUGOUT (" NAD value other than 0x00 accpeted\n"); + { + DEBUGOUT (" NAD value other than 0x00 accepted\n"); + handle->nonnull_nad = 1; + } if ((us & 0x0400)) - DEBUGOUT (" Auto IFSD exchange\n"); + { + DEBUGOUT (" Auto IFSD exchange\n"); + handle->auto_ifsd = 1; + } if ((us & 0x00010000)) { @@ -389,7 +408,7 @@ parse_ccid_descriptor (const unsigned char *buf, size_t buflen) that the device is usable for us. Returns 0 on success or an error code. */ static int -read_device_info (struct usb_device *dev) +read_device_info (ccid_driver_t handle, struct usb_device *dev) { int cfg_no; @@ -414,8 +433,9 @@ read_device_info (struct usb_device *dev) { if (ifcdesc->extra) { - if (!parse_ccid_descriptor (ifcdesc->extra, - ifcdesc->extralen)) + if (!parse_ccid_descriptor (handle, + ifcdesc->extra, + ifcdesc->extralen)) return 0; /* okay. we can use it. */ } } @@ -458,10 +478,22 @@ ccid_open_reader (ccid_driver_t *handle, int readerno) dev->descriptor->idVendor, dev->descriptor->idProduct); if (!readerno) { - rc = read_device_info (dev); + *handle = calloc (1, sizeof **handle); + if (!*handle) + { + DEBUGOUT ("out of memory\n"); + rc = -1; + free (*handle); + *handle = NULL; + goto leave; + } + + rc = read_device_info (*handle, dev); if (rc) { DEBUGOUT ("device not supported\n"); + free (*handle); + *handle = NULL; goto leave; } @@ -469,6 +501,8 @@ ccid_open_reader (ccid_driver_t *handle, int readerno) if (rc) { DEBUGOUT_1 ("usb_open failed: %d\n", rc); + free (*handle); + *handle = NULL; goto leave; } @@ -479,16 +513,11 @@ ccid_open_reader (ccid_driver_t *handle, int readerno) if (rc) { DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc); + free (*handle); + *handle = NULL; goto leave; } - *handle = calloc (1, sizeof **handle); - if (!*handle) - { - DEBUGOUT ("out of memory\n"); - rc = -1; - goto leave; - } (*handle)->idev = idev; idev = NULL; /* FIXME: Do we need to get the endpoint addresses from the @@ -508,7 +537,7 @@ ccid_open_reader (ccid_driver_t *handle, int readerno) usb_free_match (match); if (!rc && !*handle) - rc = -1; /* In case we didn't enter the while lool at all. */ + rc = -1; /* In case we didn't enter the while loop at all. */ return rc; } @@ -592,6 +621,7 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, int i, rc; size_t msglen; + retry: rc = usb_bulk_read (handle->idev, 0x82, buffer, length, @@ -628,6 +658,14 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, return -1; } + if ( !(buffer[7] & 0x03) && (buffer[7] & 0xC0) == 0x80) + { + /* Card present and active, time extension requested. */ + DEBUGOUT_2 ("time extension requested (%02X,%02X)\n", + buffer[7], buffer[8]); + goto retry; + } + DEBUGOUT_3 ("status: %02X error: %02X octet[9]: %02X\n" " data:", buffer[7], buffer[8], buffer[9] ); for (i=10; i < msglen; i++) @@ -695,7 +733,7 @@ ccid_poll (ccid_driver_t handle) int -ccid_slot_status (ccid_driver_t handle) +ccid_slot_status (ccid_driver_t handle, int *statusbits) { int rc; unsigned char msg[100]; @@ -716,6 +754,7 @@ ccid_slot_status (ccid_driver_t handle) rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, seqno); if (rc) return rc; + *statusbits = (msg[7] & 3); return 0; } @@ -727,8 +766,12 @@ ccid_get_atr (ccid_driver_t handle, { int rc; unsigned char msg[100]; - size_t msglen; + unsigned char *tpdu; + size_t msglen, tpdulen; unsigned char seqno; + int use_crc = 0; + unsigned int edc; + int i; msg[0] = PC_to_RDR_IccPowerOn; msg[5] = 0; /* slot */ @@ -756,11 +799,135 @@ ccid_get_atr (ccid_driver_t handle, *atrlen = n; } + /* Setup parameters to select T=1. */ + msg[0] = PC_to_RDR_SetParameters; + msg[5] = 0; /* slot */ + msg[6] = seqno = handle->seqno++; + msg[7] = 1; /* Select T=1. */ + msg[8] = 0; /* RFU */ + msg[9] = 0; /* RFU */ + + /* FIXME: Get those values from the ATR. */ + msg[10]= 0x01; /* Fi/Di */ + msg[11]= 0x10; /* LRC, direct convention. */ + msg[12]= 0; /* Extra guardtime. */ + msg[13]= 0x41; /* BWI/CWI */ + msg[14]= 0; /* No clock stoppping. */ + msg[15]= 254; /* IFSC */ + msg[16]= 0; /* Does not support non default NAD values. */ + set_msg_len (msg, 7); + msglen = 10 + 7; + + DEBUGOUT ("sending"); + for (i=0; i < msglen; i++) + DEBUGOUT_CONT_1 (" %02X", msg[i]); + DEBUGOUT_LF (); + + rc = bulk_out (handle, msg, msglen); + if (rc) + return rc; + /* Note that we ignore the error code on purpose. */ + bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters, seqno); + + + /* Send an S-Block with our maximun IFSD to the CCID. */ + if (!handle->auto_ifsd) + { + tpdu = msg+10; + /* NAD: DAD=1, SAD=0 */ + tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0; + tpdu[1] = (0xc0 | 0 | 1); /* S-block request: change IFSD */ + tpdu[2] = 1; + tpdu[3] = handle->max_ifsd? handle->max_ifsd : 32; + tpdulen = 4; + edc = compute_edc (tpdu, tpdulen, use_crc); + if (use_crc) + tpdu[tpdulen++] = (edc >> 8); + tpdu[tpdulen++] = edc; + + msg[0] = PC_to_RDR_XfrBlock; + msg[5] = 0; /* slot */ + msg[6] = seqno = handle->seqno++; + msg[7] = 0; + msg[8] = 0; /* RFU */ + msg[9] = 0; /* RFU */ + set_msg_len (msg, tpdulen); + msglen = 10 + tpdulen; + + DEBUGOUT ("sending"); + for (i=0; i < msglen; i++) + DEBUGOUT_CONT_1 (" %02X", msg[i]); + DEBUGOUT_LF (); + +#ifdef DEBUG_T1 + fprintf (stderr, "T1: put %c-block seq=%d\n", + ((msg[11] & 0xc0) == 0x80)? 'R' : + (msg[11] & 0x80)? 'S' : 'I', + ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40))); +#endif + + rc = bulk_out (handle, msg, msglen); + if (rc) + return rc; + + /* Fixme: The next line for the current Valgrid without support + for USB IOCTLs. */ + memset (msg, 0, sizeof msg); + + rc = bulk_in (handle, msg, sizeof msg, &msglen, + RDR_to_PC_DataBlock, seqno); + if (rc) + return rc; + + tpdu = msg + 10; + tpdulen = msglen - 10; + + if (tpdulen < 4) + { + DEBUGOUT ("cannot yet handle short blocks!\n"); + return -1; + } + +#ifdef DEBUG_T1 + fprintf (stderr, "T1: got %c-block seq=%d err=%d\n", + ((msg[11] & 0xc0) == 0x80)? 'R' : + (msg[11] & 0x80)? 'S' : 'I', + ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)), + ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0 + ); +#endif + if ((tpdu[1] & 0xe0) != 0xe0 || tpdu[2] != 1) + { + DEBUGOUT ("invalid response for S-block (Change-IFSD)\n"); + return -1; + } + DEBUGOUT_1 ("IFSD has been set to %d\n", tpdu[3]); + } + return 0; } + +static unsigned int +compute_edc (const unsigned char *data, size_t datalen, int use_crc) +{ + if (use_crc) + { + return 0x42; /* Not yet implemented. */ + } + else + { + unsigned char crc = 0; + + for (; datalen; datalen--) + crc ^= *data++; + return crc; + } +} + + /* Protocol T=1 overview @@ -819,17 +986,19 @@ ccid_transceive (ccid_driver_t handle, unsigned char *resp, size_t maxresplen, size_t *nresp) { int rc; - unsigned char send_buffer[10+258], recv_buffer[10+258]; + unsigned char send_buffer[10+259], recv_buffer[10+259]; const unsigned char *apdu; size_t apdulen; unsigned char *msg, *tpdu, *p; - size_t msglen, tpdulen, n; + size_t msglen, tpdulen, last_tpdulen, n; unsigned char seqno; int i; - unsigned char crc; + unsigned int edc; + int use_crc = 0; size_t dummy_nresp; int next_chunk = 1; int sending = 1; + int retries = 0; if (!nresp) nresp = &dummy_nresp; @@ -852,7 +1021,8 @@ ccid_transceive (ccid_driver_t handle, return -1; /* Invalid length. */ tpdu = msg+10; - tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */ + /* NAD: DAD=1, SAD=0 */ + tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0; tpdu[1] = ((handle->t1_ns & 1) << 6); /* I-block */ if (apdulen > 128 /* fixme: replace by ifsc */) { @@ -863,12 +1033,11 @@ ccid_transceive (ccid_driver_t handle, } tpdu[2] = apdulen; memcpy (tpdu+3, apdu, apdulen); - crc = 0; - for (i=0,p=tpdu; i < apdulen+3; i++) - crc ^= *p++; - tpdu[3+apdulen] = crc; - - tpdulen = apdulen + 4; + tpdulen = 3 + apdulen; + edc = compute_edc (tpdu, tpdulen, use_crc); + if (use_crc) + tpdu[tpdulen++] = (edc >> 8); + tpdu[tpdulen++] = edc; } msg[0] = PC_to_RDR_XfrBlock; @@ -879,6 +1048,7 @@ ccid_transceive (ccid_driver_t handle, msg[9] = 0; /* RFU */ set_msg_len (msg, tpdulen); msglen = 10 + tpdulen; + last_tpdulen = tpdulen; DEBUGOUT ("sending"); for (i=0; i < msglen; i++) @@ -926,7 +1096,7 @@ ccid_transceive (ccid_driver_t handle, if (!(tpdu[1] & 0x80)) { /* This is an I-block. */ - + retries = 0; if (sending) { /* last block sent was successful. */ handle->t1_ns ^= 1; @@ -937,13 +1107,15 @@ ccid_transceive (ccid_driver_t handle, { /* Reponse does not match our sequence number. */ msg = send_buffer; tpdu = msg+10; - tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */ + /* NAD: DAD=1, SAD=0 */ + tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0; tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4 | 2); /* R-block */ tpdu[2] = 0; tpdulen = 3; - for (crc=0,i=0,p=tpdu; i < tpdulen; i++) - crc ^= *p++; - tpdu[tpdulen++] = crc; + edc = compute_edc (tpdu, tpdulen, use_crc); + if (use_crc) + tpdu[tpdulen++] = (edc >> 8); + tpdu[tpdulen++] = edc; continue; } @@ -974,20 +1146,27 @@ ccid_transceive (ccid_driver_t handle, msg = send_buffer; tpdu = msg+10; - tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */ + /* NAD: DAD=1, SAD=0 */ + tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0; tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4); /* R-block */ tpdu[2] = 0; tpdulen = 3; - for (crc=0,i=0,p=tpdu; i < tpdulen; i++) - crc ^= *p++; - tpdu[tpdulen++] = crc; - + edc = compute_edc (tpdu, tpdulen, use_crc); + if (use_crc) + tpdu[tpdulen++] = (edc >> 8); + tpdu[tpdulen++] = edc; } else if ((tpdu[1] & 0xc0) == 0x80) { /* This is a R-block. */ if ( (tpdu[1] & 0x0f)) { /* Error: repeat last block */ + if (++retries > 3) + { + DEBUGOUT ("3 failed retries\n"); + return -1; + } msg = send_buffer; + tpdulen = last_tpdulen; } else if (sending && !!(tpdu[1] & 0x40) == handle->t1_ns) { /* Reponse does not match our sequence number. */ @@ -996,6 +1175,7 @@ ccid_transceive (ccid_driver_t handle, } else if (sending) { /* Send next chunk. */ + retries = 0; msg = send_buffer; next_chunk = 1; handle->t1_ns ^= 1; @@ -1008,6 +1188,7 @@ ccid_transceive (ccid_driver_t handle, } else { /* This is a S-block. */ + retries = 0; DEBUGOUT_2 ("T1 S-block %s received cmd=%d\n", (tpdu[1] & 0x20)? "response": "request", (tpdu[1] & 0x1f)); @@ -1016,14 +1197,16 @@ ccid_transceive (ccid_driver_t handle, unsigned char bwi = tpdu[3]; msg = send_buffer; tpdu = msg+10; - tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */ + /* NAD: DAD=1, SAD=0 */ + tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0; tpdu[1] = (0xc0 | 0x20 | 3); /* S-block response */ tpdu[2] = 1; tpdu[3] = bwi; tpdulen = 4; - for (crc=0,i=0,p=tpdu; i < tpdulen; i++) - crc ^= *p++; - tpdu[tpdulen++] = crc; + edc = compute_edc (tpdu, tpdulen, use_crc); + if (use_crc) + tpdu[tpdulen++] = (edc >> 8); + tpdu[tpdulen++] = edc; DEBUGOUT_1 ("T1 waittime extension of bwi=%d\n", bwi); } else @@ -1043,6 +1226,7 @@ main (int argc, char **argv) { int rc; ccid_driver_t ccid; + unsigned int slotstat; rc = ccid_open_reader (&ccid, 0); if (rc) @@ -1056,7 +1240,7 @@ main (int argc, char **argv) ccid_poll (ccid); fputs ("getting slot status ...\n", stderr); - rc = ccid_slot_status (ccid); + rc = ccid_slot_status (ccid, &slotstat); if (rc) return 1; diff --git a/scd/ccid-driver.h b/scd/ccid-driver.h index e33be55d9..8b86eb1a5 100644 --- a/scd/ccid-driver.h +++ b/scd/ccid-driver.h @@ -63,6 +63,7 @@ int ccid_open_reader (ccid_driver_t *handle, int readerno); int ccid_close_reader (ccid_driver_t handle); int ccid_get_atr (ccid_driver_t handle, unsigned char *atr, size_t maxatrlen, size_t *atrlen); +int ccid_slot_status (ccid_driver_t handle, int *statusbits); int ccid_transceive (ccid_driver_t handle, const unsigned char *apdu, size_t apdulen, unsigned char *resp, size_t maxresplen, size_t *nresp); diff --git a/scd/command.c b/scd/command.c index 4746e11b5..06ff6d2e2 100644 --- a/scd/command.c +++ b/scd/command.c @@ -56,14 +56,11 @@ has_option (const char *line, const char *name) } - - -/* Note, that this reset_notify is also used for cleanup purposes. */ +/* Reset the card and free the application context. With DO_CLOSE set + to true, close the reader and don't do just a reset. */ static void -reset_notify (ASSUAN_CONTEXT ctx) +do_reset (ctrl_t ctrl, int do_close) { - CTRL ctrl = assuan_get_pointer (ctx); - if (ctrl->card_ctx) { card_close (ctrl->card_ctx); @@ -73,13 +70,28 @@ reset_notify (ASSUAN_CONTEXT ctx) } if (ctrl->app_ctx) { - int slot = ctrl->app_ctx->slot; release_application (ctrl->app_ctx); ctrl->app_ctx = NULL; - apdu_close_reader (slot); + } + if (ctrl->reader_slot != -1) + { + if (do_close || apdu_reset (ctrl->reader_slot)) + { + apdu_close_reader (ctrl->reader_slot); + ctrl->reader_slot = -1; + } } } + +static void +reset_notify (ASSUAN_CONTEXT ctx) +{ + CTRL ctrl = assuan_get_pointer (ctx); + + do_reset (ctrl, 0); +} + static int option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value) @@ -92,7 +104,7 @@ option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value) function returns an Assuan error, so don't map the error a second time */ static AssuanError -open_card (CTRL ctrl, const char *apptype) +open_card (ctrl_t ctrl, const char *apptype) { int slot; @@ -101,13 +113,13 @@ open_card (CTRL ctrl, const char *apptype) if (ctrl->card_ctx) return 0; /* Already initialized using a card context. */ - slot = apdu_open_reader (opt.reader_port); + if (ctrl->reader_slot != -1) + slot = ctrl->reader_slot; + else + slot = apdu_open_reader (opt.reader_port); + ctrl->reader_slot = slot; if (slot != -1) - { - ctrl->app_ctx = select_application (ctrl, slot, apptype); - if (!ctrl->app_ctx) - apdu_close_reader (slot); - } + ctrl->app_ctx = select_application (ctrl, slot, apptype); if (!ctrl->app_ctx) { /* No application found - fall back to old mode. */ /* Note that we should rework the old code to use the @@ -1084,6 +1096,12 @@ scd_command_handler (int listen_fd) if (DBG_ASSUAN) assuan_set_log_stream (ctx, log_get_stream ()); + /* We open the reader right at startup so that the ticker is able to + update the status file. */ + if (ctrl.reader_slot == -1) + ctrl.reader_slot = apdu_open_reader (opt.reader_port); + + /* Command processing loop. */ for (;;) { rc = assuan_accept (ctx); @@ -1104,7 +1122,7 @@ scd_command_handler (int listen_fd) continue; } } - reset_notify (ctx); /* used for cleanup */ + do_reset (&ctrl, 1); /* Cleanup. */ assuan_deinit_server (ctx); } @@ -1156,3 +1174,22 @@ send_status_info (CTRL ctrl, const char *keyword, ...) va_end (arg_ptr); } + + +void +scd_update_reader_status_file (void) +{ + int slot; + int used; + unsigned int status, changed; + + /* Note, that we only try to get the status, becuase it does not + make sense to wait here for a operation to complete. If we are + so busy working with the card, delays in the status file updated + are should be acceptable. */ + for (slot=0; !apdu_enum_reader (slot, &used); slot++) + if (used && !apdu_get_status (slot, 0, &status, &changed)) + { + log_info ("status of slot %d is %u\n", slot, status); + } +} diff --git a/scd/iso7816.c b/scd/iso7816.c index 24361d148..fd3f0485c 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -82,6 +82,9 @@ map_sw (int sw) case SW_HOST_OUT_OF_CORE: ec = GPG_ERR_ENOMEM; break; case SW_HOST_INV_VALUE: ec = GPG_ERR_INV_VALUE; break; case SW_HOST_INCOMPLETE_CARD_RESPONSE: ec = GPG_ERR_CARD; break; + 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; default: if ((sw & 0x010000)) ec = GPG_ERR_GENERAL; /* Should not happen. */ diff --git a/scd/pcsc-wrapper.c b/scd/pcsc-wrapper.c new file mode 100644 index 000000000..4f47ee95c --- /dev/null +++ b/scd/pcsc-wrapper.c @@ -0,0 +1,631 @@ +/* pcsc-wrapper.c - Wrapper for ccessing the PC/SC service + * Copyright (C) 2003, 2004 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 + */ + +/* + This wrapper is required to handle problems with the libpscslite + library. That library assumes that pthreads are used and fails + badly if one tries to use it with a procerss using Pth. + + The operation model is pretty simple: It reads requests from stdin + and returns the answer on stdout. There is no direct mapping to the + pcsc interface but to a higher level one which resembles the code + used in scdaemon (apdu.c) when not using Pth or while running under + Windows. + + The interface is binary consisting of a command tag and the length + of the parameter list. The calling process needs to pass the + version number of the interface on the command line to make sure + that both agree on the same interface. For each port a separate + instance of this process needs to be started. + +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + + +#define PGM "pcsc-wrapper" + +/* Allow for a standalone build. */ +#ifdef VERSION +#define MYVERSION_LINE PGM " (GnuPG) " VERSION +#define BUGREPORT_LINE "\nReport bugs to .\n" +#else +#define MYVERSION_LINE PGM +#define BUGREPORT_LINE "" +#endif + +#define DEFAULT_PCSC_DRIVER "libpcsclite.so" + + +static int verbose; + + +/* PC/SC constants and function pointer. */ +#define PCSC_SCOPE_USER 0 +#define PCSC_SCOPE_TERMINAL 1 +#define PCSC_SCOPE_SYSTEM 2 +#define PCSC_SCOPE_GLOBAL 3 + +#define PCSC_PROTOCOL_T0 1 +#define PCSC_PROTOCOL_T1 2 +#define PCSC_PROTOCOL_RAW 4 + +#define PCSC_SHARE_EXCLUSIVE 1 +#define PCSC_SHARE_SHARED 2 +#define PCSC_SHARE_DIRECT 3 + +#define PCSC_LEAVE_CARD 0 +#define PCSC_RESET_CARD 1 +#define PCSC_UNPOWER_CARD 2 +#define PCSC_EJECT_CARD 3 + +struct pcsc_io_request_s { + unsigned long protocol; + unsigned long pci_len; +}; + +typedef struct pcsc_io_request_s *pcsc_io_request_t; + + +static int driver_is_open; /* True if the PC/SC driver has been + initialzied and is ready for + operations. The follwoing variables + are then valid. */ +static unsigned long pcsc_context; /* The current PC/CS context. */ +static unsigned long pcsc_card; +static unsigned long pcsc_protocol; +static unsigned char current_atr[33]; +static size_t current_atrlen; + +long (* pcsc_establish_context) (unsigned long scope, + const void *reserved1, + const void *reserved2, + unsigned long *r_context); +long (* pcsc_release_context) (unsigned long context); +long (* pcsc_list_readers) (unsigned long context, + const char *groups, + char *readers, unsigned long*readerslen); +long (* pcsc_connect) (unsigned long context, + const char *reader, + unsigned long share_mode, + unsigned long preferred_protocols, + unsigned long *r_card, + unsigned long *r_active_protocol); +long (* pcsc_disconnect) (unsigned long card, + unsigned long disposition); +long (* pcsc_status) (unsigned long card, + char *reader, unsigned long *readerlen, + unsigned long *r_state, + unsigned long *r_protocol, + unsigned char *atr, unsigned long *atrlen); +long (* pcsc_begin_transaction) (unsigned long card); +long (* pcsc_end_transaction) (unsigned long card); +long (* pcsc_transmit) (unsigned long card, + const pcsc_io_request_t send_pci, + const unsigned char *send_buffer, + unsigned long send_len, + pcsc_io_request_t recv_pci, + unsigned char *recv_buffer, + unsigned long *recv_len); +long (* pcsc_set_timeout) (unsigned long context, + unsigned long timeout); + + + +static void +bad_request (const char *type) +{ + fprintf (stderr, PGM ": bad `%s' request\n", type); + exit (1); +} + +static void +request_failed (int err) +{ + if (!err) + err = -1; + + putchar (0x81); /* Simple error/success response. */ + + putchar (0); + putchar (0); + putchar (0); + putchar (4); + + putchar ((err >> 24) & 0xff); + putchar ((err >> 16) & 0xff); + putchar ((err >> 8) & 0xff); + putchar ((err ) & 0xff); + + fflush (stdout); +} + + +static void +request_succeeded (const void *buffer, size_t buflen) +{ + size_t len; + + putchar (0x81); /* Simple error/success response. */ + + len = 4 + buflen; + putchar ((len >> 24) & 0xff); + putchar ((len >> 16) & 0xff); + putchar ((len >> 8) & 0xff); + putchar ((len ) & 0xff); + + /* Error code. */ + putchar (0); + putchar (0); + putchar (0); + putchar (0); + + /* Optional reponse string. */ + if (buffer) + fwrite (buffer, buflen, 1, stdout); + + fflush (stdout); +} + + + +static unsigned long +read_32 (FILE *fp) +{ + int c1, c2, c3, c4; + + c1 = getc (stdin); + c2 = getc (stdin); + c3 = getc (stdin); + c4 = getc (stdin); + if (c1 == EOF || c2 == EOF || c3 == EOF || c4 == EOF) + { + fprintf (stderr, PGM ": premature EOF while parsing request\n"); + exit (1); + } + return (c1 << 24) | (c2 << 16) | (c3 << 8) | c4; +} + + + +static const char * +pcsc_error_string (long err) +{ + const char *s; + + if (!err) + return "okay"; + if ((err & 0x80100000) != 0x80100000) + return "invalid PC/SC error code"; + err &= 0xffff; + switch (err) + { + case 0x0002: s = "cancelled"; break; + case 0x000e: s = "can't dispose"; break; + case 0x0008: s = "insufficient buffer"; break; + case 0x0015: s = "invalid ATR"; break; + case 0x0003: s = "invalid handle"; break; + case 0x0004: s = "invalid parameter"; break; + case 0x0005: s = "invalid target"; break; + case 0x0011: s = "invalid value"; break; + case 0x0006: s = "no memory"; break; + case 0x0013: s = "comm error"; break; + case 0x0001: s = "internal error"; break; + case 0x0014: s = "unknown error"; break; + case 0x0007: s = "waited too long"; break; + case 0x0009: s = "unknown reader"; break; + case 0x000a: s = "timeout"; break; + case 0x000b: s = "sharing violation"; break; + case 0x000c: s = "no smartcard"; break; + case 0x000d: s = "unknown card"; break; + case 0x000f: s = "proto mismatch"; break; + case 0x0010: s = "not ready"; break; + case 0x0012: s = "system cancelled"; break; + case 0x0016: s = "not transacted"; break; + case 0x0017: s = "reader unavailable"; break; + case 0x0065: s = "unsupported card"; break; + case 0x0066: s = "unresponsive card"; break; + case 0x0067: s = "unpowered card"; break; + case 0x0068: s = "reset card"; break; + case 0x0069: s = "removed card"; break; + case 0x006a: s = "inserted card"; break; + case 0x001f: s = "unsupported feature"; break; + case 0x0019: s = "PCI too small"; break; + case 0x001a: s = "reader unsupported"; break; + case 0x001b: s = "duplicate reader"; break; + case 0x001c: s = "card unsupported"; break; + case 0x001d: s = "no service"; break; + case 0x001e: s = "service stopped"; break; + default: s = "unknown PC/SC error code"; break; + } + return s; +} + +static void +load_pcsc_driver (const char *libname) +{ + void *handle; + + handle = dlopen (libname, RTLD_LAZY); + if (!handle) + { + fprintf (stderr, PGM ": failed to open driver `%s': %s", + libname, dlerror ()); + exit (1); + } + + pcsc_establish_context = dlsym (handle, "SCardEstablishContext"); + pcsc_release_context = dlsym (handle, "SCardReleaseContext"); + pcsc_list_readers = dlsym (handle, "SCardListReaders"); + pcsc_connect = dlsym (handle, "SCardConnect"); + pcsc_disconnect = dlsym (handle, "SCardDisconnect"); + pcsc_status = dlsym (handle, "SCardStatus"); + pcsc_begin_transaction = dlsym (handle, "SCardBeginTransaction"); + pcsc_end_transaction = dlsym (handle, "SCardEndTransaction"); + pcsc_transmit = dlsym (handle, "SCardTransmit"); + pcsc_set_timeout = dlsym (handle, "SCardSetTimeout"); + + if (!pcsc_establish_context + || !pcsc_release_context + || !pcsc_list_readers + || !pcsc_connect + || !pcsc_disconnect + || !pcsc_status + || !pcsc_begin_transaction + || !pcsc_end_transaction + || !pcsc_transmit + /* || !pcsc_set_timeout */) + { + /* Note that set_timeout is currently not used and also not + available under Windows. */ + fprintf (stderr, + "apdu_open_reader: invalid PC/SC driver " + "(%d%d%d%d%d%d%d%d%d%d)\n", + !!pcsc_establish_context, + !!pcsc_release_context, + !!pcsc_list_readers, + !!pcsc_connect, + !!pcsc_disconnect, + !!pcsc_status, + !!pcsc_begin_transaction, + !!pcsc_end_transaction, + !!pcsc_transmit, + !!pcsc_set_timeout ); + dlclose (handle); + exit (1); + } +} + + + + +/* Handle a open request. The argument is expected to be a string + with the port indentification. ARGBUF is always guaranteed to be + terminted by a 0 which is not counted in ARGLEN. We may modifiy + ARGBUF. */ +static void +handle_open (unsigned char *argbuf, size_t arglen) +{ + long err; + const char * portstr; + char *list = NULL; + unsigned long nreader, listlen, atrlen; + char *p; + unsigned long card_state, card_protocol; + unsigned char atr[33]; + + /* Make sure there is only the port string */ + if (arglen != strlen (argbuf)) + bad_request ("OPEN"); + portstr = argbuf; + + if (driver_is_open) + { + fprintf (stderr, PGM ": PC/SC has already been opened\n"); + request_failed (-1); + } + + err = pcsc_establish_context (PCSC_SCOPE_SYSTEM, NULL, NULL, &pcsc_context); + if (err) + { + fprintf (stderr, PGM": pcsc_establish_context failed: %s (0x%lx)\n", + pcsc_error_string (err), err); + request_failed (err); + return; + } + + err = pcsc_list_readers (pcsc_context, NULL, NULL, &nreader); + if (!err) + { + list = malloc (nreader+1); /* Better add 1 for safety reasons. */ + if (!list) + { + fprintf (stderr, PGM": error allocating memory for reader list\n"); + exit (1); + } + err = pcsc_list_readers (pcsc_context, NULL, list, &nreader); + } + if (err) + { + fprintf (stderr, PGM": pcsc_list_readers failed: %s (0x%lx)\n", + pcsc_error_string (err), err); + pcsc_release_context (pcsc_context); + free (list); + request_failed (err); + return; + } + + listlen = nreader; + p = list; + while (nreader) + { + if (!*p && !p[1]) + break; + fprintf (stderr, PGM": detected reader `%s'\n", p); + if (nreader < (strlen (p)+1)) + { + fprintf (stderr, PGM": invalid response from pcsc_list_readers\n"); + break; + } + nreader -= strlen (p)+1; + p += strlen (p) + 1; + } + + err = pcsc_connect (pcsc_context, + portstr && *portstr? portstr : list, + PCSC_SHARE_EXCLUSIVE, + PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1, + &pcsc_card, + &pcsc_protocol); + if (err) + { + fprintf (stderr, PGM": pcsc_connect failed: %s (0x%lx)\n", + pcsc_error_string (err), err); + pcsc_release_context (pcsc_context); + free (list); + request_failed (err); + return; + } + + atrlen = 32; + /* (We need to pass a dummy buffer. We use LIST because it ought to + be large enough.) */ + err = pcsc_status (pcsc_card, + list, &listlen, + &card_state, &card_protocol, + atr, &atrlen); + free (list); + if (err) + { + fprintf (stderr, PGM": pcsc_status failed: %s (0x%lx)\n", + pcsc_error_string (err), err); + pcsc_release_context (pcsc_context); + request_failed (err); + return; + } + if (atrlen >= sizeof atr || atrlen >= sizeof current_atr) + { + fprintf (stderr, PGM": ATR returned by pcsc_status is too large\n"); + exit (4); + } + memcpy (current_atr, atr, atrlen); + current_atrlen = atrlen; + driver_is_open = 1; + request_succeeded (current_atr, current_atrlen); +} + + + +/* Handle a close request. We expect no arguments. We may modifiy + ARGBUF. */ +static void +handle_close (unsigned char *argbuf, size_t arglen) +{ + if (!driver_is_open) + { + fprintf (stderr, PGM ": PC/SC has not yet been opened\n"); + request_failed (-1); + } + + pcsc_release_context (pcsc_context); + + request_succeeded (NULL, 0); +} + + + +/* Handle a transmit request. The argument is expected to be a bufer + with the APDU. We may modifiy ARGBUF. */ +static void +handle_transmit (unsigned char *argbuf, size_t arglen) +{ + long err; + struct pcsc_io_request_s send_pci; + unsigned long recv_len; + unsigned char buffer[1024]; + + /* The apdu should at least be one byte. */ + if (!arglen) + bad_request ("TRANSMIT"); + + if (!driver_is_open) + { + fprintf (stderr, PGM ": PC/SC has not yet been opened\n"); + request_failed (-1); + } + + if ((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 = sizeof (buffer); + err = pcsc_transmit (pcsc_card, &send_pci, argbuf, arglen, + NULL, buffer, &recv_len); + if (err) + { + if (verbose) + fprintf (stderr, PGM": pcsc_transmit failed: %s (0x%lx)\n", + pcsc_error_string (err), err); + request_failed (err); + return; + } + request_succeeded (buffer, recv_len); +} + + + + + + + + + + + + +static void +print_version (int with_help) +{ + fputs (MYVERSION_LINE "\n" + "Copyright (C) 2004 Free Software Foundation, Inc.\n" + "This program comes with ABSOLUTELY NO WARRANTY.\n" + "This is free software, and you are welcome to redistribute it\n" + "under certain conditions. See the file COPYING for details.\n", + stdout); + + if (with_help) + fputs ("\n" + "Usage: " PGM " [OPTIONS] API-NUMBER [LIBNAME]\n" + "Helper to connect scdaemon to the PC/SC library\n" + "\n" + " --verbose enable extra informational output\n" + " --version print version of the program and exit\n" + " --help display this help and exit\n" + BUGREPORT_LINE, stdout ); + + exit (0); +} + + +int +main (int argc, char **argv) +{ + int last_argc = -1; + int api_number = 0; + int c; + + if (argc) + { + argc--; argv++; + } + while (argc && last_argc != argc ) + { + last_argc = argc; + if (!strcmp (*argv, "--")) + { + argc--; argv++; + break; + } + else if (!strcmp (*argv, "--version")) + print_version (0); + else if (!strcmp (*argv, "--help")) + print_version (1); + else if (!strcmp (*argv, "--verbose")) + { + verbose = 1; + argc--; argv++; + } + } + if (argc != 1 && argc != 2) + { + fprintf (stderr, "usage: " PGM " API-NUMBER [LIBNAME]\n"); + exit (1); + } + + api_number = atoi (*argv); + argv++; argc--; + if (api_number != 1) + { + fprintf (stderr, PGM ": api-number %d is not valid\n", api_number); + exit (1); + } + + load_pcsc_driver (argc? *argv : DEFAULT_PCSC_DRIVER); + + while ((c = getc (stdin)) != EOF) + { + size_t arglen; + unsigned char argbuffer[2048]; + + arglen = read_32 (stdin); + if (arglen >= sizeof argbuffer - 1) + { + fprintf (stderr, PGM ": request too long\n"); + exit (1); + } + if (arglen && fread (argbuffer, arglen, 1, stdin) != 1) + { + fprintf (stderr, PGM ": error reading request: %s\n", + strerror (errno)); + exit (1); + } + argbuffer[arglen] = 0; + switch (c) + { + case 1: + handle_open (argbuffer, arglen); + break; + + case 2: + handle_close (argbuffer, arglen); + exit (0); + break; + + case 3: + handle_transmit (argbuffer, arglen); + break; + + default: + fprintf (stderr, PGM ": invalid request 0x%02X\n", c); + exit (1); + } + free (argbuffer); + } + return 0; +} + + + +/* +Local Variables: +compile-command: "gcc -Wall -g -o pcsc-wrapper pcsc-wrapper.c -ldl" +End: +*/ diff --git a/scd/scdaemon.c b/scd/scdaemon.c index bc9d90b72..aabd38861 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -33,6 +33,9 @@ #include #include #include +#ifdef USE_GNU_PTH +# include +#endif #define JNLIB_NEED_LOG_LOGV #include "scdaemon.h" @@ -131,12 +134,24 @@ static ARGPARSE_OPTS opts[] = { static volatile int caught_fatal_sig = 0; +/* Flag to indicate that a shutdown was requested. */ +static int shutdown_pending; + /* It is possible that we are currently running under setuid permissions */ static int maybe_setuid = 1; /* Name of the communication socket */ static char socket_name[128]; + +#ifdef USE_GNU_PTH +/* Pth wrapper function definitions. */ +GCRY_THREAD_OPTION_PTH_IMPL; + +static void *ticker_thread (void *arg); +#endif /*USE_GNU_PTH*/ + + static const char * my_strusage (int level) { @@ -287,6 +302,7 @@ main (int argc, char **argv ) { ARGPARSE_ARGS pargs; int orig_argc; + gpg_error_t err; int may_coredump; char **orig_argv; FILE *configfp = NULL; @@ -318,7 +334,18 @@ main (int argc, char **argv ) i18n_init (); - /* check that the libraries are suitable. Do it here because + /* Libgcrypt requires us to register the threading model first. + Note that this will also do the pth_init. */ +#ifdef USE_GNU_PTH + err = gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth); + if (err) + { + log_fatal ("can't register GNU Pth with Libgcrypt: %s\n", + gpg_strerror (err)); + } +#endif /*USE_GNU_PTH*/ + + /* Check that the libraries are suitable. Do it here because the option parsing may need services of the library */ if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) ) { @@ -568,7 +595,21 @@ main (int argc, char **argv ) if (pipe_server) - { /* this is the simple pipe based server */ + { /* This is the simple pipe based server */ +#ifdef USE_GNU_PTH + pth_attr_t tattr; + + tattr = pth_attr_new(); + pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0); + pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 64*1024); + pth_attr_set (tattr, PTH_ATTR_NAME, "ticker"); + + if (!pth_spawn (tattr, ticker_thread, NULL)) + { + log_error ("error spawning ticker thread: %s\n", strerror (errno)); + scd_exit (2); + } +#endif /*USE_GNU_PTH*/ scd_command_handler (-1); } else if (!is_daemon) @@ -780,6 +821,115 @@ scd_exit (int rc) void scd_init_default_ctrl (CTRL ctrl) { + ctrl->reader_slot = -1; +} + + +#ifdef USE_GNU_PTH + +static void +handle_signal (int signo) +{ + switch (signo) + { + case SIGHUP: + log_info ("SIGHUP received - " + "re-reading configuration and resetting cards\n"); +/* reread_configuration (); */ + break; + + case SIGUSR1: + if (opt.verbose < 5) + opt.verbose++; + log_info ("SIGUSR1 received - verbosity set to %d\n", opt.verbose); + break; + + case SIGUSR2: + if (opt.verbose) + opt.verbose--; + log_info ("SIGUSR2 received - verbosity set to %d\n", opt.verbose ); + break; + + case SIGTERM: + if (!shutdown_pending) + log_info ("SIGTERM received - shutting down ...\n"); + else + log_info ("SIGTERM received - still %ld running threads\n", + pth_ctrl( PTH_CTRL_GETTHREADS )); + shutdown_pending++; + if (shutdown_pending > 2) + { + log_info ("shutdown forced\n"); + log_info ("%s %s stopped\n", strusage(11), strusage(13) ); + cleanup (); + scd_exit (0); + } + break; + + case SIGINT: + log_info ("SIGINT received - immediate shutdown\n"); + log_info( "%s %s stopped\n", strusage(11), strusage(13)); + cleanup (); + scd_exit (0); + break; + default: + log_info ("signal %d received - no action defined\n", signo); + } } +static void +handle_tick (void) +{ + scd_update_reader_status_file (); +} + +static void * +ticker_thread (void *dummy_arg) +{ + pth_event_t sigs_ev, time_ev = NULL; + sigset_t sigs; + int signo; + + sigemptyset (&sigs ); + sigaddset (&sigs, SIGHUP); + sigaddset (&sigs, SIGUSR1); + sigaddset (&sigs, SIGUSR2); + sigaddset (&sigs, SIGINT); + sigaddset (&sigs, SIGTERM); + sigs_ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo); + + for (;;) + { + if (!time_ev) + { + time_ev = pth_event (PTH_EVENT_TIME, pth_timeout (2, 0)); + if (time_ev) + pth_event_concat (sigs_ev, time_ev, NULL); + } + + if (pth_wait (sigs_ev) < 1) + continue; + + if ( +#ifdef PTH_STATUS_OCCURRED /* This is Pth 2 */ + pth_event_status (sigs_ev) == PTH_STATUS_OCCURRED +#else + pth_event_occurred (sigs_ev) +#endif + ) + handle_signal (signo); + + /* Always run the ticker. */ + if (!shutdown_pending) + { + pth_event_isolate (sigs_ev); + pth_event_free (time_ev, PTH_FREE_ALL); + time_ev = NULL; + handle_tick (); + } + } + + pth_event_free (sigs_ev, PTH_FREE_ALL); +} +#endif /*USE_GNU_PTH*/ diff --git a/scd/scdaemon.h b/scd/scdaemon.h index 098738508..1dd32ae90 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -79,6 +79,7 @@ struct app_ctx_s; struct server_control_s { struct server_local_s *server_local; + int reader_slot; /* Slot of the open reader or -1 if not open. */ struct card_ctx_s *card_ctx; struct app_ctx_s *app_ctx; struct { @@ -101,6 +102,7 @@ void scd_init_default_ctrl (CTRL ctrl); /*-- command.c --*/ void scd_command_handler (int); void send_status_info (CTRL ctrl, const char *keyword, ...); +void scd_update_reader_status_file (void); /*-- card.c --*/ int card_open (CARD *rcard); -- cgit From 4624e9dfb3fd8260273706d43c6137f860abb89c Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 21 Apr 2004 14:44:09 +0000 Subject: * command.c (scd_update_reader_status_file): Send a signal back to the client. (option_handler): Parse the new event-signal option. * scdaemon.c (handle_signal): Do not use SIGUSR{1,2} anymore for changing the verbosity. --- scd/ChangeLog | 9 +++++++++ scd/command.c | 46 ++++++++++++++++++++++++++++++++++++++++++++-- scd/scdaemon.c | 8 ++------ 3 files changed, 55 insertions(+), 8 deletions(-) (limited to 'scd/scdaemon.c') diff --git a/scd/ChangeLog b/scd/ChangeLog index bf5c36cbc..037090863 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,12 @@ +2004-04-21 Werner Koch + + * command.c (scd_update_reader_status_file): Send a signal back to + the client. + (option_handler): Parse the new event-signal option. + + * scdaemon.c (handle_signal): Do not use SIGUSR{1,2} anymore for + changing the verbosity. + 2004-04-20 Werner Koch * command.c (scd_update_reader_status_file): Write status files. diff --git a/scd/command.c b/scd/command.c index 9e77cbe0f..6fa100ff9 100644 --- a/scd/command.c +++ b/scd/command.c @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -33,14 +34,21 @@ #include "app-common.h" #include "apdu.h" /* Required for apdu_*_reader (). */ -/* maximum length aloowed as a PIN; used for INQUIRE NEEDPIN */ +/* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN */ #define MAXLEN_PIN 100 + +/* We keep track of the primary client using scdaemon. This one will + for example receive signal on card change. */ +static ctrl_t primary_connection; + + #define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t)) /* Data used to associate an Assuan context with local server data */ struct server_local_s { ASSUAN_CONTEXT assuan_ctx; + int event_signal; /* Or 0 if not used. */ }; @@ -96,7 +104,18 @@ reset_notify (ASSUAN_CONTEXT ctx) static int option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value) { - return 0; + ctrl_t ctrl = assuan_get_pointer (ctx); + + if (!strcmp (key, "event-signal")) + { + /* A value of 0 is allowed to reset the event signal. */ + int i = *value? atoi (value) : -1; + if (i < 0) + return ASSUAN_Parameter_Error; + ctrl->server_local->event_signal = i; + } + + return 0; } @@ -1096,6 +1115,10 @@ scd_command_handler (int listen_fd) if (DBG_ASSUAN) assuan_set_log_stream (ctx, log_get_stream ()); + /* Store the primary connection's assuan context. */ + if (!primary_connection) + primary_connection = &ctrl; + /* We open the reader right at startup so that the ticker is able to update the status file. */ if (ctrl.reader_slot == -1) @@ -1122,6 +1145,12 @@ scd_command_handler (int listen_fd) continue; } } + + /* The next client will be the primary conenction if this one + terminates. */ + if (primary_connection == &ctrl) + primary_connection = NULL; + do_reset (&ctrl, 1); /* Cleanup. */ assuan_deinit_server (ctx); @@ -1221,6 +1250,19 @@ scd_update_reader_status_file (void) fclose (fp); } xfree (fname); + + /* Send a signal to the primary client, if any. */ + if (primary_connection && primary_connection->server_local + && primary_connection->server_local->assuan_ctx) + { + pid_t pid = assuan_get_pid (primary_connection + ->server_local->assuan_ctx); + int signo = primary_connection->server_local->event_signal; + + log_info ("client pid is %d, sending signal %d\n", pid, signo); + if (pid != (pid_t)(-1) && pid && signo > 0) + kill (pid, signo); + } } } } diff --git a/scd/scdaemon.c b/scd/scdaemon.c index aabd38861..5066a373b 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -839,15 +839,11 @@ handle_signal (int signo) break; case SIGUSR1: - if (opt.verbose < 5) - opt.verbose++; - log_info ("SIGUSR1 received - verbosity set to %d\n", opt.verbose); + log_info ("SIGUSR1 received - no action defined\n"); break; case SIGUSR2: - if (opt.verbose) - opt.verbose--; - log_info ("SIGUSR2 received - verbosity set to %d\n", opt.verbose ); + log_info ("SIGUSR2 received - no action defined\n"); break; case SIGTERM: -- cgit From 1101deced5790f04bc7c5913616dfd4ba557055b Mon Sep 17 00:00:00 2001 From: Marcus Brinkmann Date: Mon, 26 Apr 2004 13:07:08 +0000 Subject: 2004-04-26 Marcus Brinkmann * scdaemon.c (main): For now, always print default filename for --gpgconf-list, and never /dev/null. --- scd/ChangeLog | 5 +++++ scd/scdaemon.c | 10 +++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) (limited to 'scd/scdaemon.c') diff --git a/scd/ChangeLog b/scd/ChangeLog index 037090863..eb2074bba 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,8 @@ +2004-04-26 Marcus Brinkmann + + * scdaemon.c (main): For now, always print default filename for + --gpgconf-list, and never /dev/null. + 2004-04-21 Werner Koch * command.c (scd_update_reader_status_file): Send a signal back to diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 5066a373b..d0306aeb6 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -538,7 +538,10 @@ main (int argc, char **argv ) } if (gpgconf_list) - { /* List options and default values in the GPG Conf format. */ + { + char *filename; + + /* List options and default values in the GPG Conf format. */ /* The following list is taken from gnupg/tools/gpgconf-comp.c. */ /* Option flags. YOU MUST NOT CHANGE THE NUMBERS OF THE EXISTING @@ -557,9 +560,10 @@ main (int argc, char **argv ) a default, which is described by the value of the ARGDEF field. */ #define GC_OPT_FLAG_NO_ARG_DESC (1UL << 6) + filename = make_filename (opt.homedir, "scdaemon.conf", NULL); printf ("gpgconf-scdaemon.conf:%lu:\"%s\n", - GC_OPT_FLAG_DEFAULT, - config_filename?config_filename:"/dev/null"); + GC_OPT_FLAG_DEFAULT, filename); + xfree (filename); printf ("verbose:%lu:\n" "quiet:%lu:\n" -- cgit From 5fa53f752458107ecfc49c4a58fb87123193fdfa Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 26 Apr 2004 13:46:36 +0000 Subject: * scdaemon.c (main): Do the last change the usual way. This is so that we can easily test for versioned config files above. * gpgsm.c (main) : Do not use /dev/null as default config filename. --- scd/ChangeLog | 5 +++++ scd/scdaemon.c | 8 +++----- sm/ChangeLog | 3 +++ sm/gpgsm.c | 6 ++++-- 4 files changed, 15 insertions(+), 7 deletions(-) (limited to 'scd/scdaemon.c') diff --git a/scd/ChangeLog b/scd/ChangeLog index eb2074bba..3aea9f08d 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,8 @@ +2004-04-26 Werner Koch + + * scdaemon.c (main): Do the last change the usual way. This is so + that we can easily test for versioned config files above. + 2004-04-26 Marcus Brinkmann * scdaemon.c (main): For now, always print default filename for diff --git a/scd/scdaemon.c b/scd/scdaemon.c index d0306aeb6..5e9737ae4 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -539,8 +539,6 @@ main (int argc, char **argv ) if (gpgconf_list) { - char *filename; - /* List options and default values in the GPG Conf format. */ /* The following list is taken from gnupg/tools/gpgconf-comp.c. */ @@ -559,11 +557,11 @@ main (int argc, char **argv ) /* The NO_ARG_DESC flag for an option indicates that the argument has a default, which is described by the value of the ARGDEF field. */ #define GC_OPT_FLAG_NO_ARG_DESC (1UL << 6) + if (!config_filename) + config_filename = make_filename (opt.homedir, "scdaemon.conf", NULL ); - filename = make_filename (opt.homedir, "scdaemon.conf", NULL); printf ("gpgconf-scdaemon.conf:%lu:\"%s\n", - GC_OPT_FLAG_DEFAULT, filename); - xfree (filename); + GC_OPT_FLAG_DEFAULT, config_filename); printf ("verbose:%lu:\n" "quiet:%lu:\n" diff --git a/sm/ChangeLog b/sm/ChangeLog index 6c3ff32fb..aaf5d9047 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,5 +1,8 @@ 2004-04-26 Werner Koch + * gpgsm.c (main) : Do not use /dev/null as default config + filename. + * call-agent.c (gpgsm_agent_pksign, gpgsm_agent_pkdecrypt) (gpgsm_agent_genkey, gpgsm_agent_istrusted) (gpgsm_agent_marktrusted, gpgsm_agent_havekey) diff --git a/sm/gpgsm.c b/sm/gpgsm.c index eff167367..33d8a2c48 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -1294,9 +1294,11 @@ main ( int argc, char **argv) a default, which is described by the value of the ARGDEF field. */ #define GC_OPT_FLAG_NO_ARG_DESC (1UL << 6) + if (!config_filename) + config_filename = make_filename (opt.homedir, "gpgsm.conf", NULL); + printf ("gpgconf-gpgsm.conf:%lu:\"%s\n", - GC_OPT_FLAG_DEFAULT, - config_filename?config_filename:"/dev/null"); + GC_OPT_FLAG_DEFAULT, config_filename); printf ("verbose:%lu:\n" "quiet:%lu:\n" -- cgit From d9147773b4e5eda669ebd79454052d0a191cde55 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 22 Jul 2004 09:37:36 +0000 Subject: * trustlist.c (read_list): Allow colons in the fingerprint. (headerblurb): Rephrased. * gpg-agent.c (handle_connections): Increase the stack size ot 256k. * de.po: Updated. * scdaemon.c (main): Bumbed thread stack size up to 512k. * keylist.c (list_cert_raw): Print the keygrip. --- ChangeLog | 9 ++ NEWS | 11 +- README | 34 ++++-- TODO | 11 +- agent/ChangeLog | 7 ++ agent/gpg-agent.c | 2 +- agent/trustlist.c | 28 +++-- configure.ac | 3 +- po/ChangeLog | 4 + po/de.po | 350 +++++++++++++++++++++++++++++------------------------- scd/ChangeLog | 4 + scd/scdaemon.c | 2 +- sm/ChangeLog | 4 + sm/keylist.c | 6 +- 14 files changed, 275 insertions(+), 200 deletions(-) (limited to 'scd/scdaemon.c') diff --git a/ChangeLog b/ChangeLog index dc43552dd..42fe782af 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2004-07-22 Werner Koch + + Released 1.9.10. + +2004-07-21 Werner Koch + + * configure.ac: Don't set DIE to no after it might has been set to + yes. + 2004-07-20 Werner Koch * Makefile.am (sm): Build kbx only if gpgsm is to be build. diff --git a/NEWS b/NEWS index 3d9b4ca34..74250b04a 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,12 @@ -Noteworthy changes in version 1.9.10 ------------------------------------------------- +Noteworthy changes in version 1.9.10 (2004-07-22) +------------------------------------------------- + + * Fixed a serious bug in the checking of trusted root certificates. + + * New configure option --enable-agent-pnly allows to build and + install just the agent. + + * Fixed a problem with the log file handling. Noteworthy changes in version 1.9.9 (2004-06-08) diff --git a/README b/README index 5b4a69027..8027b6960 100644 --- a/README +++ b/README @@ -9,6 +9,10 @@ GnuPG 2.0 release. Note that GnuPG 1.3 and 1.9 are not always in sync and thus features and bug fixes done in 1.3 are not necessary available in 1.9. +You should use this GnuPG version if you want to use the gpg-agent or +gpgsm (the S/MIME variant of gpg). Note that the gpg-agent is also +helpful when using the standard gpg versions (1.2.x or 1.3.x). + BUILD INSTRUCTIONS ================== @@ -20,7 +24,10 @@ GnuPG 1.9 depends on the following packages: libassuan (ftp://ftp.gnupg.org/gcrypt/alpha/libassuan/) libksba (ftp://ftp.gnupg.org/gcrypt/alpha/libksba/) -You also need the pinentry package for most function of GnupG; however +If you use the configure option --enable-agent-only, libksba is not +required. + +You also need the pinentry package for most function of GnuPG; however it is not a build requirement. pinentry is available at ftp://ftp.gnupg.org/gcrypt/pinentry/ . @@ -28,7 +35,7 @@ You should get the latest versions of course, the GnuPG configure script complains if a version is not sufficient. After building and installing the above packages in the order as given -above, you may now continue with GnupG installation (you may also just +above, you may now continue with GnuPG installation (you may also just try to build GnuPG to see whether your already installed versions are sufficient). @@ -42,9 +49,9 @@ As with all packages, you just have to do If everything succeeds, you have a working GnuPG with support for S/MIME and smartcards. Note that there is no binary gpg but a gpg2 so -that this package won't confict with a GnuPG 1.2 or1.3 -installation. gpg2 behaves just like gpg and it is possible to symlink -oto gpg if you want to use gpg 1.9. +that this package won't confict with a GnuPG 1.2 or 1.3 +installation. gpg2 behaves just like gpg; it is however suggested to +keep using gpg 1.2.x or 1.3.x. In case of problem please ask on gpa-dev@gnupg.org for advise. Note that this release is only expected to build on GNU and *BSD systems. @@ -71,7 +78,7 @@ gpgsm: prepended before each block. -gpg2: +gpg2: (Note that these card commands are also available with gpg 1.3.x) ----- --card-status @@ -268,9 +275,10 @@ policies.txt trustlist.txt - A list of trusted certificates usually maintained by - gpg-agent. It can however be edited manually. The file will - be created automagically with some explaining comments. + A list of trusted certificates. The file will be created + automagically with some explaining comments. By using + gpg-agent's option --allow-mark-trusted, gpg-agent may add new + entries after user confirmation. random_seed @@ -436,7 +444,7 @@ modes for gpgsm, here is the entire list of ways to specify a key: *Heine -Please note that we have reused the hash mark indentifier which was +Please note that we have reused the hash mark identifier which was used in old GnuPG versions to indicate the so called local-id. It is not anymore used and there should be no conflict when used with X.509 stuff. @@ -456,7 +464,7 @@ file. gpgsm --import foo.p12 -This require that the gpg-agent is running. +This requires that the gpg-agent is running. HOW TO EXPORT A PRIVATE KEY @@ -482,8 +490,8 @@ however you must have build GnuPG with support for the OpenSC library. The build process automagically detects the presence of this library and will include support for these cards. -The other card we currently support is the Telesec NetKey card with -the NKS 2.0 card application. +The other cards we currently support are the Telesec NetKey card with +the NKS 2.0 card application and all generic DINSIG cards. Before GPGSM can make use of a new card it must gather some information, like the card's serial number, the public keys and the diff --git a/TODO b/TODO index cb58f907b..ce835d8ec 100644 --- a/TODO +++ b/TODO @@ -1,12 +1,5 @@ -*- outline -*- -* Keylisting -Add the keygrip to the dump output -Explain how to setup a root CA key as trusted -Look for new system-wide certificates -Explain how trustlist.txt might be managed. - - * src/base64 ** Make parsing more robust @@ -83,4 +76,6 @@ might want to have an agent context for each service request ** Add a test to check the extkeyusage. - +* doc/ +** Explain how to setup a root CA key as trusted +** Explain how trustlist.txt might be managed. diff --git a/agent/ChangeLog b/agent/ChangeLog index 929959d85..d616b1544 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,10 @@ +2004-07-22 Werner Koch + + * trustlist.c (read_list): Allow colons in the fingerprint. + (headerblurb): Rephrased. + + * gpg-agent.c (handle_connections): Increase the stack size ot 256k. + 2004-06-20 Moritz Schulte * gpg-agent.c: Include (build fix for BSD). diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 69a28e78b..f7e701bbc 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -1158,7 +1158,7 @@ handle_connections (int listen_fd) tattr = pth_attr_new(); pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0); - pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 32*1024); + pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 256*1024); pth_attr_set (tattr, PTH_ATTR_NAME, "gpg-agent"); sigemptyset (&sigs ); diff --git a/agent/trustlist.c b/agent/trustlist.c index 16b7dc34e..a0b6b9861 100644 --- a/agent/trustlist.c +++ b/agent/trustlist.c @@ -32,15 +32,16 @@ #include /* fixme: need a way to avoid assuan calls here */ static const char headerblurb[] = -"# This is the list of trusted keys. Comments like this one and empty\n" -"# lines are allowed but keep in mind that the entire file is integrity\n" +"# This is the list of trusted keys. Comment lines, like this one, as\n" +"# well as empty lines are ignored. The entire file may be integrity\n" "# protected by the use of a MAC, so changing the file does not make\n" -"# much sense without the knowledge of the MAC key. Lines do have a\n" -"# length limit but this is not serious limitation as the format of the\n" +"# sense without the knowledge of the MAC key. Lines do have a length\n" +"# limit but this is not serious limitation as the format of the\n" "# entries is fixed and checked by gpg-agent: A non-comment line starts\n" -"# with optional white spaces, followed by exactly 40 hex character,\n" -"# optioanlly followed by a flag character which my either be 'P', 'S'\n" -"# or '*'. Additional data delimited with by a white space is ignored.\n" +"# with optional white spaces, followed by the SHA-1 fingerpint in hex,\n" +"# optionally followed by a flag character which my either be 'P', 'S'\n" +"# or '*'. Additional data, delimited by white space, is ignored.\n" +"#\n" "# NOTE: You should give the gpg-agent a HUP after editing this file.\n" "\n"; @@ -97,7 +98,7 @@ static int read_list (char *key, int *keyflag) { int rc; - int c, i; + int c, i, j; char *p, line[256]; if (!trustfp) @@ -125,16 +126,17 @@ read_list (char *key, int *keyflag) : GPG_ERR_INCOMPLETE_LINE); } - /* Allow for emty lines and spaces */ + /* Allow for empty lines and spaces */ for (p=line; spacep (p); p++) ; } while (!*p || *p == '\n' || *p == '#'); - for (i=0; hexdigitp (p+i) && i < 40; i++) - key[i] = p[i] >= 'a'? (p[i] & 0xdf): p[i]; - key[i] = 0; - if (i!=40 || !(spacep (p+i) || p[i] == '\n')) + for (i=j=0; (p[i] == ':' || hexdigitp (p+i)) && j < 40; i++) + if ( p[i] != ':' ) + key[j++] = p[i] >= 'a'? (p[i] & 0xdf): p[i]; + key[j] = 0; + if (j!=40 || !(spacep (p+i) || p[i] == '\n')) { log_error ("invalid formatted fingerprint in trustlist\n"); return gpg_error (GPG_ERR_BAD_DATA); diff --git a/configure.ac b/configure.ac index e0f9b720e..87c015033 100644 --- a/configure.ac +++ b/configure.ac @@ -24,7 +24,7 @@ min_automake_version="1.7.6" # Version number: Remember to change it immediately *after* a release. # Add a "-cvs" prefix for non-released code. -AC_INIT(gnupg, 1.9.10-cvs, gnupg-devel@gnupg.org) +AC_INIT(gnupg, 1.9.10, gnupg-devel@gnupg.org) # Set development_version to yes if the minor number is odd or you # feel that the default check for a development version is not # sufficient. @@ -1067,7 +1067,6 @@ if test "$have_libassuan" = "no"; then ***]]) fi if test "$have_ksba" = "no"; then - die=no AC_MSG_NOTICE([[ *** *** You need libksba to build this program. diff --git a/po/ChangeLog b/po/ChangeLog index 97e68c730..c1bcd81e1 100644 --- a/po/ChangeLog +++ b/po/ChangeLog @@ -1,3 +1,7 @@ +2004-07-22 Werner Koch + + * de.po: Updated. + 2004-04-06 Werner Koch * Makevars (DOMAIN): Init from PACKAGE_GT diff --git a/po/de.po b/po/de.po index 2cf41ddb0..4dbe66e8a 100644 --- a/po/de.po +++ b/po/de.po @@ -8,17 +8,17 @@ # msgid "" msgstr "" -"Project-Id-Version: gnupg2 1.9.7\n" +"Project-Id-Version: gnupg2 1.9.10\n" "Report-Msgid-Bugs-To: translations@gnupg.org\n" -"POT-Creation-Date: 2004-04-29 19:50+0200\n" -"PO-Revision-Date: 2004-04-06 11:47+0200\n" +"POT-Creation-Date: 2004-07-22 09:54+0200\n" +"PO-Revision-Date: 2004-07-22 10:05+0200\n" "Last-Translator: Werner Koch \n" "Language-Team: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=iso-8859-1\n" "Content-Transfer-Encoding: 8bit\n" -#: agent/gpg-agent.c:94 agent/protect-tool.c:92 scd/scdaemon.c:92 +#: agent/gpg-agent.c:96 agent/protect-tool.c:94 scd/scdaemon.c:92 msgid "" "@Options:\n" " " @@ -26,51 +26,59 @@ msgstr "" "@Optionen:\n" " " -#: agent/gpg-agent.c:96 scd/scdaemon.c:94 +#: agent/gpg-agent.c:98 scd/scdaemon.c:94 msgid "run in server mode (foreground)" msgstr "Im Server Modus ausführen" -#: agent/gpg-agent.c:97 scd/scdaemon.c:95 +#: agent/gpg-agent.c:99 scd/scdaemon.c:95 msgid "run in daemon mode (background)" msgstr "Im Daemon Modus ausführen" -#: agent/gpg-agent.c:98 kbx/kbxutil.c:73 scd/scdaemon.c:96 sm/gpgsm.c:312 +#: agent/gpg-agent.c:100 kbx/kbxutil.c:75 scd/scdaemon.c:96 sm/gpgsm.c:313 #: tools/gpgconf.c:62 msgid "verbose" msgstr "ausführlich" -#: agent/gpg-agent.c:99 kbx/kbxutil.c:74 scd/scdaemon.c:97 sm/gpgsm.c:313 +#: agent/gpg-agent.c:101 kbx/kbxutil.c:76 scd/scdaemon.c:97 sm/gpgsm.c:314 msgid "be somewhat more quiet" msgstr "etwas weniger Aussageb erzeugen" -#: agent/gpg-agent.c:100 scd/scdaemon.c:98 +#: agent/gpg-agent.c:102 scd/scdaemon.c:98 msgid "sh-style command output" msgstr "Ausgabe für /bin/sh" -#: agent/gpg-agent.c:101 scd/scdaemon.c:99 +#: agent/gpg-agent.c:103 scd/scdaemon.c:99 msgid "csh-style command output" msgstr "Ausgabe für /bin/csh" -#: agent/gpg-agent.c:102 +#: agent/gpg-agent.c:104 msgid "|FILE|read options from FILE" msgstr "|DATEI|Konfigurationsoptionen aus DATEI lesen" -#: agent/gpg-agent.c:107 scd/scdaemon.c:106 +#: agent/gpg-agent.c:109 scd/scdaemon.c:106 msgid "do not detach from the console" msgstr "Im Vordergrund laufen lassen" -#: agent/gpg-agent.c:108 +#: agent/gpg-agent.c:110 msgid "do not grab keyboard and mouse" msgstr "Tastatur und Maus nicht \"grabben\"" -#: agent/gpg-agent.c:109 scd/scdaemon.c:107 sm/gpgsm.c:315 +#: agent/gpg-agent.c:111 scd/scdaemon.c:107 sm/gpgsm.c:316 msgid "use a log file for the server" msgstr "Logausgaben in eine Datei umlenken" -#: agent/gpg-agent.c:110 +#: agent/gpg-agent.c:112 msgid "do not allow multiple connections" msgstr "Nicht mehr als eine Verbindung erlauben" +#: agent/gpg-agent.c:115 +msgid "|PGM|use PGM as the PIN-Entry program" +msgstr "|PGM|benutze PGM as PIN-Entry" + +#: agent/gpg-agent.c:117 +msgid "|PGM|use PGM as the SCdaemon program" +msgstr "|PGM|benutze PGM as SCdaemon" + #: agent/gpg-agent.c:124 msgid "ignore requests to change the TTY" msgstr "Ignoriere Anfragen, das TTY zu wechseln" @@ -79,21 +87,33 @@ msgstr "Ignoriere Anfragen, das TTY zu wechseln" msgid "ignore requests to change the X display" msgstr "Ignoriere Anfragen, das X-Display zu wechseln" -#: agent/gpg-agent.c:180 agent/protect-tool.c:124 scd/scdaemon.c:165 -#: sm/gpgsm.c:479 tools/gpgconf.c:85 +#: agent/gpg-agent.c:129 +msgid "|N|expire cached PINs after N seconds" +msgstr "|N|lasse PINs im Cache nach N Sekunden verfallen" + +#: agent/gpg-agent.c:131 +msgid "do not use the PIN cache when signing" +msgstr "benutze PINs im Cache nicht bem Signieren" + +#: agent/gpg-agent.c:133 +msgid "allow clients to mark keys as \"trusted\"" +msgstr "erlaube Aufrufern Schlüssel als \"vertrauenswürdig\" zu markieren" + +#: agent/gpg-agent.c:191 agent/protect-tool.c:127 scd/scdaemon.c:165 +#: sm/gpgsm.c:481 tools/gpgconf.c:85 msgid "Please report bugs to <" msgstr "Fehlerberichte bitte an <" -#: agent/gpg-agent.c:180 agent/protect-tool.c:124 scd/scdaemon.c:165 -#: sm/gpgsm.c:479 tools/gpgconf.c:85 +#: agent/gpg-agent.c:191 agent/protect-tool.c:127 scd/scdaemon.c:165 +#: sm/gpgsm.c:481 tools/gpgconf.c:85 msgid ">.\n" msgstr ">.\n" -#: agent/gpg-agent.c:183 +#: agent/gpg-agent.c:194 msgid "Usage: gpg-agent [options] (-h for help)" msgstr "Gebrauch: gpg-agent [Optionen] (-h für Hilfe)" -#: agent/gpg-agent.c:185 +#: agent/gpg-agent.c:196 msgid "" "Syntax: gpg-agent [options] [command [args]]\n" "Secret key management for GnuPG\n" @@ -101,55 +121,55 @@ msgstr "" "Syntax: gpg-agent [Optionen] [Kommando [Argumente]]\n" "Verwaltung von geheimen Schlüssel für GnuPG\n" -#: agent/gpg-agent.c:256 scd/scdaemon.c:239 sm/gpgsm.c:602 +#: agent/gpg-agent.c:267 scd/scdaemon.c:239 sm/gpgsm.c:604 #, c-format msgid "invalid debug-level `%s' given\n" msgstr "ungültige Debugebene `%s' angegeben\n" -#: agent/gpg-agent.c:414 agent/protect-tool.c:1035 kbx/kbxutil.c:230 -#: scd/scdaemon.c:352 sm/gpgsm.c:725 +#: agent/gpg-agent.c:439 agent/protect-tool.c:1040 kbx/kbxutil.c:232 +#: scd/scdaemon.c:352 sm/gpgsm.c:726 #, c-format msgid "libgcrypt is too old (need %s, have %s)\n" msgstr "" "Die Bibliothek \"libgcrypt\" is zu alt (benötigt wird %s, vorhanden ist %s)\n" -#: agent/gpg-agent.c:487 scd/scdaemon.c:432 sm/gpgsm.c:823 +#: agent/gpg-agent.c:512 scd/scdaemon.c:432 sm/gpgsm.c:824 #, c-format msgid "NOTE: no default option file `%s'\n" msgstr "Notiz: Voreingestellte Konfigurationsdatei `%s' fehlt\n" -#: agent/gpg-agent.c:492 agent/gpg-agent.c:955 scd/scdaemon.c:437 -#: sm/gpgsm.c:827 +#: agent/gpg-agent.c:517 agent/gpg-agent.c:983 scd/scdaemon.c:437 +#: sm/gpgsm.c:828 #, c-format msgid "option file `%s': %s\n" msgstr "Konfigurationsdatei `%s': %s\n" -#: agent/gpg-agent.c:500 scd/scdaemon.c:445 sm/gpgsm.c:834 +#: agent/gpg-agent.c:525 scd/scdaemon.c:445 sm/gpgsm.c:835 #, c-format msgid "reading options from `%s'\n" msgstr "Optionen werden aus `%s' gelesen\n" -#: agent/gpg-agent.c:641 scd/scdaemon.c:619 +#: agent/gpg-agent.c:668 scd/scdaemon.c:619 msgid "please use the option `--daemon' to run the program in the background\n" msgstr "" "Bitte die Option `--daemon' nutzen um das Programm im Hintergund " "auszuführen\n" -#: agent/gpg-agent.c:988 agent/gpg-agent.c:1022 +#: agent/gpg-agent.c:1016 agent/gpg-agent.c:1050 #, c-format msgid "can't create directory `%s': %s\n" msgstr "Das Verzeichniss `%s' kann nicht erstell werden: %s\n" -#: agent/gpg-agent.c:991 agent/gpg-agent.c:1027 +#: agent/gpg-agent.c:1019 agent/gpg-agent.c:1055 #, c-format msgid "directory `%s' created\n" msgstr "Verzeichniss `%s' wurde erstellt\n" -#: agent/protect-tool.c:127 +#: agent/protect-tool.c:130 msgid "Usage: gpg-protect-tool [options] (-h for help)\n" msgstr "Gebrauch: gpg-protect-tool [Optionen] (-h für Hilfe)\n" -#: agent/protect-tool.c:129 +#: agent/protect-tool.c:132 msgid "" "Syntax: gpg-protect-tool [options] [args]]\n" "Secret key maintenance tool\n" @@ -157,7 +177,7 @@ msgstr "" "Syntax: gpg-protect-tool [Optionen] [Argumente]\n" "Werkzeug zum Bearbeiten von geheimen Schlüsseln\n" -#: agent/protect-tool.c:1142 +#: agent/protect-tool.c:1148 msgid "" "Please enter the passphrase or the PIN\n" "needed to complete this operation." @@ -165,7 +185,7 @@ msgstr "" "Die Eingabe des Mantras (Passphrase) bzw. der PIN\n" "wird benötigt um diese Aktion auszuführen." -#: agent/protect-tool.c:1145 +#: agent/protect-tool.c:1151 msgid "Passphrase:" msgstr "Passphrase:" @@ -236,19 +256,19 @@ msgstr "Falsches Mantra (Passphrase)" msgid "Passphrase" msgstr "Mantra" -#: common/sysutils.c:84 +#: common/sysutils.c:87 #, c-format msgid "can't disable core dumps: %s\n" msgstr "" "Das Erstellen eines Speicherabzugs (core-dump) kann nicht verhindert werden: " "%s\n" -#: common/sysutils.c:159 +#: common/sysutils.c:182 #, c-format msgid "Warning: unsafe ownership on %s \"%s\"\n" msgstr "WARNUNG: Unsichere Besitzrechte für %s \"%s\"\n" -#: common/sysutils.c:191 +#: common/sysutils.c:214 #, c-format msgid "Warning: unsafe permissions on %s \"%s\"\n" msgstr "WARNUNG: Unsichere Zugriffsrechte für %s \"%s\"\n" @@ -287,12 +307,12 @@ msgstr "Vom Benutzer abgebrochen\n" msgid "problem with the agent\n" msgstr "Problem mit dem Agenten\n" -#: jnlib/logging.c:547 +#: jnlib/logging.c:555 #, c-format msgid "you found a bug ... (%s:%d)\n" msgstr "Sie haben einen Bug (Softwarefehler) gefunden ... (%s:%d)\n" -#: kbx/kbxutil.c:62 sm/gpgsm.c:223 tools/gpgconf.c:53 +#: kbx/kbxutil.c:63 sm/gpgsm.c:224 tools/gpgconf.c:53 msgid "" "@Commands:\n" " " @@ -300,7 +320,7 @@ msgstr "" "@Kommandos:\n" " " -#: kbx/kbxutil.c:68 sm/gpgsm.c:258 tools/gpgconf.c:59 +#: kbx/kbxutil.c:70 sm/gpgsm.c:259 tools/gpgconf.c:59 msgid "" "@\n" "Options:\n" @@ -310,39 +330,31 @@ msgstr "" "Optionen:\n" " " -#: kbx/kbxutil.c:70 sm/gpgsm.c:260 -msgid "create ascii armored output" -msgstr "Ausgabe mit ASCII Hülle wird erzeugt" - -#: kbx/kbxutil.c:72 sm/gpgsm.c:311 tools/gpgconf.c:61 -msgid "use as output file" -msgstr "als Ausgabedatei benutzen" - -#: kbx/kbxutil.c:75 sm/gpgsm.c:320 tools/gpgconf.c:64 +#: kbx/kbxutil.c:77 sm/gpgsm.c:321 tools/gpgconf.c:64 msgid "do not make any changes" msgstr "Keine Änderungen durchführen" -#: kbx/kbxutil.c:77 +#: kbx/kbxutil.c:79 msgid "set debugging flags" msgstr "Debug Flags setzen" -#: kbx/kbxutil.c:78 +#: kbx/kbxutil.c:80 msgid "enable full debugging" msgstr "Alle Debug Flags setzen" -#: kbx/kbxutil.c:99 +#: kbx/kbxutil.c:101 msgid "Please report bugs to " msgstr "Bite richten sie Berichte über Bugs (Softwarefehler) an " -#: kbx/kbxutil.c:99 +#: kbx/kbxutil.c:101 msgid ".\n" msgstr ".\n" -#: kbx/kbxutil.c:103 +#: kbx/kbxutil.c:105 msgid "Usage: kbxutil [options] [files] (-h for help)" msgstr "Gebrauch: kbxutil [Optionen] [Dateien] (-h für Hilfe)" -#: kbx/kbxutil.c:106 +#: kbx/kbxutil.c:108 msgid "" "Syntax: kbxutil [options] [files]\n" "list, export, import Keybox data\n" @@ -350,7 +362,7 @@ msgstr "" "Syntax: kbxutil [Optionen] [Dateien]\n" "Anlistem exportieren und Importieren von KeyBox Dateien\n" -#: scd/scdaemon.c:100 sm/gpgsm.c:332 +#: scd/scdaemon.c:100 sm/gpgsm.c:333 msgid "read options from file" msgstr "Konfigurationsoptionen aus Datei lesen" @@ -523,8 +535,8 @@ msgstr "Der Herausgeber wird von einer externen Stelle gesucht\n" msgid "number of issuers matching: %d\n" msgstr "Anzahl der übereinstimmenden Heruasgeber: %d\n" -#: sm/certchain.c:403 sm/certchain.c:561 sm/certchain.c:909 sm/decrypt.c:260 -#: sm/encrypt.c:341 sm/sign.c:324 sm/verify.c:107 +#: sm/certchain.c:403 sm/certchain.c:561 sm/certchain.c:912 sm/decrypt.c:260 +#: sm/encrypt.c:341 sm/sign.c:324 sm/verify.c:106 msgid "failed to allocated keyDB handle\n" msgstr "Ein keyDB Handle konnte nicht bereitgestellt werden\n" @@ -589,25 +601,25 @@ msgstr "Das Wurzelzertifikat wurde nun als vertrauensw msgid "checking the trust list failed: %s\n" msgstr "Fehler beim Prüfen der vertrauenswürdigen Zertifikate: %s\n" -#: sm/certchain.c:721 sm/import.c:145 +#: sm/certchain.c:724 sm/import.c:166 msgid "certificate chain too long\n" msgstr "Der Zertifikatkette ist zu lang\n" -#: sm/certchain.c:733 +#: sm/certchain.c:736 msgid "issuer certificate not found" msgstr "Herausgeberzertifikat nicht gefunden" -#: sm/certchain.c:766 +#: sm/certchain.c:769 msgid "certificate has a BAD signature" msgstr "Das Zertifikat hat eine FALSCHE Signatur" -#: sm/certchain.c:789 +#: sm/certchain.c:792 msgid "found another possible matching CA certificate - trying again" msgstr "" "Eine anderes möglicherweise passendes CA-Zertifikat gefunden - versuche " "nochmal" -#: sm/certchain.c:812 +#: sm/certchain.c:815 #, c-format msgid "certificate chain longer than allowed by CA (%d)" msgstr "Die Zertifikatkette ist länger als von der CA erlaubt (%d)" @@ -660,227 +672,235 @@ msgstr "Schwacher Schl msgid "no valid recipients given\n" msgstr "Keine gültigen Empfänger angegeben\n" -#: sm/gpgsm.c:225 +#: sm/gpgsm.c:226 msgid "|[FILE]|make a signature" msgstr "|[DATEI]|Erzeuge eine Signatur" -#: sm/gpgsm.c:226 +#: sm/gpgsm.c:227 msgid "|[FILE]|make a clear text signature" msgstr "|[DATEI]|Erzeuge eine Klartextsignatur" -#: sm/gpgsm.c:227 +#: sm/gpgsm.c:228 msgid "make a detached signature" msgstr "Erzeuge eine abgetrennte Signatur" -#: sm/gpgsm.c:228 +#: sm/gpgsm.c:229 msgid "encrypt data" msgstr "Verschlüssele die Daten" -#: sm/gpgsm.c:229 +#: sm/gpgsm.c:230 msgid "encryption only with symmetric cipher" msgstr "Verschlüsselung nur mit symmetrischem Algrithmus" -#: sm/gpgsm.c:230 +#: sm/gpgsm.c:231 msgid "decrypt data (default)" msgstr "Enschlüssele die Daten" -#: sm/gpgsm.c:231 +#: sm/gpgsm.c:232 msgid "verify a signature" msgstr "Überprüfen einer Signatur" -#: sm/gpgsm.c:233 +#: sm/gpgsm.c:234 msgid "list keys" msgstr "Schlüssel anzeigen" -#: sm/gpgsm.c:234 +#: sm/gpgsm.c:235 msgid "list external keys" msgstr "Externe Schlüssel anzeigen" -#: sm/gpgsm.c:235 +#: sm/gpgsm.c:236 msgid "list secret keys" msgstr "Geheime Schlüssel anzeigen" -#: sm/gpgsm.c:236 +#: sm/gpgsm.c:237 msgid "list certificate chain" msgstr "Schlüssel mit Zertifikatekette anzeigen" -#: sm/gpgsm.c:238 +#: sm/gpgsm.c:239 msgid "list keys and fingerprints" msgstr "Schlüssel und Fingerprint anzeigen" -#: sm/gpgsm.c:239 +#: sm/gpgsm.c:240 msgid "generate a new key pair" msgstr "Neues Schlüsselpaar erzeugen" -#: sm/gpgsm.c:240 +#: sm/gpgsm.c:241 msgid "remove key from the public keyring" msgstr "Schlüssel aus dem öffentlichen Schlüsselbund löschen" -#: sm/gpgsm.c:241 +#: sm/gpgsm.c:242 msgid "export keys to a key server" msgstr "Schlüssen an eine Schlüsselserver exportieren" -#: sm/gpgsm.c:242 +#: sm/gpgsm.c:243 msgid "import keys from a key server" msgstr "Schlüssel von einem Schlüsselserver importieren" -#: sm/gpgsm.c:243 +#: sm/gpgsm.c:244 msgid "import certificates" msgstr "Zertifikate importieren" -#: sm/gpgsm.c:244 +#: sm/gpgsm.c:245 msgid "export certificates" msgstr "Zertifikate exportieren" -#: sm/gpgsm.c:245 +#: sm/gpgsm.c:246 msgid "register a smartcard" msgstr "Smartcard registrieren" -#: sm/gpgsm.c:246 +#: sm/gpgsm.c:247 msgid "run in server mode" msgstr "Im Server Modus ausführen" -#: sm/gpgsm.c:247 +#: sm/gpgsm.c:248 msgid "pass a command to the dirmngr" msgstr "Das Kommand an den Dirmngr durchreichen" -#: sm/gpgsm.c:249 +#: sm/gpgsm.c:250 msgid "invoke gpg-protect-tool" msgstr "Rufe das gpg-protect-tool auf" -#: sm/gpgsm.c:250 +#: sm/gpgsm.c:251 msgid "change a passphrase" msgstr "Das Mantra (Passphrase) ändern" -#: sm/gpgsm.c:262 +#: sm/gpgsm.c:261 +msgid "create ascii armored output" +msgstr "Ausgabe mit ASCII Hülle wird erzeugt" + +#: sm/gpgsm.c:263 msgid "create base-64 encoded output" msgstr "Ausgabe im Basis-64 format erzeugen" -#: sm/gpgsm.c:264 +#: sm/gpgsm.c:265 msgid "assume input is in PEM format" msgstr "Eingabedaten sind im PEM Format" -#: sm/gpgsm.c:266 +#: sm/gpgsm.c:267 msgid "assume input is in base-64 format" msgstr "Eingabedaten sin im Basis-64 Format" -#: sm/gpgsm.c:268 +#: sm/gpgsm.c:269 msgid "assume input is in binary format" msgstr "Eingabedaten sind im Binärformat" -#: sm/gpgsm.c:270 +#: sm/gpgsm.c:271 msgid "|NAME|encrypt for NAME" msgstr "|NAME|Verschlüsseln für NAME" -#: sm/gpgsm.c:273 +#: sm/gpgsm.c:274 msgid "never consult a CRL" msgstr "Niemals eine CRL konsultieren" -#: sm/gpgsm.c:278 +#: sm/gpgsm.c:279 msgid "check validity using OCSP" msgstr "Die Gültigkeit mittels OCSP prüfen" -#: sm/gpgsm.c:281 +#: sm/gpgsm.c:282 msgid "|N|number of certificates to include" msgstr "|N|Sende N Zertifikate mit" -#: sm/gpgsm.c:284 +#: sm/gpgsm.c:285 msgid "|FILE|take policy information from FILE" msgstr "|DATEI|Richtlinieninformationen DATEI entnehmen" -#: sm/gpgsm.c:287 +#: sm/gpgsm.c:288 msgid "do not check certificate policies" msgstr "Zertikikatrichtlinien nicht überprüfen" -#: sm/gpgsm.c:291 +#: sm/gpgsm.c:292 msgid "fetch missing issuer certificates" msgstr "Fehlende Zertifikate automatisch holen" -#: sm/gpgsm.c:295 +#: sm/gpgsm.c:296 msgid "|NAME|use NAME as default recipient" msgstr "|NAME|Benutze NAME als voreingestellten Empfänger" -#: sm/gpgsm.c:297 +#: sm/gpgsm.c:298 msgid "use the default key as default recipient" msgstr "Benuzte voreingestellten Schlüssel als Standardempfänger" -#: sm/gpgsm.c:303 +#: sm/gpgsm.c:304 msgid "use this user-id to sign or decrypt" msgstr "Benuzte diese Benutzer ID zum Signieren oder Entschlüsseln" -#: sm/gpgsm.c:306 +#: sm/gpgsm.c:307 msgid "|N|set compress level N (0 disables)" msgstr "|N|Benutze Komprimierungsstufe N" -#: sm/gpgsm.c:308 +#: sm/gpgsm.c:309 msgid "use canonical text mode" msgstr "Kanonischen Textmodus benutzen" -#: sm/gpgsm.c:314 +#: sm/gpgsm.c:312 tools/gpgconf.c:61 +msgid "use as output file" +msgstr "als Ausgabedatei benutzen" + +#: sm/gpgsm.c:315 msgid "don't use the terminal at all" msgstr "Das Terminal überhaupt nicht benutzen" -#: sm/gpgsm.c:317 +#: sm/gpgsm.c:318 msgid "force v3 signatures" msgstr "Version 3 Signaturen erzwingen" -#: sm/gpgsm.c:318 +#: sm/gpgsm.c:319 msgid "always use a MDC for encryption" msgstr "Immer das MDC Verfahren zum verschlüsseln mitbenutzen" -#: sm/gpgsm.c:323 +#: sm/gpgsm.c:324 msgid "batch mode: never ask" msgstr "Stapelverarbeitungs Modus: Nie nachfragen" -#: sm/gpgsm.c:324 +#: sm/gpgsm.c:325 msgid "assume yes on most questions" msgstr "\"Ja\" auf die meisten Anfragen annehmen" -#: sm/gpgsm.c:325 +#: sm/gpgsm.c:326 msgid "assume no on most questions" msgstr "\"Nein\" auf die meisten Anfragen annehmen" -#: sm/gpgsm.c:327 +#: sm/gpgsm.c:328 msgid "add this keyring to the list of keyrings" msgstr "Diesen Keyring in die Liste der Keyrings aufnehmen" -#: sm/gpgsm.c:328 +#: sm/gpgsm.c:329 msgid "add this secret keyring to the list" msgstr "Diese geheimen Keyring in die Liste aufnehmen" -#: sm/gpgsm.c:329 +#: sm/gpgsm.c:330 msgid "|NAME|use NAME as default secret key" msgstr "|NAME|Benutze NAME als voreingestellten Schlüssel" -#: sm/gpgsm.c:330 +#: sm/gpgsm.c:331 msgid "|HOST|use this keyserver to lookup keys" msgstr "|HOST|Benutze HOST als Schlüsselserver" -#: sm/gpgsm.c:331 +#: sm/gpgsm.c:332 msgid "|NAME|set terminal charset to NAME" msgstr "|NAME|Den Zeichensatz für das Terminal auf NAME setzen" -#: sm/gpgsm.c:340 +#: sm/gpgsm.c:342 msgid "|FD|write status info to this FD" msgstr "|FD|Statusinformationen auf Dateidescriptor FD schreiben" -#: sm/gpgsm.c:347 +#: sm/gpgsm.c:349 msgid "|FILE|load extension module FILE" msgstr "|DATEI|Das Erweiterungsmodul DATEI laden" -#: sm/gpgsm.c:353 +#: sm/gpgsm.c:355 msgid "|NAME|use cipher algorithm NAME" msgstr "|NAME|Den Verhsclüsselungsalgrithmus NAME benutzen" -#: sm/gpgsm.c:355 +#: sm/gpgsm.c:357 msgid "|NAME|use message digest algorithm NAME" msgstr "|NAME|Den Hashalgorithmus NAME benutzen" -#: sm/gpgsm.c:357 +#: sm/gpgsm.c:359 msgid "|N|use compress algorithm N" msgstr "|N|Den Kompressionsalgorithmus Nummer N benutzen" -#: sm/gpgsm.c:365 +#: sm/gpgsm.c:367 msgid "" "@\n" "(See the man page for a complete listing of all commands and options)\n" @@ -888,7 +908,7 @@ msgstr "" "@\n" "(Die \"man\" Seite beschreibt alle Kommands und Optionen)\n" -#: sm/gpgsm.c:368 +#: sm/gpgsm.c:370 msgid "" "@\n" "Examples:\n" @@ -908,11 +928,11 @@ msgstr "" " --list-keys [Namen] Schlüssel anzeigenn\n" " --fingerprint [Namen] \"Fingerabdrücke\" anzeigen\\n\n" -#: sm/gpgsm.c:482 +#: sm/gpgsm.c:484 msgid "Usage: gpgsm [options] [files] (-h for help)" msgstr "Gebrauch: gpgsm [Optionen] [Dateien] (-h für Hilfe)" -#: sm/gpgsm.c:485 +#: sm/gpgsm.c:487 msgid "" "Syntax: gpgsm [options] [files]\n" "sign, check, encrypt or decrypt using the S/MIME protocol\n" @@ -921,7 +941,7 @@ msgstr "" "Gebrauch: gpgsm [Optionen] [Dateien]\n" "Signieren, prüfen, ver- und entschlüsseln mittels S/MIME protocol\n" -#: sm/gpgsm.c:492 +#: sm/gpgsm.c:494 msgid "" "\n" "Supported algorithms:\n" @@ -929,129 +949,144 @@ msgstr "" "\n" "Unterstützte Algorithmen:\n" -#: sm/gpgsm.c:573 +#: sm/gpgsm.c:575 msgid "usage: gpgsm [options] " msgstr "Gebrauch: gpgsm [Optionen] " -#: sm/gpgsm.c:639 +#: sm/gpgsm.c:641 msgid "conflicting commands\n" msgstr "Widersprechende Kommandos\n" -#: sm/gpgsm.c:655 +#: sm/gpgsm.c:657 #, c-format msgid "can't encrypt to `%s': %s\n" msgstr "Verschlüsseln für `%s' nicht möglich: %s\n" -#: sm/gpgsm.c:730 +#: sm/gpgsm.c:731 #, c-format msgid "libksba is too old (need %s, have %s)\n" msgstr "Die Bibliothek Libksba is nicht aktuell (benötige %s, habe %s)\n" -#: sm/gpgsm.c:1169 +#: sm/gpgsm.c:1175 msgid "WARNING: program may create a core file!\n" msgstr "WARNUNG: Programm könnte eine core-dump-Datei schreiben!\n" -#: sm/gpgsm.c:1181 +#: sm/gpgsm.c:1187 msgid "WARNING: running with faked system time: " msgstr "WARNUNG: Ausführung mit gefälschter Systemzeit: " -#: sm/gpgsm.c:1201 +#: sm/gpgsm.c:1213 msgid "selected cipher algorithm is invalid\n" msgstr "Das ausgewählte Verschlüsselungsverfahren ist ungültig\n" -#: sm/gpgsm.c:1209 +#: sm/gpgsm.c:1221 msgid "selected digest algorithm is invalid\n" msgstr "Das ausgewählte Hashverfahren ist ungültig\n" -#: sm/gpgsm.c:1239 +#: sm/gpgsm.c:1251 #, c-format msgid "can't sign using `%s': %s\n" msgstr "Signieren mit `%s' nicht möglich: %s\n" -#: sm/gpgsm.c:1406 +#: sm/gpgsm.c:1415 msgid "this command has not yet been implemented\n" msgstr "Diee Kommando wurde noch nicht implementiert\n" -#: sm/gpgsm.c:1629 sm/gpgsm.c:1662 +#: sm/gpgsm.c:1638 sm/gpgsm.c:1671 #, c-format msgid "can't open `%s': %s\n" msgstr "Datei `%s' kann nicht geöffnet werden: %s\n" -#: sm/import.c:114 +#: sm/import.c:118 #, c-format msgid "total number processed: %lu\n" msgstr "gesamte verarbeitete Anzahl: %lu\n" -#: sm/import.c:117 +#: sm/import.c:121 #, c-format msgid " imported: %lu" msgstr " importiert: %lu" -#: sm/import.c:121 +#: sm/import.c:125 #, c-format msgid " unchanged: %lu\n" msgstr " nicht geändert: %lu\n" -#: sm/import.c:123 +#: sm/import.c:127 +#, c-format +msgid " secret keys read: %lu\n" +msgstr " gelesene private Schlüssel: %lu\n" + +#: sm/import.c:129 +#, c-format +msgid " secret keys imported: %lu\n" +msgstr "importierte priv. Schlüssel: %lu\n" + +#: sm/import.c:131 +#, c-format +msgid " secret keys unchanged: %lu\n" +msgstr "ungeänderte priv. Schlüssel: %lu\n" + +#: sm/import.c:133 #, c-format msgid " not imported: %lu\n" msgstr " nicht importiert: %lu\n" -#: sm/import.c:204 +#: sm/import.c:229 msgid "error storing certificate\n" msgstr "Fehler beim speichern des Zertifikats\n" -#: sm/import.c:211 +#: sm/import.c:237 msgid "basic certificate checks failed - not imported\n" msgstr "Grundlegende Zertifikatprüfungen fehlgeschlagen - nicht importiert\n" -#: sm/import.c:396 sm/import.c:428 +#: sm/import.c:423 sm/import.c:455 #, c-format msgid "error importing certificate: %s\n" msgstr "Fehler beim Importieren des Zertifikats: %s\n" -#: sm/import.c:457 +#: sm/import.c:484 #, c-format msgid "error creating a pipe: %s\n" msgstr "Fehler beim Erzeugen einer \"Pipe\": %s\n" -#: sm/import.c:465 +#: sm/import.c:492 #, c-format msgid "error forking process: %s\n" msgstr "Fehler beim \"Forken\" des Prozess: %s\n" -#: sm/import.c:562 sm/import.c:587 +#: sm/import.c:590 sm/import.c:615 #, c-format msgid "error creating temporary file: %s\n" msgstr "Fehler beim Erstellen einer temporären Datei: %s\n" -#: sm/import.c:570 +#: sm/import.c:598 #, c-format msgid "error writing to temporary file: %s\n" msgstr "Fehler beim Schreiben auf eine temporäre Datei: %s\n" -#: sm/import.c:579 +#: sm/import.c:607 #, c-format msgid "error reading input: %s\n" msgstr "Fehler beim Lesen der Eingabe: %s\n" -#: sm/import.c:649 +#: sm/import.c:703 #, c-format msgid "waiting for protect-tool to terminate failed: %s\n" msgstr "" "Das Warten auf die Beendigung des protect-tools ist fehlgeschlagen: %s\n" -#: sm/import.c:652 +#: sm/import.c:706 #, c-format msgid "error running `%s': probably not installed\n" msgstr "Feler bei Ausführung von `%s': wahrscheinlich nicht installiert\n" -#: sm/import.c:654 +#: sm/import.c:708 #, c-format msgid "error running `%s': exit status %d\n" msgstr "Fehler bei Ausführung von `%s': Endestatus %d\n" -#: sm/import.c:657 +#: sm/import.c:711 #, c-format msgid "error running `%s': terminated\n" msgstr "Fehler beim Ausführen von `%s': beendet\n" @@ -1113,24 +1148,24 @@ msgstr "Fehler beim Holen der gespeicherten Flags: %s\n" msgid "error storing flags: %s\n" msgstr "Fehler beim Speichern der Flags: %s\n" -#: sm/verify.c:387 +#: sm/verify.c:381 msgid "Signature made " msgstr "Signatur erzeugt am " -#: sm/verify.c:391 +#: sm/verify.c:385 msgid "[date not given]" msgstr "[Datum nicht vorhanden]" -#: sm/verify.c:392 +#: sm/verify.c:386 #, c-format msgid " using certificate ID %08lX\n" msgstr "mittels Zertifikat ID %08lX\n" -#: sm/verify.c:505 +#: sm/verify.c:499 msgid "Good signature from" msgstr "Korrekte Signatur von" -#: sm/verify.c:506 +#: sm/verify.c:500 msgid " aka" msgstr " alias" @@ -1188,6 +1223,3 @@ msgstr "Komponente nicht gefunden" #~ msgstr "" #~ "Schlüsselverwendungszweck nicht vorhanden - wird zum Verschlüsseln " #~ "akzeptiert\n" - -#~ msgid " skipped new keys: %lu\n" -#~ msgstr " übersprungene Schlüssel: %lu\n" diff --git a/scd/ChangeLog b/scd/ChangeLog index 1d3cff257..3a634b92b 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,7 @@ +2004-07-22 Werner Koch + + * scdaemon.c (main): Bumbed thread stack size up to 512k. + 2004-07-16 Werner Koch * apdu.c (reader_table_s): Add function pointers for the backends. diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 5e9737ae4..b0038de16 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -603,7 +603,7 @@ main (int argc, char **argv ) tattr = pth_attr_new(); pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0); - pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 64*1024); + pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 512*1024); pth_attr_set (tattr, PTH_ATTR_NAME, "ticker"); if (!pth_spawn (tattr, ticker_thread, NULL)) diff --git a/sm/ChangeLog b/sm/ChangeLog index 5571540be..b9780cc90 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,3 +1,7 @@ +2004-07-22 Werner Koch + + * keylist.c (list_cert_raw): Print the keygrip. + 2004-07-20 Werner Koch * certchain.c (gpgsm_validate_chain): The trust check didn't diff --git a/sm/keylist.c b/sm/keylist.c index 27c67ded3..0fd06ca13 100644 --- a/sm/keylist.c +++ b/sm/keylist.c @@ -500,7 +500,7 @@ print_names_raw (FILE *fp, int indent, ksba_name_t name) /* List one certificate in raw mode useful to have a closer look at - the certificate. This one does not beautification and only minimal + the certificate. This one does no beautification and only minimal output sanitation. It is mainly useful for debugging. */ static void list_cert_raw (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret, @@ -559,6 +559,10 @@ list_cert_raw (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret, fprintf (fp, " md5_fpr: %s\n", dn?dn:"error"); xfree (dn); + dn = gpgsm_get_keygrip_hexstring (cert); + fprintf (fp, " keygrip: %s\n", dn?dn:"error"); + xfree (dn); + ksba_cert_get_validity (cert, 0, t); fputs (" notBefore: ", fp); gpgsm_print_time (fp, t); -- cgit From 44bf2d0ae4adbcf5b6b404bfc6526c461ae6a92a Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 22 Jul 2004 10:52:56 +0000 Subject: * configure.ac: Define AM conditional HAVE_OPENSC. * Makefile.am: Make OpenSC lib link after libgcrypt. Do not link to pth. * apdu.c: Don't use Pth if we use OpenSC. * sc-investigate.c, scdaemon.c: Disable use of pth if OpenSC is used. * scdaemon.c (main): Bumbed thread stack size up to 512k. --- ChangeLog | 2 ++ configure.ac | 1 + scd/ChangeLog | 5 +++++ scd/Makefile.am | 21 ++++++++++++++------- scd/apdu.c | 4 ++++ scd/sc-investigate.c | 11 +++++++---- scd/scdaemon.c | 9 ++++++++- 7 files changed, 41 insertions(+), 12 deletions(-) (limited to 'scd/scdaemon.c') diff --git a/ChangeLog b/ChangeLog index 42fe782af..d83d94cde 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,8 @@ Released 1.9.10. + * configure.ac: Define AM conditional HAVE_OPENSC. + 2004-07-21 Werner Koch * configure.ac: Don't set DIE to no after it might has been set to diff --git a/configure.ac b/configure.ac index 87c015033..94760f529 100644 --- a/configure.ac +++ b/configure.ac @@ -471,6 +471,7 @@ if test $have_opensc = yes; then AC_DEFINE(HAVE_OPENSC,1, [defined if the OpenSC library is available]) fi +AM_CONDITIONAL(HAVE_OPENSC, test "$have_opensc" = "yes") # # Check whether the (highly desirable) GNU Pth library is available diff --git a/scd/ChangeLog b/scd/ChangeLog index 3a634b92b..cb759c0b2 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,5 +1,10 @@ 2004-07-22 Werner Koch + * Makefile.am: Make OpenSC lib link after libgcrypt. Do not link + to pth. + * apdu.c: Don't use Pth if we use OpenSC. + * sc-investigate.c, scdaemon.c: Disable use of pth if OpenSC is used. + * scdaemon.c (main): Bumbed thread stack size up to 512k. 2004-07-16 Werner Koch diff --git a/scd/Makefile.am b/scd/Makefile.am index c3c603d28..e3dfbf0b6 100644 --- a/scd/Makefile.am +++ b/scd/Makefile.am @@ -25,6 +25,13 @@ AM_CPPFLAGS = -I$(top_srcdir)/intl -I$(top_srcdir)/common include $(top_srcdir)/am/cmacros.am +# avoid linking against Pth if we are using OpenSC. +if HAVE_OPENSC +pth_libs = +else +pth_libs = $(PTH_LIBS) +endif + AM_CFLAGS = $(OPENSC_CFLAGS) $(LIBGCRYPT_CFLAGS) \ $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) $(PTH_CFLAGS) @@ -44,9 +51,9 @@ scdaemon_SOURCES = \ scdaemon_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \ - $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(PTH_LIBS) \ + $(LIBGCRYPT_LIBS) $(pth_libs) \ $(KSBA_LIBS) $(LIBASSUAN_LIBS) \ - $(LIBUSB_LIBS) -lgpg-error @INTLLIBS@ -ldl + $(LIBUSB_LIBS) $(OPENSC_LIBS) -lgpg-error @INTLLIBS@ -ldl sc_investigate_SOURCES = \ sc-investigate.c scdaemon.h \ @@ -59,8 +66,8 @@ sc_investigate_SOURCES = \ sc_investigate_LDADD = \ ../jnlib/libjnlib.a ../common/libcommon.a \ - $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(PTH_LIBS) \ - $(KSBA_LIBS) $(LIBUSB_LIBS) \ + $(LIBGCRYPT_LIBS) $(pth_libs) \ + $(KSBA_LIBS) $(LIBUSB_LIBS) $(OPENSC_LIBS) \ @INTLLIBS@ -lgpg-error -ldl @@ -76,10 +83,10 @@ sc_copykeys_SOURCES = \ sc_copykeys_LDADD = \ ../jnlib/libjnlib.a ../common/libcommon.a \ ../common/libsimple-pwquery.a \ - $(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(PTH_LIBS) \ - $(KSBA_LIBS) $(LIBUSB_LIBS) \ + $(LIBGCRYPT_LIBS) $(pth_libs) \ + $(KSBA_LIBS) $(LIBUSB_LIBS) $(OPENSC_LIBS) \ -lgpg-error @INTLLIBS@ -ldl pcsc_wrapper_SOURCES = pcsc-wrapper.c pcsc_wrapper_LDADD = -ldl -pcsc_wrapper_CFLAGS = \ No newline at end of file +pcsc_wrapper_CFLAGS = diff --git a/scd/apdu.c b/scd/apdu.c index cc9b213fb..28d32ad2c 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -19,6 +19,7 @@ */ #include + #include #include #include @@ -31,6 +32,9 @@ #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 diff --git a/scd/sc-investigate.c b/scd/sc-investigate.c index be985533c..8d34ab77e 100644 --- a/scd/sc-investigate.c +++ b/scd/sc-investigate.c @@ -90,12 +90,12 @@ static ARGPARSE_OPTS opts[] = { {0} }; - +#ifndef HAVE_OPENSC #ifdef USE_GNU_PTH /* Pth wrapper function definitions. */ GCRY_THREAD_OPTION_PTH_IMPL; #endif /*USE_GNU_PTH*/ - +#endif /*!HAVE_OPENSC*/ static void interactive_shell (int slot); static void dump_other_cards (int slot); @@ -160,8 +160,10 @@ main (int argc, char **argv ) /* Try to auto set the character set. */ set_native_charset (NULL); - /* Libgcrypt requires us to register the threading model first. - Note that this will also do the pth_init. */ + /* Libgcrypt requires us to register the threading model first. We + can't use pth at all if we are using OpenSC becuase OpenSC uses + ptreads. Note that this will also do the pth_init. */ +#ifndef HAVE_OPENSC #ifdef USE_GNU_PTH rc = gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth); if (rc) @@ -170,6 +172,7 @@ main (int argc, char **argv ) gpg_strerror (rc)); } #endif /*USE_GNU_PTH*/ +#endif /*!HAVE_OPENSC*/ /* Check that the libraries are suitable. Do it here because the option parsing may need services of the library */ diff --git a/scd/scdaemon.c b/scd/scdaemon.c index b0038de16..f647aecc2 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -144,13 +144,14 @@ static int maybe_setuid = 1; static char socket_name[128]; +#ifndef HAVE_OPENSC #ifdef USE_GNU_PTH /* Pth wrapper function definitions. */ GCRY_THREAD_OPTION_PTH_IMPL; static void *ticker_thread (void *arg); #endif /*USE_GNU_PTH*/ - +#endif /*!HAVE_OPENSC*/ static const char * my_strusage (int level) @@ -336,6 +337,7 @@ main (int argc, char **argv ) /* Libgcrypt requires us to register the threading model first. Note that this will also do the pth_init. */ +#ifndef HAVE_OPENSC #ifdef USE_GNU_PTH err = gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth); if (err) @@ -344,6 +346,7 @@ main (int argc, char **argv ) gpg_strerror (err)); } #endif /*USE_GNU_PTH*/ +#endif /*!HAVE_OPENSC*/ /* Check that the libraries are suitable. Do it here because the option parsing may need services of the library */ @@ -598,6 +601,7 @@ main (int argc, char **argv ) if (pipe_server) { /* This is the simple pipe based server */ +#ifndef HAVE_OPENSC #ifdef USE_GNU_PTH pth_attr_t tattr; @@ -612,6 +616,7 @@ main (int argc, char **argv ) scd_exit (2); } #endif /*USE_GNU_PTH*/ +#endif /*!HAVE_OPENSC*/ scd_command_handler (-1); } else if (!is_daemon) @@ -827,6 +832,7 @@ scd_init_default_ctrl (CTRL ctrl) } +#ifndef HAVE_OPENSC #ifdef USE_GNU_PTH static void @@ -931,3 +937,4 @@ ticker_thread (void *dummy_arg) pth_event_free (sigs_ev, PTH_FREE_ALL); } #endif /*USE_GNU_PTH*/ +#endif /*!HAVE_OPENSC*/ -- cgit From 818e9bad581868f8455a62fb3548b4dcc9d4cbac Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 5 Aug 2004 09:24:36 +0000 Subject: * configure.ac: Changed tests for libusb to also suuport the stable version 0.1.x. * scdaemon.texi (Card applications): New section. * scdaemon.c (main): New option --disable-application. * app.c (is_app_allowed): New. (select_application): Use it to check for disabled applications. * 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. --- ChangeLog | 5 +++ TODO | 22 +++++++++++ configure.ac | 6 +-- doc/ChangeLog | 4 ++ doc/scdaemon.texi | 57 ++++++++++++++++++++++++++- scd/ChangeLog | 13 +++++++ scd/apdu.c | 39 ++++++++++++++++++- scd/apdu.h | 3 +- scd/app.c | 19 +++++++-- scd/ccid-driver.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++------ scd/ccid-driver.h | 2 +- scd/iso7816.c | 1 + scd/scdaemon.c | 6 +++ scd/scdaemon.h | 2 + sm/ChangeLog | 5 +++ sm/certreqgen.c | 15 +++---- 16 files changed, 282 insertions(+), 31 deletions(-) (limited to 'scd/scdaemon.c') diff --git a/ChangeLog b/ChangeLog index d83d94cde..7b14b7944 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2004-08-05 Werner Koch + + * configure.ac: Changed tests for libusb to also suuport the + stable version 0.1.x. + 2004-07-22 Werner Koch Released 1.9.10. diff --git a/TODO b/TODO index ce835d8ec..6e5c8cfb8 100644 --- a/TODO +++ b/TODO @@ -79,3 +79,25 @@ might want to have an agent context for each service request * doc/ ** Explain how to setup a root CA key as trusted ** Explain how trustlist.txt might be managed. + + +* Requirements by the BSI +** Support authorityKeyIdentifier.keyIdentifier + This needs support in libksba/src/cert.c as well as in sm/*.c. + Need test certs as well. Same goes for CRL authorityKeyIdentifier. + +** For pkcs#10 request header. + We use "NEW CERTIFICATE REQUEST" the specs say "CERTIFICATE + REQUEST" should be used. However it seems that their CA software + is also able to use our header. Binary pkcs#10 request are not + allowed. + +** Dirmngr: name subordination (nameRelativeToCRLIssuer) + is not yet supported by Dirmngr. + +** Dirmngr: CRL DP URI + The CRL DP shall use an URI for LDAP without a host name. The host + name shall be looked by using the DN in the URI. We don't implement + this yet. Solution is to have a mapping DN->host in our ldapservers + configuration file. + diff --git a/configure.ac b/configure.ac index ca6eaf14f..f34faf469 100644 --- a/configure.ac +++ b/configure.ac @@ -451,15 +451,13 @@ AM_PATH_KSBA("$NEED_KSBA_VERSION",have_ksba=yes,have_ksba=no) # # libusb allows us to use the integrated CCID smartcard reader driver. # -# Note, that we need the CVS version. FIXME: libusb should have a -# regular check as the other libraries do. -# -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]) ]) AC_SUBST(LIBUSB_LIBS) +AC_CHECK_FUNCS(usb_create_match) # # Check wether it is necessary to link against libdl. diff --git a/doc/ChangeLog b/doc/ChangeLog index 1760695be..79a8c9532 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,7 @@ +2004-08-05 Werner Koch + + * scdaemon.texi (Card applications): New section. + 2004-06-22 Werner Koch * glossary.texi: New. diff --git a/doc/scdaemon.texi b/doc/scdaemon.texi index 51ec4b34c..3e11a8930 100644 --- a/doc/scdaemon.texi +++ b/doc/scdaemon.texi @@ -10,7 +10,7 @@ @c man begin DESCRIPTION -The @sc{scdaeon} is a daemon to manage smartcards. It is usually +The @sc{scdaemon} is a daemon to manage smartcards. It is usually invoked by gpg-agent and in general not used directly. @c man end @@ -20,6 +20,7 @@ invoked by gpg-agent and in general not used directly. @menu * Scdaemon Commands:: List of all commands. * Scdaemon Options:: List of all options. +* Card applications:: Description of card applications. * Scdaemon Examples:: Some usage examples. * Scdaemon Protocol:: The protocol the daemon uses. @end menu @@ -176,18 +177,70 @@ is @code{libtowitoko.so}. @itemx --deny-admin @opindex allow-admin @opindex deny-admin -This enables the use of Admin class commands for card application +This enables the use of Admin class commands for card applications where this is supported. Currently we support it for the OpenPGP card. Deny is the default. This commands is useful to inhibit accidental access to admin class command which could ultimately lock the card through worng PIN numbers. +@item --disable-application @var{name} +@opindex disable-application +This option disables the use of the card application named +@var{name}. This is mainly useful for debugging or if a application +with lower priority should be used by default. + @end table All the long options may also be given in the configuration file after stripping off the two leading dashes. +@c man begin CARD APPLICATIONS + +@node Card applications +@section Description of card applications + +@sc{scdaemon} supports the card applications as described below. + +@menu +* OpenPGP Card:: The OpenPGP card application +* NKS Card:: The Telesec NetKey card application +* DINSIG Card:: The DINSIG card application +* PKCS#15 Card:: The PKCS#15 card application +@end menu + +@node OpenPGP Card +@subsection The OpenPGP card application ``openpgp'' + +This application is currently only used by @sc{gpg} but may in +future also be useful with @sc{gpgsm}. + +The specification for such a card is available at +@uref{http://g10code.com/docs/openpgp-card-1.0.pdf}. + +@node NKS Card +@subsection The Telesec NetKey card ``nks'' + +This is the main application of the Telesec cards as available in +Germany. It is a superset of the German DINSIG card. The card is +used by @sc{gpgsm}. + +@node DINSIG Card +@subsection The DINSIG card application ``dinsig'' + +This is an application as described in the German draft standard +@emph{DIN V 66291-1}. It is intended to be used by cards supporteing +the German signature law and its bylaws (SigG and SigV). + +@node PKCS#15 Card +@subsection The PKCS#15 card application ``p15'' + +This is common fraqmework for smart card applications; support is only +available if compiled with support for the OpenSC library. It is used +by @sc{gpgsm}. + + + @c @c Examples @c diff --git a/scd/ChangeLog b/scd/ChangeLog index b0f07dc82..80b244ef1 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,16 @@ +2004-08-05 Werner Koch + + * scdaemon.c (main): New option --disable-application. + * app.c (is_app_allowed): New. + (select_application): Use it to check for disabled applications. + + * 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. + 2004-07-27 Moritz Schulte * apdu.c: Include . diff --git a/scd/apdu.c b/scd/apdu.c index 7898452c4..5f800c983 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -19,7 +19,6 @@ */ #include - #include #include #include @@ -296,6 +295,7 @@ host_sw_string (long err) 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"; } } @@ -1633,7 +1633,42 @@ reset_rapdu_reader (int slot) static int 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; } diff --git a/scd/apdu.h b/scd/apdu.h index a2b781266..a0654a242 100644 --- a/scd/apdu.h +++ b/scd/apdu.h @@ -58,7 +58,8 @@ enum { SW_HOST_CARD_INACTIVE = 0x10009, SW_HOST_CARD_IO_ERROR = 0x1000a, SW_HOST_GENERAL_ERROR = 0x1000b, - SW_HOST_NO_READER = 0x1000c + SW_HOST_NO_READER = 0x1000c, + SW_HOST_ABORTED = 0x1000d }; diff --git a/scd/app.c b/scd/app.c index a9a9243eb..b3a13f19a 100644 --- a/scd/app.c +++ b/scd/app.c @@ -32,6 +32,19 @@ #include "tlv.h" +/* Check wether the application NAME is allowed. This does not mean + we have support for it though. */ +static int +is_app_allowed (const char *name) +{ + strlist_t l; + + for (l=opt.disabled_applications; l; l = l->next) + if (!strcmp (l->d, name)) + return 0; /* no */ + return 1; /* yes */ +} + /* If called with NAME as NULL, select the best fitting application and return a context; otherwise select the application with NAME and return a context. SLOT identifies the reader device. Returns @@ -84,11 +97,11 @@ select_application (ctrl_t ctrl, int slot, const char *name) rc = gpg_error (GPG_ERR_NOT_FOUND); - if (!name || !strcmp (name, "openpgp")) + if (rc && is_app_allowed ("openpgp") && (!name || !strcmp (name, "openpgp"))) rc = app_select_openpgp (app); - if (rc && (!name || !strcmp (name, "nks"))) + if (rc && is_app_allowed ("nks") && (!name || !strcmp (name, "nks"))) rc = app_select_nks (app); - if (rc && (!name || !strcmp (name, "dinsig"))) + if (rc && is_app_allowed ("dinsig") && (!name || !strcmp (name, "dinsig"))) rc = app_select_dinsig (app); if (rc && name) rc = gpg_error (GPG_ERR_NOT_SUPPORTED); diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c index 4751b8cdb..0fc168590 100644 --- a/scd/ccid-driver.c +++ b/scd/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; @@ -451,8 +457,9 @@ read_device_info (ccid_driver_t handle, struct usb_device *dev) 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; @@ -471,7 +478,7 @@ ccid_open_reader (ccid_driver_t *handle, int readerno) DEBUGOUT_1 ("usb_create_match failed: %d\n", rc); return CCID_DRIVER_ERR_NO_READER; } - + while (usb_find_device(match, dev, &dev) >= 0) { DEBUGOUT_3 ("%-40s %04X/%04X\n", dev->filename, @@ -530,7 +537,6 @@ ccid_open_reader (ccid_driver_t *handle, int readerno) readerno--; } - leave: if (idev) usb_close (idev); @@ -542,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. */ } @@ -894,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", @@ -1092,10 +1185,9 @@ ccid_transceive (ccid_driver_t handle, if (tpdulen < 4) { - DEBUGOUT ("cannot yet handle short blocks!\n"); - return CCID_DRIVER_ERR_NOT_SUPPORTED; + 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' : diff --git a/scd/ccid-driver.h b/scd/ccid-driver.h index 0b108f16d..0cb52e1c3 100644 --- a/scd/ccid-driver.h +++ b/scd/ccid-driver.h @@ -70,7 +70,7 @@ #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/scd/iso7816.c b/scd/iso7816.c index d5db607f8..cbb314eb2 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -90,6 +90,7 @@ map_sw (int sw) 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)) diff --git a/scd/scdaemon.c b/scd/scdaemon.c index f647aecc2..b54a63816 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -80,6 +80,7 @@ enum cmd_and_opt_values oDisableOpenSC, oAllowAdmin, oDenyAdmin, + oDisableApplication, aTest }; @@ -124,6 +125,7 @@ static ARGPARSE_OPTS opts[] = { /* end --disable-opensc */}, { oAllowAdmin, "allow-admin", 0, N_("allow the use of admin card commands")}, { oDenyAdmin, "deny-admin", 0, "@" }, + { oDisableApplication, "disable-application", 2, "@"}, {0} }; @@ -493,6 +495,10 @@ main (int argc, char **argv ) case oAllowAdmin: opt.allow_admin = 1; break; case oDenyAdmin: opt.allow_admin = 0; break; + case oDisableApplication: + add_to_strlist (&opt.disabled_applications, pargs.r.ret_str); + break; + default : pargs.err = configfp? 1:2; break; } } diff --git a/scd/scdaemon.h b/scd/scdaemon.h index 1dd32ae90..c8d78c88b 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -52,6 +52,8 @@ struct { int disable_ccid; /* Disable the use of the internal CCID driver. */ int allow_admin; /* Allow the use of admin commands for certain cards. */ + strlist_t disabled_applications; /* card applications we do not + want to use. */ } opt; diff --git a/sm/ChangeLog b/sm/ChangeLog index b9780cc90..b21a5faff 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,3 +1,8 @@ +2004-07-23 Werner Koch + + * certreqgen.c (proc_parameters): Do not allow key length below + 1024. + 2004-07-22 Werner Koch * keylist.c (list_cert_raw): Print the keygrip. diff --git a/sm/certreqgen.c b/sm/certreqgen.c index 969ed14b0..ab1f539de 100644 --- a/sm/certreqgen.c +++ b/sm/certreqgen.c @@ -444,7 +444,7 @@ proc_parameters (ctrl_t ctrl, if (i < 1 || i != GCRY_PK_RSA ) { r = get_parameter (para, pKEYTYPE); - log_error ("line %d: invalid algorithm\n", r->lnr); + log_error (_("line %d: invalid algorithm\n"), r->lnr); return gpg_error (GPG_ERR_INV_PARAMETER); } @@ -453,11 +453,12 @@ proc_parameters (ctrl_t ctrl, nbits = 1024; else nbits = get_parameter_uint (para, pKEYLENGTH); - if (nbits < 512 || nbits > 4096) + if (nbits < 1024 || nbits > 4096) { + /* The BSI specs dated 2002-11-25 don't allow lengths below 1024. */ r = get_parameter (para, pKEYTYPE); - log_error ("line %d: invalid key length %u (valid are 512 to 4096)\n", - r->lnr, nbits); + log_error (_("line %d: invalid key length %u (valid are %d to %d)\n"), + r->lnr, nbits, 1024, 4096); return gpg_error (GPG_ERR_INV_PARAMETER); } @@ -470,7 +471,7 @@ proc_parameters (ctrl_t ctrl, if (!(s=get_parameter_value (para, pNAMEDN))) { r = get_parameter (para, pKEYTYPE); - log_error ("line %d: no subject name given\n", r->lnr); + log_error (_("line %d: no subject name given\n"), r->lnr); return gpg_error (GPG_ERR_INV_PARAMETER); } /* fixme check s */ @@ -485,7 +486,7 @@ proc_parameters (ctrl_t ctrl, || strstr(s, "..")) { r = get_parameter (para, pKEYTYPE); - log_error ("line %d: not a valid email address\n", r->lnr); + log_error (_("line %d: not a valid email address\n"), r->lnr); return gpg_error (GPG_ERR_INV_PARAMETER); } } @@ -497,7 +498,7 @@ proc_parameters (ctrl_t ctrl, if (rc) { r = get_parameter (para, pKEYTYPE); - log_error ("line %d: key generation failed: %s\n", + log_error (_("line %d: key generation failed: %s\n"), r->lnr, gpg_strerror (rc)); return rc; } -- cgit From 69967b04125e53811182a01d2700984469117339 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 15 Dec 2004 14:15:54 +0000 Subject: A whole bunch of changes to allow building for W32. --- ChangeLog | 10 ++ Makefile.am | 8 +- acinclude.m4 | 22 ++-- agent/ChangeLog | 13 +++ agent/call-scd.c | 2 +- agent/gpg-agent.c | 16 +-- agent/protect-tool.c | 20 +++- common/ChangeLog | 15 +++ common/asshelp.c | 119 ++++++++++----------- common/exechelp.c | 276 +++++++++++++++++++++++++++++++++++++++++++++--- common/iobuf.c | 4 +- common/simple-pwquery.c | 6 +- common/sysutils.h | 7 ++ common/ttyname.c | 32 ++++++ common/util.h | 4 +- common/w32reg.c | 5 +- configure.ac | 30 +----- g10/call-agent.c | 2 + jnlib/ChangeLog | 4 + jnlib/logging.c | 7 +- m4/ksba.m4 | 2 +- scd/ChangeLog | 11 ++ scd/Makefile.am | 14 ++- scd/apdu.c | 7 +- scd/command.c | 5 + scd/scdaemon.c | 40 ++++++- sm/ChangeLog | 10 ++ sm/Makefile.am | 8 +- sm/gpgsm.c | 15 ++- tools/ChangeLog | 10 ++ tools/Makefile.am | 5 +- tools/gpgconf-comp.c | 18 ++++ 32 files changed, 589 insertions(+), 158 deletions(-) create mode 100644 common/ttyname.c (limited to 'scd/scdaemon.c') diff --git a/ChangeLog b/ChangeLog index d290c9481..95cf87d25 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2004-12-15 Werner Koch + + * Makefile.am (SUBDIRS) [W32]: Do not build in tests/. + + * acinclude.m4: Add proper macro name quoting for use with + automake 1.9. + + * configure.ac: Add replacement check for ttyname. + Removed support for a included zlib. + 2004-12-06 Werner Koch * configure.ac (have_w32_system): New. Disable Pth checks for W32. diff --git a/Makefile.am b/Makefile.am index 08d025abf..e6cbde893 100644 --- a/Makefile.am +++ b/Makefile.am @@ -53,8 +53,14 @@ else scd = endif +if HAVE_W32_SYSTEM +tests = +else +tests = tests +endif + SUBDIRS = m4 intl jnlib common ${kbx} \ - ${gpg} ${sm} ${agent} ${scd} tools po doc tests + ${gpg} ${sm} ${agent} ${scd} tools po doc ${tests} dist-hook: @set -e; \ diff --git a/acinclude.m4 b/acinclude.m4 index f6bbae78e..5f742b279 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -20,7 +20,7 @@ dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA dnl GNUPG_CHECK_TYPEDEF(TYPE, HAVE_NAME) dnl Check whether a typedef exists and create a #define $2 if it exists dnl -AC_DEFUN(GNUPG_CHECK_TYPEDEF, +AC_DEFUN([GNUPG_CHECK_TYPEDEF], [ AC_MSG_CHECKING(for $1 typedef) AC_CACHE_VAL(gnupg_cv_typedef_$1, [AC_TRY_COMPILE([#define _GNU_SOURCE 1 @@ -38,7 +38,7 @@ AC_DEFUN(GNUPG_CHECK_TYPEDEF, dnl GNUPG_CHECK_GNUMAKE dnl -AC_DEFUN(GNUPG_CHECK_GNUMAKE, +AC_DEFUN([GNUPG_CHECK_GNUMAKE], [ if ${MAKE-make} --version 2>/dev/null | grep '^GNU ' >/dev/null 2>&1; then : @@ -55,7 +55,7 @@ AC_DEFUN(GNUPG_CHECK_GNUMAKE, dnl GNUPG_CHECK_FAQPROG dnl -AC_DEFUN(GNUPG_CHECK_FAQPROG, +AC_DEFUN([GNUPG_CHECK_FAQPROG], [ AC_MSG_CHECKING(for faqprog.pl) if faqprog.pl -V 2>/dev/null | grep '^faqprog.pl ' >/dev/null 2>&1; then working_faqprog=yes @@ -82,7 +82,7 @@ dnl fi dnl GNUPG_CHECK_DOCBOOK_TO_TEXI dnl -AC_DEFUN(GNUPG_CHECK_DOCBOOK_TO_TEXI, +AC_DEFUN([GNUPG_CHECK_DOCBOOK_TO_TEXI], [ AC_CHECK_PROG(DOCBOOK_TO_TEXI, docbook2texi, yes, no) AC_MSG_CHECKING(for sgml to texi tools) @@ -101,7 +101,7 @@ AC_DEFUN(GNUPG_CHECK_DOCBOOK_TO_TEXI, dnl GNUPG_CHECK_ENDIAN dnl define either LITTLE_ENDIAN_HOST or BIG_ENDIAN_HOST dnl -define(GNUPG_CHECK_ENDIAN, +AC_DEFUN([GNUPG_CHECK_ENDIAN], [ tmp_assumed_endian=big if test "$cross_compiling" = yes; then @@ -158,7 +158,7 @@ define(GNUPG_CHECK_ENDIAN, # Check for the getsockopt SO_PEERCRED -AC_DEFUN(GNUPG_SYS_SO_PEERCRED, +AC_DEFUN([GNUPG_SYS_SO_PEERCRED], [ AC_MSG_CHECKING(for SO_PEERCRED) AC_CACHE_VAL(gnupg_cv_sys_so_peercred, [AC_TRY_COMPILE([#include ], @@ -183,7 +183,7 @@ AC_DEFUN(GNUPG_SYS_SO_PEERCRED, # either be "yes" or "no" and decided on the default value for # build_NAME and whether --enable-NAME or --disable-NAME is shown with # ./configure --help -AC_DEFUN(GNUPG_BUILD_PROGRAM, +AC_DEFUN([GNUPG_BUILD_PROGRAM], [build_$1=$2 m4_if([$2],[yes],[ AC_ARG_ENABLE([$1], AC_HELP_STRING([--disable-$1], @@ -210,7 +210,7 @@ AC_DEFUN(GNUPG_BUILD_PROGRAM, # If the version is sufficient, HAVE_PTH will be set to yes. # # Taken form the m4 macros which come with Pth -AC_DEFUN(GNUPG_PTH_VERSION_CHECK, +AC_DEFUN([GNUPG_PTH_VERSION_CHECK], [ _pth_version=`$PTH_CONFIG --version | awk 'NR==1 {print [$]3}'` _req_version="ifelse([$1],,1.2.0,$1)" @@ -253,7 +253,7 @@ AC_DEFUN(GNUPG_PTH_VERSION_CHECK, # mlock is there a macro using memlk() dnl GNUPG_CHECK_MLOCK dnl -define(GNUPG_CHECK_MLOCK, +AC_DEFUN([GNUPG_CHECK_MLOCK], [ AC_CHECK_FUNCS(mlock) if test "$ac_cv_func_mlock" = "no"; then AC_CHECK_HEADERS(sys/mman.h) @@ -343,7 +343,7 @@ define(GNUPG_CHECK_MLOCK, dnl Stolen from gcc dnl Define MKDIR_TAKES_ONE_ARG if mkdir accepts only one argument instead dnl of the usual 2. -AC_DEFUN(GNUPG_FUNC_MKDIR_TAKES_ONE_ARG, +AC_DEFUN([GNUPG_FUNC_MKDIR_TAKES_ONE_ARG], [AC_CHECK_HEADERS(sys/stat.h unistd.h direct.h) AC_CACHE_CHECK([if mkdir takes one argument], gnupg_cv_mkdir_takes_one_arg, [AC_TRY_COMPILE([ @@ -371,7 +371,7 @@ dnl AM_PATH_OPENSC([MINIMUM-VERSION, dnl [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]]) dnl Test for OpenSC and define OPENSC_CFLAGS and OPENSC_LIBS dnl -AC_DEFUN(AM_PATH_OPENSC, +AC_DEFUN([AM_PATH_OPENSC], [ AC_ARG_WITH(opensc-prefix, AC_HELP_STRING([--with-opensc-prefix=PFX], [prefix where OpenSC is installed (optional)]), diff --git a/agent/ChangeLog b/agent/ChangeLog index e0bf52b45..3669b0e43 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,16 @@ +2004-12-15 Werner Koch + + * gpg-agent.c [W32]: Various hacks to make it work. + + * findkey.c (agent_write_private_key) [W32]: Adjust open call. + + * call-scd.c (start_scd) [W32]: Don't check whether the daemon + didn't died. To hard to do under Windows. + (start_scd) [W32]: Disable sending of the event signal option. + + * protect-tool.c (read_file, export_p12_file) [W32]: Use setmode + to get stdout and stin into binary mode. + 2004-12-05 Moritz Schulte * query.c (start_pinentry): Allow CTRL be NULL. diff --git a/agent/call-scd.c b/agent/call-scd.c index 575986dc9..828040772 100644 --- a/agent/call-scd.c +++ b/agent/call-scd.c @@ -215,7 +215,7 @@ start_scd (ctrl_t ctrl) /* We better do a sanity check now to see whether it has accidently died. */ -#ifndef HAVE_W32_SYSTEM /* fixme */ +#ifndef HAVE_W32_SYSTEM pid = assuan_get_pid (scd_ctx); if (pid != (pid_t)(-1) && pid && ((rc=waitpid (pid, NULL, WNOHANG))==-1 || (rc == pid)) ) diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 0a483ac48..307d43d36 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -33,7 +33,7 @@ #ifndef HAVE_W32_SYSTEM #include #include -#endif +#endif /*HAVE_W32_SYSTEM*/ #include #include #ifdef USE_GNU_PTH @@ -438,17 +438,18 @@ main (int argc, char **argv ) /* Libgcrypt requires us to register the threading model first. Note that this will also do the pth_init. */ -#if defined(USE_GNU_PTH) && !defined(HAVE_W32_SYSTEM) +#ifdef USE_GNU_PTH +# ifdef HAVE_W32_SYSTEM + pth_init (); +# else /*!HAVE_W32_SYSTEM*/ err = gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth); if (err) { log_fatal ("can't register GNU Pth with Libgcrypt: %s\n", gpg_strerror (err)); } -#endif /*USE_GNU_PTH && !HAVE_W32_SYSTEM*/ -#ifdef HAVE_W32_SYSTEM - pth_init (); -#endif +# endif/*!HAVE_W32_SYSTEM*/ +#endif /*USE_GNU_PTH*/ /* Check that the libraries are suitable. Do it here because the option parsing may need services of the library. */ @@ -716,12 +717,11 @@ main (int argc, char **argv ) } /* Make sure that we have a default ttyname. */ -#ifndef HAVE_W32_SYSTEM if (!default_ttyname && ttyname (1)) default_ttyname = xstrdup (ttyname (1)); if (!default_ttytype && getenv ("TERM")) default_ttytype = xstrdup (getenv ("TERM")); -#endif + if (pipe_server) { /* this is the simple pipe based server */ diff --git a/agent/protect-tool.c b/agent/protect-tool.c index 286adde54..ef8a50916 100644 --- a/agent/protect-tool.c +++ b/agent/protect-tool.c @@ -35,6 +35,9 @@ #ifdef HAVE_LANGINFO_CODESET #include #endif +#ifdef HAVE_DOSISH_SYSTEM +#include /* for setmode() */ +#endif #define JNLIB_NEED_LOG_LOGV #include "agent.h" @@ -262,6 +265,9 @@ read_file (const char *fname, size_t *r_length) size_t nread, bufsize = 0; fp = stdin; +#ifdef HAVE_DOSISH_SYSTEM + setmode ( fileno(fp) , O_BINARY ); +#endif buf = NULL; buflen = 0; #define NCHUNK 8192 @@ -975,6 +981,9 @@ export_p12_file (const char *fname) if (!key) return; +#ifdef HAVE_DOSISH_SYSTEM + setmode ( fileno (stdout) , O_BINARY ); +#endif fwrite (key, keylen, 1, stdout); xfree (key); } @@ -1056,12 +1065,12 @@ main (int argc, char **argv ) gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); -#ifdef __MINGW32__ +#ifdef HAVE_W32_SYSTEM opt_homedir = read_w32_registry_string ( NULL, "Software\\GNU\\GnuPG", "HomeDir" ); -#else +#else /*!HAVE_W32_SYSTEM*/ opt_homedir = getenv ("GNUPGHOME"); -#endif +#endif /*!HAVE_W32_SYSTEM*/ if (!opt_homedir || !*opt_homedir) opt_homedir = GNUPG_DEFAULT_HOMEDIR; @@ -1213,9 +1222,10 @@ get_passphrase (int promptno) if (!pw) { if (err) - log_error ("error while asking for the passphrase\n"); + log_error (_("error while asking for the passphrase: %s\n"), + gpg_strerror (err)); else - log_info ("cancelled\n"); + log_info (_("cancelled\n")); agent_exit (0); } diff --git a/common/ChangeLog b/common/ChangeLog index afdded6d9..3115ad897 100644 --- a/common/ChangeLog +++ b/common/ChangeLog @@ -1,3 +1,18 @@ +2004-12-15 Werner Koch + + * sysutils.h [W32]: Prototypes for registry functions. + * w32reg.c: Include sysutils.h + + * simple-pwquery.c [W32]: Dummy code to allow a build. + + * exechelp.c [W32]: Implemented for W32 . + + * ttyname.c: New. + + * asshelp.c (send_one_option): New. + (send_pinentry_environment): Cleaned up and made sure that empty + values are not send. + 2004-12-07 Werner Koch * asshelp.c (send_pinentry_environment) [W32]: Do not use ttyname. diff --git a/common/asshelp.c b/common/asshelp.c index 751412e6c..243d6b9e7 100644 --- a/common/asshelp.c +++ b/common/asshelp.c @@ -32,8 +32,32 @@ #include "asshelp.h" -/* Send the assuan command pertaining to the pinenry environment. The - OPT_* arguments are optional and may be used to overide the + +static gpg_error_t +send_one_option (assuan_context_t ctx, const char *name, const char *value) +{ + gpg_error_t err; + char *optstr; + + if (!value || !*value) + err = 0; /* Avoid sending empty strings. */ + else if (asprintf (&optstr, "OPTION %s=%s", name, value ) < 0) + err = gpg_error_from_errno (errno); + else + { + assuan_error_t ae; + + ae = assuan_transact (ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); + err = ae? map_assuan_err (ae) : 0; + free (optstr); + } + + return err; +} + + +/* Send the assuan commands pertaining to the pinenry environment. The + OPT_* arguments are optional and may be used to override the defaults taken from the current locale. */ gpg_error_t send_pinentry_environment (assuan_context_t ctx, @@ -43,62 +67,49 @@ send_pinentry_environment (assuan_context_t ctx, const char *opt_lc_ctype, const char *opt_lc_messages) { - int rc = 0; + gpg_error_t err = 0; char *dft_display = NULL; char *dft_ttyname = NULL; char *dft_ttytype = NULL; char *old_lc = NULL; char *dft_lc = NULL; + /* Send the DISPLAY variable. */ dft_display = getenv ("DISPLAY"); if (opt_display || dft_display) { - char *optstr; - if (asprintf (&optstr, "OPTION display=%s", - opt_display ? opt_display : dft_display) < 0) - return gpg_error_from_errno (errno); - rc = assuan_transact (ctx, optstr, NULL, NULL, NULL, NULL, NULL, - NULL); - free (optstr); - if (rc) - return map_assuan_err (rc); + err = send_one_option (ctx, "display", + opt_display ? opt_display : dft_display); + if (err) + return err; } + + /* Send the name of the TTY. */ if (!opt_ttyname) { dft_ttyname = getenv ("GPG_TTY"); -#ifdef HAVE_DOSISH_SYSTEM - if (!dft_ttyname || !*dft_ttyname ) - dft_ttyname = "/dev/tty"; /* Use a fake. */ -#else if ((!dft_ttyname || !*dft_ttyname) && ttyname (0)) dft_ttyname = ttyname (0); -#endif } if (opt_ttyname || dft_ttyname) { - char *optstr; - if (asprintf (&optstr, "OPTION ttyname=%s", - opt_ttyname ? opt_ttyname : dft_ttyname) < 0) - return gpg_error_from_errno (errno); - rc = assuan_transact (ctx, optstr, NULL, NULL, NULL, NULL, NULL, - NULL); - free (optstr); - if (rc) - return map_assuan_err (rc); + err = send_one_option (ctx, "ttyname", + opt_ttyname ? opt_ttyname : dft_ttyname); + if (err) + return err; } + + /* Send the type of the TTY. */ dft_ttytype = getenv ("TERM"); if (opt_ttytype || (dft_ttyname && dft_ttytype)) { - char *optstr; - if (asprintf (&optstr, "OPTION ttytype=%s", - opt_ttyname ? opt_ttytype : dft_ttytype) < 0) - return gpg_error_from_errno (errno); - rc = assuan_transact (ctx, optstr, NULL, NULL, NULL, NULL, NULL, - NULL); - free (optstr); - if (rc) - return map_assuan_err (rc); + err = send_one_option (ctx, "ttytype", + opt_ttyname ? opt_ttytype : dft_ttytype); + if (err) + return err; } + + /* Send the value for LC_CTYPE. */ #if defined(HAVE_SETLOCALE) && defined(LC_CTYPE) old_lc = setlocale (LC_CTYPE, NULL); if (old_lc) @@ -111,18 +122,8 @@ send_pinentry_environment (assuan_context_t ctx, #endif if (opt_lc_ctype || (dft_ttyname && dft_lc)) { - char *optstr; - if (asprintf (&optstr, "OPTION lc-ctype=%s", - opt_lc_ctype ? opt_lc_ctype : dft_lc) < 0) - rc = gpg_error_from_errno (errno); - else - { - rc = assuan_transact (ctx, optstr, NULL, NULL, NULL, NULL, NULL, - NULL); - free (optstr); - if (rc) - rc = map_assuan_err (rc); - } + err = send_one_option (ctx, "lc-ctype", + opt_lc_ctype ? opt_lc_ctype : dft_lc); } #if defined(HAVE_SETLOCALE) && defined(LC_CTYPE) if (old_lc) @@ -131,8 +132,10 @@ send_pinentry_environment (assuan_context_t ctx, free (old_lc); } #endif - if (rc) - return rc; + if (err) + return err; + + /* Send the value for LC_MESSAGES. */ #if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES) old_lc = setlocale (LC_MESSAGES, NULL); if (old_lc) @@ -145,18 +148,8 @@ send_pinentry_environment (assuan_context_t ctx, #endif if (opt_lc_messages || (dft_ttyname && dft_lc)) { - char *optstr; - if (asprintf (&optstr, "OPTION lc-messages=%s", - opt_lc_messages ? opt_lc_messages : dft_lc) < 0) - rc = gpg_error_from_errno (errno); - else - { - rc = assuan_transact (ctx, optstr, NULL, NULL, NULL, NULL, NULL, - NULL); - free (optstr); - if (rc) - rc = map_assuan_err (rc); - } + err = send_one_option (ctx, "display", + opt_lc_messages ? opt_lc_messages : dft_lc); } #if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES) if (old_lc) @@ -165,7 +158,9 @@ send_pinentry_environment (assuan_context_t ctx, free (old_lc); } #endif + if (err) + return err; - return rc; + return 0; } diff --git a/common/exechelp.c b/common/exechelp.c index 206e16d51..0a9cb824f 100644 --- a/common/exechelp.c +++ b/common/exechelp.c @@ -30,8 +30,7 @@ #ifdef USE_GNU_PTH #include #endif -#ifdef _WIN32 -#else +#ifndef HAVE_W32_SYSTEM #include #endif @@ -39,6 +38,9 @@ #include "i18n.h" #include "exechelp.h" +/* Define to 1 do enable debugging. */ +#define DEBUG_W32_SPAWN 1 + #ifdef _POSIX_OPEN_MAX #define MAX_OPEN_FDS _POSIX_OPEN_MAX @@ -57,6 +59,105 @@ #endif +#ifdef HAVE_W32_SYSTEM +/* We assume that a HANDLE can be represented by an int which should + be true for all i386 systems (HANDLE is defined as void *) and + these are the only systems for which Windows is available. Further + we assume that -1 denotes an invalid handle. */ +# define fd_to_handle(a) ((HANDLE)(a)) +# define handle_to_fd(a) ((int)(a)) +# define pid_to_handle(a) ((HANDLE)(a)) +# define handle_to_pid(a) ((int)(a)) +#endif + + +#ifdef HAVE_W32_SYSTEM +/* Build a command line for use with W32's CreateProcess. On success + CMDLINE gets the address of a newly allocated string. */ +static gpg_error_t +build_w32_commandline (const char *pgmname, const char **argv, char **cmdline) +{ + int i, n; + const char *s; + char *buf, *p; + + *cmdline = NULL; + n = strlen (pgmname); + for (i=0; (s=argv[i]); i++) + { + n += strlen (s) + 1 + 2; /* (1 space, 2 quoting */ + for (; *s; s++) + if (*s == '\"') + n++; /* Need to double inner quotes. */ + } + n++; + + buf = p = xtrymalloc (n); + if (!buf) + return gpg_error_from_errno (errno); + + /* fixme: PGMNAME may not contain spaces etc. */ + p = stpcpy (p, pgmname); + for (i=0; argv[i]; i++) + { + if (!*argv[i]) /* Empty string. */ + p = stpcpy (p, " \"\""); + else if (strpbrk (argv[i], " \t\n\v\f\"")) + { + p = stpcpy (p, " \""); + for (s=argv[i]; *s; s++) + { + *p++ = *s; + if (*s == '\"') + *p++ = *s; + } + *p++ = '\"'; + *p = 0; + } + else + p = stpcpy (stpcpy (p, " "), argv[i]); + } + + *cmdline= buf; + return 0; +} +#endif /*HAVE_W32_SYSTEM*/ + + +#ifdef HAVE_W32_SYSTEM +/* Create pipe where the write end is inheritable. */ +static int +create_inheritable_pipe (int filedes[2]) +{ + HANDLE r, w, h; + SECURITY_ATTRIBUTES sec_attr; + + memset (&sec_attr, 0, sizeof sec_attr ); + sec_attr.nLength = sizeof sec_attr; + sec_attr.bInheritHandle = FALSE; + + if (!CreatePipe (&r, &w, &sec_attr, 0)) + return -1; + + if (!DuplicateHandle (GetCurrentProcess(), w, + GetCurrentProcess(), &h, 0, + TRUE, DUPLICATE_SAME_ACCESS )) + { + log_error ("DuplicateHandle failed: %s\n", w32_strerror (-1)); + CloseHandle (r); + CloseHandle (w); + return -1; + } + CloseHandle (w); + w = h; + + filedes[0] = handle_to_fd (r); + filedes[1] = handle_to_fd (w); + return 0; +} +#endif /*HAVE_W32_SYSTEM*/ + + /* Fork and exec the PGMNAME, connect the file descriptor of INFILE to stdin, write the output to OUTFILE, return a new stream in @@ -73,10 +174,121 @@ gnupg_spawn_process (const char *pgmname, const char *argv[], void (*preexec)(void), FILE **statusfile, pid_t *pid) { -#ifdef _WIN32 - return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +#ifdef HAVE_W32_SYSTEM + gpg_error_t err; + SECURITY_ATTRIBUTES sec_attr; + PROCESS_INFORMATION pi = + { + NULL, /* Returns process handle. */ + 0, /* Returns primary thread handle. */ + 0, /* Returns pid. */ + 0 /* Returns tid. */ + }; + STARTUPINFO si; + int cr_flags; + char *cmdline; + int fd, fdout, rp[2]; -#else /* !_WIN32 */ + /* Setup return values. */ + *statusfile = NULL; + *pid = (pid_t)(-1); + fflush (infile); + rewind (infile); + fd = _get_osfhandle (fileno (infile)); + fdout = _get_osfhandle (fileno (outfile)); + if (fd == -1 || fdout == -1) + log_fatal ("no file descriptor for file passed to gnupg_spawn_process\n"); + + /* Prepare security attributes. */ + memset (&sec_attr, 0, sizeof sec_attr ); + sec_attr.nLength = sizeof sec_attr; + sec_attr.bInheritHandle = FALSE; + + /* Build the command line. */ + err = build_w32_commandline (pgmname, argv, &cmdline); + if (err) + return err; + + /* Create a pipe. */ + if (create_inheritable_pipe (rp)) + { + err = gpg_error (GPG_ERR_GENERAL); + log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); + xfree (cmdline); + return err; + } + + /* Start the process. Note that we can't run the PREEXEC function + because this would change our own environment. */ + memset (&si, 0, sizeof si); + si.cb = sizeof (si); + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE; + si.hStdInput = fd_to_handle (fd); + si.hStdOutput = fd_to_handle (fdout); + si.hStdError = fd_to_handle (rp[1]); + + cr_flags = (CREATE_DEFAULT_ERROR_MODE + | GetPriorityClass (GetCurrentProcess ()) + | CREATE_SUSPENDED); + log_debug ("CreateProcess, path=`%s' cmdline=`%s'", pgmname, cmdline); + if (!CreateProcess (pgmname, /* Program to start. */ + cmdline, /* Command line arguments. */ + &sec_attr, /* Process security attributes. */ + &sec_attr, /* Thread security attributes. */ + TRUE, /* Inherit handles. */ + cr_flags, /* Creation flags. */ + NULL, /* Environment. */ + NULL, /* Use current drive/directory. */ + &si, /* Startup information. */ + &pi /* Returns process information. */ + )) + { + log_error ("CreateProcess failed: %s\n", w32_strerror (-1)); + xfree (cmdline); + CloseHandle (fd_to_handle (rp[0])); + CloseHandle (fd_to_handle (rp[1])); + return gpg_error (GPG_ERR_GENERAL); + } + xfree (cmdline); + cmdline = NULL; + + /* Close the other end of the pipe. */ + CloseHandle (fd_to_handle (rp[1])); + + log_debug ("CreateProcess ready: hProcess=%p hThread=%p" + " dwProcessID=%d dwThreadId=%d\n", + pi.hProcess, pi.hThread, + (int) pi.dwProcessId, (int) pi.dwThreadId); + + /* Process ha been created suspended; resume it now. */ + ResumeThread (pi.hThread); + CloseHandle (pi.hThread); + + { + int x; + + x = _open_osfhandle (rp[0], 0); + if (x == -1) + log_error ("failed to translate osfhandle %p\n", (void*)rp[0] ); + else + { + log_debug ("_open_osfhandle %p yields %d\n", (void*)fd, x ); + *statusfile = fdopen (x, "r"); + } + } + if (!*statusfile) + { + err = gpg_error_from_errno (errno); + log_error (_("can't fdopen pipe for reading: %s\n"), gpg_strerror (err)); + CloseHandle (pi.hProcess); + return err; + } + + *pid = handle_to_pid (pi.hProcess); + return 0; + +#else /* !HAVE_W32_SYSTEM */ gpg_error_t err; int fd, fdout, rp[2]; @@ -87,8 +299,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[], fd = fileno (infile); fdout = fileno (outfile); if (fd == -1 || fdout == -1) - log_fatal ("no file descriptor for file passed" - " to gnupg_spawn_process: %s\n", strerror (errno) ); + log_fatal ("no file descriptor for file passed to gnupg_spawn_process\n"); if (pipe (rp) == -1) { @@ -170,7 +381,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[], } return 0; -#endif /* !_WIN32 */ +#endif /* !HAVE_W32_SYSTEM */ } @@ -183,10 +394,51 @@ gnupg_wait_process (const char *pgmname, pid_t pid) { gpg_err_code_t ec; -#ifdef _WIN32 - ec = GPG_ERR_NOT_IMPLEMENTED; +#ifdef HAVE_W32_SYSTEM + HANDLE proc = fd_to_handle (pid); + int code; + DWORD exc; + + if (pid == (pid_t)(-1)) + return gpg_error (GPG_ERR_INV_VALUE); + + /* FIXME: We should do a pth_waitpid here. However this has not yet + been implemented. A special W32 pth system call would even be + better. */ + code = WaitForSingleObject (proc, INFINITE); + switch (code) + { + case WAIT_FAILED: + log_error (_("waiting for process %d to terminate failed: %s\n"), + (int)pid, w32_strerror (-1)); + ec = GPG_ERR_GENERAL; + break; + + case WAIT_OBJECT_0: + if (!GetExitCodeProcess (proc, &exc)) + { + log_error (_("error getting exit code of process %d: %s\n"), + (int)pid, w32_strerror (-1) ); + ec = GPG_ERR_GENERAL; + } + else if (exc) + { + log_error (_("error running `%s': exit status %d\n"), + pgmname, (int)exc ); + ec = GPG_ERR_GENERAL; + } + else + ec = 0; + break; + + default: + log_error ("WaitForSingleObject returned unexpected " + "code %d for pid %d\n", code, (int)pid ); + ec = GPG_ERR_GENERAL; + break; + } -#else /* !_WIN32 */ +#else /* !HAVE_W32_SYSTEM */ int i, status; if (pid == (pid_t)(-1)) @@ -222,7 +474,7 @@ gnupg_wait_process (const char *pgmname, pid_t pid) } else ec = 0; -#endif /* !_WIN32 */ +#endif /* !HAVE_W32_SYSTEM */ return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec); diff --git a/common/iobuf.c b/common/iobuf.c index 4d735397e..52a388514 100644 --- a/common/iobuf.c +++ b/common/iobuf.c @@ -1096,7 +1096,7 @@ iobuf_cancel (iobuf_t a) if (s && *s) { #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) - remove_name = m_strdup (s); + remove_name = xstrdup (s); #else remove (s); #endif @@ -1267,7 +1267,7 @@ iobuf_sockopen (int fd, const char *mode) size_t len; a = iobuf_alloc (strchr (mode, 'w') ? 2 : 1, 8192); - scx = m_alloc (sizeof *scx + 25); + scx = xmalloc (sizeof *scx + 25); scx->sock = fd; scx->print_only_name = 1; sprintf (scx->fname, "[sock %d]", fd); diff --git a/common/simple-pwquery.c b/common/simple-pwquery.c index 1e8eae63b..e7f992a88 100644 --- a/common/simple-pwquery.c +++ b/common/simple-pwquery.c @@ -31,7 +31,7 @@ #include #include #include -#ifdef _WIN32 +#ifdef HAVE_W32_SYSTEM #include #else #include @@ -182,10 +182,8 @@ agent_send_all_options (int fd) } dft_ttyname = getenv ("GPG_TTY"); -#ifndef HAVE_W32_SYSTEM if ((!dft_ttyname || !*dft_ttyname) && ttyname (0)) dft_ttyname = ttyname (0); -#endif if (dft_ttyname && *dft_ttyname) { if ((rc=agent_send_option (fd, "ttyname", dft_ttyname))) @@ -261,7 +259,7 @@ agent_send_all_options (int fd) static int agent_open (int *rfd) { -#ifdef _WIN32 +#ifdef HAVE_W32_SYSTEM return SPWQ_NO_AGENT; /* FIXME */ #else int rc; diff --git a/common/sysutils.h b/common/sysutils.h index 66f714acd..9df292031 100644 --- a/common/sysutils.h +++ b/common/sysutils.h @@ -27,5 +27,12 @@ int enable_core_dumps (void); const unsigned char *get_session_marker (size_t *rlen); int check_permissions (const char *path,int extension,int checkonly); +#ifdef HAVE_W32_SYSTEM +/*-- w32reg.c --*/ +char *read_w32_registry_string( const char *root, + const char *dir, const char *name ); +int write_w32_registry_string(const char *root, const char *dir, + const char *name, const char *value); +#endif /*HAVE_W32_SYSTEM*/ #endif /*GNUPG_COMMON_SYSUTILS_H*/ diff --git a/common/ttyname.c b/common/ttyname.c new file mode 100644 index 000000000..822beef99 --- /dev/null +++ b/common/ttyname.c @@ -0,0 +1,32 @@ +/* ttyname.c - Replacement for ttyname. + * Copyright (C) 2004 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 + */ + +/* This one is a simple dummy and suitable for Dosish systems. */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include + +char * +ttyname (int fd) +{ + return NULL; +} diff --git a/common/util.h b/common/util.h index fad7d38cc..0b9357d3a 100644 --- a/common/util.h +++ b/common/util.h @@ -147,7 +147,9 @@ int asprintf (char **result, const char *format, ...) JNLIB_GCC_A_PRINTF(2,3); #ifndef HAVE_STRSEP char *strsep (char **stringp, const char *delim); #endif - +#ifndef HAVE_TTYNAME +char *ttyname (int fd); +#endif /*-- some macros to replace ctype ones and avoid locale problems --*/ #define spacep(p) (*(p) == ' ' || *(p) == '\t') diff --git a/common/w32reg.c b/common/w32reg.c index 19fb613e7..a85ac7348 100644 --- a/common/w32reg.c +++ b/common/w32reg.c @@ -19,7 +19,7 @@ */ #include -#if defined (_WIN32) || defined (__CYGWIN32__) +#ifdef HAVE_W32_SYSTEM /* This module is only used in this environment */ #include @@ -29,6 +29,7 @@ #include #include "util.h" +#include "sysutils.h" static HKEY get_root_key(const char *root) @@ -169,4 +170,4 @@ write_w32_registry_string(const char *root, const char *dir, return 0; } -#endif /* __MINGW32__ || __CYGWIN32__ */ +#endif /*HAVE_W32_SYSTEM*/ diff --git a/configure.ac b/configure.ac index 481e52c52..facbfe416 100644 --- a/configure.ac +++ b/configure.ac @@ -218,12 +218,6 @@ if test "$use_exec" = yes ; then AC_MSG_RESULT($enableval) fi -AC_MSG_CHECKING([whether the included zlib is requested]) -AC_ARG_WITH(included-zlib, - [ --with-included-zlib use the zlib code included here], -[g10_force_zlib=yes], [g10_force_zlib=no] ) -AC_MSG_RESULT($g10_force_zlib) - dnl dnl Check whether we want to use Linux capabilities dnl @@ -799,6 +793,7 @@ AC_REPLACE_FUNCS(fseeko ftello) AC_REPLACE_FUNCS(isascii) AC_REPLACE_FUNCS(putc_unlocked) AC_REPLACE_FUNCS(strsep) +AC_REPLACE_FUNCS(ttyname) @@ -923,14 +918,10 @@ fi dnl Do we have zlib? Must do it here because Solaris failed dnl when compiling a conftest (due to the "-lz" from LIBS). -use_local_zlib=yes -if test "$g10_force_zlib" = "yes"; then - : -else - _cppflags="${CPPFLAGS}" - _ldflags="${LDFLAGS}" +_cppflags="${CPPFLAGS}" +_ldflags="${LDFLAGS}" - AC_ARG_WITH(zlib, +AC_ARG_WITH(zlib, [ --with-zlib=DIR use libz in DIR],[ if test -d "$withval"; then CPPFLAGS="${CPPFLAGS} -I$withval/include" @@ -938,23 +929,12 @@ else fi ]) - AC_CHECK_HEADER(zlib.h, +AC_CHECK_HEADER(zlib.h, AC_CHECK_LIB(z, deflateInit2_, - use_local_zlib=no LIBS="$LIBS -lz", CPPFLAGS=${_cppflags} LDFLAGS=${_ldflags}), CPPFLAGS=${_cppflags} LDFLAGS=${_ldflags}) -fi -if test "$use_local_zlib" = yes ; then - AM_CONDITIONAL(ENABLE_LOCAL_ZLIB, true) - AC_CONFIG_LINKS(zlib.h:zlib/zlib.h zconf.h:zlib/zconf.h ) - ZLIBS="../zlib/libzlib.a" -else - AM_CONDITIONAL(ENABLE_LOCAL_ZLIB, false) - ZLIBS= -fi -AC_SUBST(ZLIBS) # See wether we want to run the long test suite. diff --git a/g10/call-agent.c b/g10/call-agent.c index f93132fde..473b38236 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -171,6 +171,8 @@ start_agent (void) if (rc) return map_assuan_err (rc); +#warning put this code into common/asshelp.c + dft_display = getenv ("DISPLAY"); if (opt.display || dft_display) { diff --git a/jnlib/ChangeLog b/jnlib/ChangeLog index 2eaa7916f..71512715b 100644 --- a/jnlib/ChangeLog +++ b/jnlib/ChangeLog @@ -1,3 +1,7 @@ +2004-12-15 Werner Koch + + * logging.c [W32]: Don't include unavailable headers. + 2004-12-14 Werner Koch * w32-pth.c (_pth_strerror): Renamed to ... diff --git a/jnlib/logging.c b/jnlib/logging.c index 960d816eb..781f03e6d 100644 --- a/jnlib/logging.c +++ b/jnlib/logging.c @@ -35,16 +35,13 @@ #include #include #include -#ifndef _WIN32 +#ifndef HAVE_W32_SYSTEM #include #include -#endif +#endif /*!HAVE_W32_SYSTEM*/ #include #include #include -#ifdef __MINGW32__ -# include -#endif #define JNLIB_NEED_LOG_LOGV 1 diff --git a/m4/ksba.m4 b/m4/ksba.m4 index c59ac8024..99017c39e 100644 --- a/m4/ksba.m4 +++ b/m4/ksba.m4 @@ -14,7 +14,7 @@ dnl AM_PATH_KSBA([MINIMUM-VERSION, dnl [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]]) dnl Test for libksba and define KSBA_CFLAGS and KSBA_LIBS dnl -AC_DEFUN(AM_PATH_KSBA, +AC_DEFUN([AM_PATH_KSBA], [ AC_ARG_WITH(ksba-prefix, AC_HELP_STRING([--with-ksba-prefix=PFX], [prefix where KSBA is installed (optional)]), diff --git a/scd/ChangeLog b/scd/ChangeLog index 628055e80..fe3b3f6c4 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,14 @@ +2004-12-15 Werner Koch + + * scdaemon.c [W32]: Various hacks to make it run under W32. + + * command.c (scd_update_reader_status_file) [W32]: Don't use kill. + + * apdu.c [W32]: Disable use of pcsc_wrapper. + + * Makefile.am (scdaemon_LDADD): Reorder libs. + (sc_copykeys_LDADD): Add libassuan because it is needed for W32. + 2004-12-06 Werner Koch * Makefile.am (pkglib_PROGRAMS): Build only for W32. diff --git a/scd/Makefile.am b/scd/Makefile.am index 43bee4889..fba006c5a 100644 --- a/scd/Makefile.am +++ b/scd/Makefile.am @@ -19,7 +19,7 @@ ## Process this file with automake to produce Makefile.in bin_PROGRAMS = scdaemon sc-copykeys -if HAVE_W32_SYSTEM +if ! HAVE_W32_SYSTEM pkglib_PROGRAMS = pcsc-wrapper endif @@ -53,10 +53,8 @@ scdaemon_SOURCES = \ scdaemon_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \ - $(LIBGCRYPT_LIBS) $(pth_libs) \ - $(KSBA_LIBS) $(LIBASSUAN_LIBS) \ - $(LIBUSB_LIBS) $(OPENSC_LIBS) -lgpg-error @LIBINTL@ \ - @DL_LIBS@ + $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(pth_libs) $(LIBASSUAN_LIBS) \ + $(LIBUSB_LIBS) $(OPENSC_LIBS) -lgpg-error $(LIBINTL) $(DL_LIBS) sc_copykeys_SOURCES = \ sc-copykeys.c scdaemon.h \ @@ -70,10 +68,10 @@ sc_copykeys_SOURCES = \ sc_copykeys_LDADD = \ ../jnlib/libjnlib.a ../common/libcommon.a \ ../common/libsimple-pwquery.a \ - $(LIBGCRYPT_LIBS) $(pth_libs) \ - $(KSBA_LIBS) $(LIBUSB_LIBS) $(OPENSC_LIBS) \ + $(LIBGCRYPT_LIBS) $(pth_libs) $(KSBA_LIBS) $(LIBASSUAN_LIBS) \ + $(LIBUSB_LIBS) $(OPENSC_LIBS) \ -lgpg-error @LIBINTL@ @DL_LIBS@ pcsc_wrapper_SOURCES = pcsc-wrapper.c -pcsc_wrapper_LDADD = @DL_LIBS@ +pcsc_wrapper_LDADD = $(DL_LIBS) pcsc_wrapper_CFLAGS = diff --git a/scd/apdu.c b/scd/apdu.c index f4b32d141..9120616de 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -65,11 +65,16 @@ #include "dynload.h" #include "ccid-driver.h" + +/* To to conflicting use of threading libraries we usually can't link + against libpcsclite. Instead we use a wrapper program. */ #ifdef USE_GNU_PTH +#ifndef HAVE_W32_SYSTEM #define NEED_PCSC_WRAPPER 1 #endif +#endif - + #define MAX_READER 4 /* Number of readers we support concurrently. */ diff --git a/scd/command.c b/scd/command.c index 6fa100ff9..b99fffc09 100644 --- a/scd/command.c +++ b/scd/command.c @@ -1260,8 +1260,13 @@ scd_update_reader_status_file (void) int signo = primary_connection->server_local->event_signal; log_info ("client pid is %d, sending signal %d\n", pid, signo); + +#ifdef HAVE_W32_SYSTEM +#warning Need to implement a notification service +#else if (pid != (pid_t)(-1) && pid && signo > 0) kill (pid, signo); +#endif } } } diff --git a/scd/scdaemon.c b/scd/scdaemon.c index b54a63816..135f0973a 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -29,8 +29,10 @@ #include #include #include +#ifndef HAVE_W32_SYSTEM #include #include +#endif /*HAVE_W32_SYSTEM*/ #include #include #ifdef USE_GNU_PTH @@ -47,6 +49,9 @@ #include "i18n.h" #include "sysutils.h" #include "app-common.h" +#ifdef HAVE_W32_SYSTEM +#include "../jnlib/w32-afunix.h" +#endif enum cmd_and_opt_values @@ -131,7 +136,12 @@ static ARGPARSE_OPTS opts[] = { }; +/* The card dirver we use by default for PC/SC. */ +#ifdef HAVE_W32_SYSTEM +#define DEFAULT_PCSC_DRIVER "winscard.dll" +#else #define DEFAULT_PCSC_DRIVER "libpcsclite.so" +#endif static volatile int caught_fatal_sig = 0; @@ -148,8 +158,10 @@ static char socket_name[128]; #ifndef HAVE_OPENSC #ifdef USE_GNU_PTH +#ifndef HAVE_W32_SYSTEM /* Pth wrapper function definitions. */ GCRY_THREAD_OPTION_PTH_IMPL; +#endif static void *ticker_thread (void *arg); #endif /*USE_GNU_PTH*/ @@ -341,12 +353,16 @@ main (int argc, char **argv ) Note that this will also do the pth_init. */ #ifndef HAVE_OPENSC #ifdef USE_GNU_PTH +# ifdef HAVE_W32_SYSTEM + pth_init (); +# else /*!HAVE_W32_SYSTEM*/ err = gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth); if (err) { log_fatal ("can't register GNU Pth with Libgcrypt: %s\n", gpg_strerror (err)); } +# endif /*!HAVE_W32_SYSTEM*/ #endif /*USE_GNU_PTH*/ #endif /*!HAVE_OPENSC*/ @@ -649,12 +665,15 @@ main (int argc, char **argv ) if (!p) BUG (); *p = 0;; + +#ifndef HAVE_W32_SYSTEM if (!mkdtemp(socket_name)) { log_error ("can't create directory `%s': %s\n", socket_name, strerror(errno) ); exit (1); } +#endif *p = '/'; if (strchr (socket_name, ':') ) @@ -669,7 +688,11 @@ main (int argc, char **argv ) } +#ifdef HAVE_W32_SYSTEM + fd = _w32_sock_new (AF_UNIX, SOCK_STREAM, 0); +#else fd = socket (AF_UNIX, SOCK_STREAM, 0); +#endif if (fd == -1) { log_error ("can't create socket: %s\n", strerror(errno) ); @@ -682,7 +705,13 @@ main (int argc, char **argv ) len = (offsetof (struct sockaddr_un, sun_path) + strlen(serv_addr.sun_path) + 1); - if (bind (fd, (struct sockaddr*)&serv_addr, len) == -1) + if ( +#ifdef HAVE_W32_SYSTEM + _w32_sock_bind +#else + bind +#endif + (fd, (struct sockaddr*)&serv_addr, len) == -1) { log_error ("error binding socket to `%s': %s\n", serv_addr.sun_path, strerror (errno) ); @@ -702,6 +731,7 @@ main (int argc, char **argv ) fflush (NULL); +#ifndef HAVE_W32_SYSTEM pid = fork (); if (pid == (pid_t)-1) { @@ -800,6 +830,8 @@ main (int argc, char **argv ) exit (1); } +#endif /*!HAVE_W32_SYSTEM*/ + scd_command_handler (fd); close (fd); @@ -846,6 +878,7 @@ handle_signal (int signo) { switch (signo) { +#ifndef HAVE_W32_SYSTEM case SIGHUP: log_info ("SIGHUP received - " "re-reading configuration and resetting cards\n"); @@ -882,6 +915,7 @@ handle_signal (int signo) cleanup (); scd_exit (0); break; +#endif /*!HAVE_W32_SYSTEM*/ default: log_info ("signal %d received - no action defined\n", signo); @@ -901,6 +935,7 @@ ticker_thread (void *dummy_arg) sigset_t sigs; int signo; +#ifndef HAVE_W32_SYSTEM /* fixme */ sigemptyset (&sigs ); sigaddset (&sigs, SIGHUP); sigaddset (&sigs, SIGUSR1); @@ -908,6 +943,9 @@ ticker_thread (void *dummy_arg) sigaddset (&sigs, SIGINT); sigaddset (&sigs, SIGTERM); sigs_ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo); +#else + sigs_ev = NULL; +#endif for (;;) { diff --git a/sm/ChangeLog b/sm/ChangeLog index c1e445e4e..096c4ca8e 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,3 +1,13 @@ +2004-12-15 Werner Koch + + * misc.c (setup_pinentry_env) [W32]: Don't use it. + + * gpgsm.c (main) [W32]: Init Pth because we need it for the socket + operations and to resolve libassuan symbols. + (run_protect_tool) [W32]: Disable it. + + * Makefile.am (gpgsm_LDADD): Move LIBASSUAN_LIBS more to the end. + 2004-12-07 Werner Koch * Makefile.am (gpgsm_LDADD): Put libassuan before jnlib because diff --git a/sm/Makefile.am b/sm/Makefile.am index ff4524fc7..9136eb920 100644 --- a/sm/Makefile.am +++ b/sm/Makefile.am @@ -51,9 +51,9 @@ gpgsm_SOURCES = \ certreqgen.c -gpgsm_LDADD = $(LIBASSUAN_LIBS) ../jnlib/libjnlib.a ../kbx/libkeybox.a \ - ../common/libcommon.a \ - $(LIBGCRYPT_LIBS) $(KSBA_LIBS) -lgpg-error \ - $(LIBINTL) +gpgsm_LDADD = ../jnlib/libjnlib.a ../kbx/libkeybox.a \ + ../common/libcommon.a \ + $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) -lgpg-error \ + $(LIBINTL) diff --git a/sm/gpgsm.c b/sm/gpgsm.c index 0feca2608..f79375da7 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -26,6 +26,9 @@ #include #include #include +#ifdef USE_GNU_PTH +# include +#endif #include "gpgsm.h" #include @@ -736,6 +739,11 @@ main ( int argc, char **argv) NEED_KSBA_VERSION, ksba_check_version (NULL) ); } +#ifdef HAVE_W32_SYSTEM + pth_init (); +#endif + + gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); may_coredump = disable_core_dumps (); @@ -746,7 +754,8 @@ main ( int argc, char **argv) i18n_init(); opt.def_cipher_algoid = "1.2.840.113549.3.7"; /*des-EDE3-CBC*/ -#ifdef __MINGW32__ + +#ifdef HAVE_W32_SYSTEM opt.homedir = read_w32_registry_string ( NULL, "Software\\GNU\\GnuPG", "HomeDir" ); #else @@ -1688,7 +1697,7 @@ open_fwrite (const char *filename) static void run_protect_tool (int argc, char **argv) { -#ifndef _WIN32 +#ifndef HAVE_W32_SYSTEM const char *pgm; char **av; int i; @@ -1707,6 +1716,6 @@ run_protect_tool (int argc, char **argv) av[i] = NULL; execv (pgm, av); log_error ("error executing `%s': %s\n", pgm, strerror (errno)); -#endif +#endif /*HAVE_W32_SYSTEM*/ gpgsm_exit (2); } diff --git a/tools/ChangeLog b/tools/ChangeLog index cb341ad4f..34e985947 100644 --- a/tools/ChangeLog +++ b/tools/ChangeLog @@ -1,3 +1,13 @@ +2004-12-15 Werner Koch + + * Makefile.am (bin_PROGRAMS) [W32]: Do not build watchgnupg. + + * gpgconf-comp.c (gpg_agent_runtime_change) [W32]: No way yet to + send a signal. Disable. + (change_options_file, change_options_program) [W32]: No link(2), + so we disable it. + (gc_component_change_options): Use rename instead of link. + 2004-12-13 Werner Koch * gpgconf-comp.c : Fixed typo. diff --git a/tools/Makefile.am b/tools/Makefile.am index 2378df813..112c77e7c 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -32,7 +32,10 @@ sbin_SCRIPTS = addgnupghome bin_SCRIPTS = gpgsm-gencert.sh -bin_PROGRAMS = gpgconf watchgnupg +bin_PROGRAMS = gpgconf +if !HAVE_W32_SYSTEM +bin_PROGRAMS += watchgnupg +endif gpgconf_SOURCES = gpgconf.c gpgconf.h gpgconf-comp.c no-libgcrypt.c diff --git a/tools/gpgconf-comp.c b/tools/gpgconf-comp.c index 5d78df86d..fe696301c 100644 --- a/tools/gpgconf-comp.c +++ b/tools/gpgconf-comp.c @@ -861,6 +861,7 @@ static struct void gpg_agent_runtime_change (void) { +#ifndef HAVE_W32_SYSTEM char *agent = getenv ("GPG_AGENT_INFO"); char *pid_str; unsigned long pid_long; @@ -888,6 +889,7 @@ gpg_agent_runtime_change (void) /* Ignore any errors here. */ kill (pid, SIGHUP); +#endif /*!HAVE_W32_SYSTEM*/ } @@ -1741,7 +1743,12 @@ change_options_file (gc_component_t component, gc_backend_t backend, arg = NULL; } +#if HAVE_W32_SYSTEM + res = 0; +#warning no backups for W32 yet - need to write a copy function +#else res = link (dest_filename, orig_filename); +#endif if (res < 0 && errno != ENOENT) return -1; if (res < 0) @@ -2005,7 +2012,12 @@ change_options_program (gc_component_t component, gc_backend_t backend, src_filename = xasprintf ("%s.gpgconf.%i.new", dest_filename, getpid ()); orig_filename = xasprintf ("%s.gpgconf.%i.bak", dest_filename, getpid ()); +#if HAVE_W32_SYSTEM + res = 0; +#warning no backups for W32 yet - need to write a copy function +#else res = link (dest_filename, orig_filename); +#endif if (res < 0 && errno != ENOENT) return -1; if (res < 0) @@ -2418,12 +2430,18 @@ gc_component_change_options (int component, FILE *in) err = rename (src_pathname[i], dest_pathname[i]); else { +#ifdef HAVE_W32_SYSTEM + /* FIXME: Won't work becuase W32 doesn't silently + overwrite. */ + err = rename (src_pathname[i], dest_pathname[i]); +#else /*!HAVE_W32_SYSTEM*/ /* This is a bit safer than rename() because we expect DEST_PATHNAME not to be there. If it happens to be there, this will fail. */ err = link (src_pathname[i], dest_pathname[i]); if (!err) unlink (src_pathname[i]); +#endif /*!HAVE_W32_SYSTEM*/ } if (err) break; -- cgit From 694037f2a58df62e94b6f4da86ad2af5d2e19109 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Sat, 18 Dec 2004 18:53:32 +0000 Subject: * configure.ac (AH_BOTTOM): Define EXEEXT_S. * autogen.sh: Updated --build-w32 feature. * gpg-agent.c (main): Remove special Pth initialize. * w32-pth.c (pth_init): Reverse return values. Use TRUE and FALSE constants. (pth_kill, pth_mutex_acquire, pth_attr_set, pth_join, pth_cancel): Ditto. * scdaemon.c (main) [W32]: Remove special Pth initialize.. --- ChangeLog | 6 +++ agent/ChangeLog | 2 + agent/gpg-agent.c | 6 --- autogen.sh | 98 ++++++++++++++++++++++++------------------------ common/exechelp.c | 2 +- configure.ac | 12 +++--- jnlib/ChangeLog | 7 ++++ jnlib/w32-pth.c | 110 +++++++++++++++++++++++++++++++++++------------------- scd/ChangeLog | 2 + scd/scdaemon.c | 6 --- sm/gpgsm.c | 1 + 11 files changed, 147 insertions(+), 105 deletions(-) (limited to 'scd/scdaemon.c') diff --git a/ChangeLog b/ChangeLog index 95cf87d25..3db690f2d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2004-12-18 Werner Koch + + * configure.ac (AH_BOTTOM): Define EXEEXT_S. + + * autogen.sh: Updated --build-w32 feature. + 2004-12-15 Werner Koch * Makefile.am (SUBDIRS) [W32]: Do not build in tests/. diff --git a/agent/ChangeLog b/agent/ChangeLog index b22241bc9..52673e5b3 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,5 +1,7 @@ 2004-12-18 Werner Koch + * gpg-agent.c (main): Remove special Pth initialize. + * agent.h (map_assuan_err): Define in terms of map_assuan_err_with_source. diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 307d43d36..dfa457282 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -179,9 +179,7 @@ static void create_directories (void); static void handle_connections (int listen_fd); /* Pth wrapper function definitions. */ -#ifndef HAVE_W32_SYSTEM GCRY_THREAD_OPTION_PTH_IMPL; -#endif #endif /*USE_GNU_PTH*/ static void check_for_running_agent (void); @@ -439,16 +437,12 @@ main (int argc, char **argv ) /* Libgcrypt requires us to register the threading model first. Note that this will also do the pth_init. */ #ifdef USE_GNU_PTH -# ifdef HAVE_W32_SYSTEM - pth_init (); -# else /*!HAVE_W32_SYSTEM*/ err = gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth); if (err) { log_fatal ("can't register GNU Pth with Libgcrypt: %s\n", gpg_strerror (err)); } -# endif/*!HAVE_W32_SYSTEM*/ #endif /*USE_GNU_PTH*/ /* Check that the libraries are suitable. Do it here because diff --git a/autogen.sh b/autogen.sh index a607d63da..87ddc6369 100755 --- a/autogen.sh +++ b/autogen.sh @@ -40,64 +40,66 @@ MSGMERGE=${GETTEXT_PREFIX}${MSGMERGE:-msgmerge}${GETTEXT_SUFFIX} DIE=no -if [ "$1" = "--build-w32" ]; then +# ***** W32 build script ******* +# Used to cross-compile for Windows. +if test "$1" = "--build-w32"; then + tmp=`dirname $0` + tsdir=`cd "$tmp"; pwd` shift - target=i386--mingw32 - if [ ! -f ./config.guess ]; then - echo "./config.guess not found" >&2 + if [ ! -f $tsdir/scripts/config.guess ]; then + echo "$tsdir/scripts/config.guess not found" >&2 exit 1 fi - host=`./config.guess` - - if ! mingw32 --version >/dev/null; then - echo "We need at least version 0.3 of MingW32/CPD" >&2 - exit 1 + build=`$tsdir/scripts/config.guess` + + [ -z "$w32root" ] && w32root="$HOME/w32root" + echo "Using $w32root as standard install directory" >&2 + + # See whether we have the Debian cross compiler package or the + # old mingw32/cpd system + if i586-mingw32msvc-gcc --version >/dev/null 2>&1 ; then + host=i586-mingw32msvc + crossbindir=/usr/$host/bin + else + host=i386--mingw32 + if ! mingw32 --version >/dev/null; then + echo "We need at least version 0.3 of MingW32/CPD" >&2 + exit 1 + fi + crossbindir=`mingw32 --install-dir`/bin + # Old autoconf version required us to setup the environment + # with the proper tool names. + CC=`mingw32 --get-path gcc` + CPP=`mingw32 --get-path cpp` + AR=`mingw32 --get-path ar` + RANLIB=`mingw32 --get-path ranlib` + export CC CPP AR RANLIB fi - - if [ -f config.h ]; then - if grep HAVE_DOSISH_SYSTEM config.h | grep undef >/dev/null; then + + if [ -f "$tsdir/config.log" ]; then + if ! head $tsdir/config.log | grep "$host" >/dev/null; then echo "Pease run a 'make distclean' first" >&2 exit 1 fi fi - crossinstalldir=`mingw32 --install-dir` - crossbindir=`mingw32 --get-bindir 2>/dev/null` \ - || crossbindir="$crossinstalldir/bin" - crossdatadir=`mingw32 --get-datadir 2>/dev/null` \ - || crossdatadir="$crossinstalldir/share" - crosslibdir=`mingw32 --get-libdir 2>/dev/null` \ - || crosslibdir="$crossinstalldir/i386--mingw32/lib" - crossincdir=`mingw32 --get-includedir 2>/dev/null` \ - || crossincdir="$crossinstalldir/i386--mingw32/include" - CC=`mingw32 --get-path gcc` - CPP=`mingw32 --get-path cpp` - AR=`mingw32 --get-path ar` - RANLIB=`mingw32 --get-path ranlib` - export CC CPP AR RANLIB - - disable_foo_tests="" - if [ -n "$lib_config_files" ]; then - for i in $lib_config_files; do - j=`echo $i | tr '[a-z-]' '[A-Z_]'` - eval "$j=${crossbindir}/$i" - export $j - disable_foo_tests="$disable_foo_tests --disable-`echo $i| \ - sed 's,-config$,,'`-test" - if [ ! -f "${crossbindir}/$i" ]; then - echo "$i not installed for MingW32" >&2 - DIE=yes - fi - done - fi - [ $DIE = yes ] && exit 1 - - ./configure --host=${host} --target=${target} ${disable_foo_tests} \ - --bindir=${crossbindir} --libdir=${crosslibdir} \ - --datadir=${crossdatadir} --includedir=${crossincdir} \ - --enable-maintainer-mode $* - exit $? + ./configure --enable-maintainer-mode --prefix=${w32root} \ + --host=i586-mingw32msvc --build=${build} \ + --with-gpg-error-prefix=${w32root} \ + --with-ksba-prefix=${w32root} \ + --with-libgcrypt-prefix=${w32root} \ + --with-libassuan-prefix=${w32root} \ + --with-zlib=${w32root} \ + --with-pth-prefix=${w32root} + rc=$? + # Ugly hack to overcome a gettext problem. Someone should look into + # gettext to figure out why the po directory is not ignored as it used + # to be. + [ $rc = 0 ] && touch $tsdir/po/all + exit $rc fi +# ***** end W32 build script ******* + diff --git a/common/exechelp.c b/common/exechelp.c index 0a9cb824f..dc0a6b0e1 100644 --- a/common/exechelp.c +++ b/common/exechelp.c @@ -231,7 +231,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[], cr_flags = (CREATE_DEFAULT_ERROR_MODE | GetPriorityClass (GetCurrentProcess ()) | CREATE_SUSPENDED); - log_debug ("CreateProcess, path=`%s' cmdline=`%s'", pgmname, cmdline); + log_debug ("CreateProcess, path=`%s' cmdline=`%s'\n", pgmname, cmdline); if (!CreateProcess (pgmname, /* Program to start. */ cmdline, /* Command line arguments. */ &sec_attr, /* Process security attributes. */ diff --git a/configure.ac b/configure.ac index facbfe416..463358f28 100644 --- a/configure.ac +++ b/configure.ac @@ -255,11 +255,13 @@ AH_BOTTOM([ #define EXTSEP_C '.' #define DIRSEP_S "\\" #define EXTSEP_S "." +#define EXEEXT_S ".exe" #else #define DIRSEP_C '/' #define EXTSEP_C '.' #define DIRSEP_S "/" #define EXTSEP_S "." +#define EXEEXT_S "" #endif /* This is the same as VERSION, but should be overridden if the @@ -283,20 +285,20 @@ AH_BOTTOM([ /* Setup the hardwired names of modules. */ #ifndef GNUPG_DEFAULT_AGENT -#define GNUPG_DEFAULT_AGENT ( GNUPG_BINDIR DIRSEP_S "gpg-agent" ) +#define GNUPG_DEFAULT_AGENT ( GNUPG_BINDIR DIRSEP_S "gpg-agent" EXEEXT_S ) #endif #ifndef GNUPG_DEFAULT_PINENTRY -#define GNUPG_DEFAULT_PINENTRY ( GNUPG_BINDIR DIRSEP_S "pinentry" ) +#define GNUPG_DEFAULT_PINENTRY ( GNUPG_BINDIR DIRSEP_S "pinentry" EXEEXT_S ) #endif #ifndef GNUPG_DEFAULT_SCDAEMON -#define GNUPG_DEFAULT_SCDAEMON ( GNUPG_BINDIR DIRSEP_S "scdaemon" ) +#define GNUPG_DEFAULT_SCDAEMON ( GNUPG_BINDIR DIRSEP_S "scdaemon" EXEEXT_S ) #endif #ifndef GNUPG_DEFAULT_DIRMNGR -#define GNUPG_DEFAULT_DIRMNGR ( GNUPG_BINDIR DIRSEP_S "dirmngr" ) +#define GNUPG_DEFAULT_DIRMNGR ( GNUPG_BINDIR DIRSEP_S "dirmngr" EXEEXT_S ) #endif #ifndef GNUPG_DEFAULT_PROTECT_TOOL #define GNUPG_DEFAULT_PROTECT_TOOL \ - ( GNUPG_LIBEXECDIR DIRSEP_S "gpg-protect-tool" ) + ( GNUPG_LIBEXECDIR DIRSEP_S "gpg-protect-tool" EXEEXT_S ) #endif diff --git a/jnlib/ChangeLog b/jnlib/ChangeLog index 71512715b..4a9665c45 100644 --- a/jnlib/ChangeLog +++ b/jnlib/ChangeLog @@ -1,3 +1,10 @@ +2004-12-18 Werner Koch + + * w32-pth.c (pth_init): Reverse return values. Use TRUE and FALSE + constants. + (pth_kill, pth_mutex_acquire, pth_attr_set, pth_join, pth_cancel): + Ditto. + 2004-12-15 Werner Koch * logging.c [W32]: Don't include unavailable headers. diff --git a/jnlib/w32-pth.c b/jnlib/w32-pth.c index b47e8c9be..7710a622f 100644 --- a/jnlib/w32-pth.c +++ b/jnlib/w32-pth.c @@ -33,11 +33,24 @@ #include #include +#include "logging.h" /* For log_get_prefix () */ + /* We don't want to have any Windows specific code in the header, thus we use a macro which defaults to a compatible type in w32-pth.h. */ #define W32_PTH_HANDLE_INTERNAL HANDLE #include "w32-pth.h" +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif +#if FALSE != 0 || TRUE != 1 +#error TRUE or FALSE defined to wrong values +#endif + + static int pth_initialized = 0; @@ -107,10 +120,12 @@ pth_init (void) SECURITY_ATTRIBUTES sa; WSADATA wsadat; - fprintf (stderr, "pth_init: called.\n"); - pth_initialized = 1; + if (pth_initialized) + return TRUE; + + fprintf (stderr, "%s: pth_init: called.\n", log_get_prefix (NULL)); if (WSAStartup (0x202, &wsadat)) - abort (); + return FALSE; pth_signo = 0; InitializeCriticalSection (&pth_shd); if (pth_signo_ev) @@ -121,9 +136,11 @@ pth_init (void) sa.nLength = sizeof sa; pth_signo_ev = CreateEvent (&sa, TRUE, FALSE, NULL); if (!pth_signo_ev) - abort (); + return FALSE; + + pth_initialized = 1; EnterCriticalSection (&pth_shd); - return 0; + return TRUE; } @@ -140,7 +157,7 @@ pth_kill (void) DeleteCriticalSection (&pth_shd); WSACleanup (); pth_initialized = 0; - return 0; + return TRUE; } @@ -160,7 +177,8 @@ enter_pth (const char *function) { /* Fixme: I am not sure whether the same thread my enter a critical section twice. */ - fprintf (stderr, "enter_pth (%s)\n", function? function:""); +/* fprintf (stderr, "%s: enter_pth (%s)\n", + log_get_prefix (NULL), function? function:""); */ LeaveCriticalSection (&pth_shd); } @@ -169,7 +187,8 @@ static void leave_pth (const char *function) { EnterCriticalSection (&pth_shd); - fprintf (stderr, "leave_pth (%s)\n", function? function:""); +/* fprintf (stderr, "%s: leave_pth (%s)\n", + log_get_prefix (NULL), function? function:""); */ } @@ -234,7 +253,8 @@ pth_read (int fd, void * buffer, size_t size) { char strerr[256]; - fprintf (stderr, "pth_read(%d) failed read from file: %s\n", fd, + fprintf (stderr, "%s: pth_read(%d) failed read from file: %s\n", + log_get_prefix (NULL), fd, w32_strerror (strerr, sizeof strerr)); n = -1; } @@ -273,7 +293,8 @@ pth_write (int fd, const void * buffer, size_t size) n = WriteFile ((HANDLE)fd, buffer, size, &nwrite, NULL); if (!n) { - fprintf (stderr, "pth_write(%d) failed in write: %s\n", fd, + fprintf (stderr, "%s: pth_write(%d) failed in write: %s\n", + log_get_prefix (NULL), fd, w32_strerror (strerr, sizeof strerr)); n = -1; } @@ -437,7 +458,7 @@ pth_mutex_acquire (pth_mutex_t *hd, int tryonly, pth_event_t ev_extra) if (!hd || !hd->mx) { leave_pth (__FUNCTION__); - return -1; + return FALSE; } #if 0 @@ -488,7 +509,7 @@ pth_mutex_acquire (pth_mutex_t *hd, int tryonly, pth_event_t ev_extra) hd->mx_state |= PTH_MUTEX_LOCKED; leave_pth (__FUNCTION__); - return 0; + return TRUE; } @@ -513,7 +534,7 @@ pth_mutex_init (pth_mutex_t *hd) hd->mx_state = PTH_MUTEX_INITIALIZED; leave_pth (__FUNCTION__); - return 0; + return TRUE; } @@ -537,7 +558,7 @@ pth_attr_destroy (pth_attr_t hd) if (hd->name) free (hd->name); free (hd); - return 0; + return TRUE; } @@ -547,7 +568,7 @@ pth_attr_set (pth_attr_t hd, int field, ...) va_list args; char * str; int val; - int rc = 0; + int rc = TRUE; implicit_init (); @@ -559,7 +580,8 @@ pth_attr_set (pth_attr_t hd, int field, ...) if (val) { hd->flags |= PTH_ATTR_JOINABLE; - fprintf (stderr, "pth_attr_set: PTH_ATTR_JOINABLE\n"); + fprintf (stderr, "%s: pth_attr_set: PTH_ATTR_JOINABLE\n", + log_get_prefix (NULL)); } break; @@ -569,7 +591,8 @@ pth_attr_set (pth_attr_t hd, int field, ...) { hd->flags |= PTH_ATTR_STACK_SIZE; hd->stack_size = val; - fprintf (stderr, "pth_attr_set: PTH_ATTR_STACK_SIZE %d\n", val); + fprintf (stderr, "%s: pth_attr_set: PTH_ATTR_STACK_SIZE %d\n", + log_get_prefix (NULL), val); } break; @@ -581,14 +604,15 @@ pth_attr_set (pth_attr_t hd, int field, ...) { hd->name = strdup (str); if (!hd->name) - return -1; + return FALSE; hd->flags |= PTH_ATTR_NAME; - fprintf (stderr, "pth_attr_set: PTH_ATTR_NAME %s\n", hd->name); + fprintf (stderr, "%s: pth_attr_set: PTH_ATTR_NAME %s\n", + log_get_prefix (NULL), hd->name); } break; default: - rc = -1; + rc = FALSE; break; } va_end (args); @@ -627,12 +651,14 @@ do_pth_spawn (pth_attr_t hd, void *(*func)(void *), void *arg) FIXME: We should no use th W32's Thread handle directly but keep our own thread control structure. CTX may be used for that. */ - fprintf (stderr, "do_pth_spawn creating thread ...\n"); + fprintf (stderr, "%s: do_pth_spawn creating thread ...\n", + log_get_prefix (NULL)); th = CreateThread (&sa, hd->stack_size, (LPTHREAD_START_ROUTINE)launch_thread, ctx, CREATE_SUSPENDED, &tid); ctx->th = th; - fprintf (stderr, "do_pth_spawn created thread %p\n", th); + fprintf (stderr, "%s: do_pth_spawn created thread %p\n", + log_get_prefix (NULL),th); if (!th) free (ctx); else @@ -660,7 +686,7 @@ pth_spawn (pth_attr_t hd, void *(*func)(void *), void *arg) int pth_join (pth_t hd, void **value) { - return 0; + return TRUE; } @@ -675,7 +701,7 @@ pth_cancel (pth_t hd) WaitForSingleObject (hd, 1000); TerminateThread (hd, 0); leave_pth (__FUNCTION__); - return 0; + return TRUE; } @@ -689,7 +715,7 @@ pth_abort (pth_t hd) enter_pth (__FUNCTION__); TerminateThread (hd, 0); leave_pth (__FUNCTION__); - return 0; + return TRUE; } @@ -759,7 +785,7 @@ sig_handler (DWORD signo) case CTRL_BREAK_EVENT: pth_signo = SIGTERM; break; } SetEvent (pth_signo_ev); - fprintf (stderr, "sig_handler=%d\n", pth_signo); + fprintf (stderr, "%s: sig_handler=%d\n", log_get_prefix (NULL), pth_signo); return TRUE; } @@ -771,7 +797,7 @@ do_pth_event_body (unsigned long spec, va_list arg) pth_event_t ev; int rc; - fprintf (stderr, "pth_event spec=%lu\n", spec); + fprintf (stderr, "%s: pth_event spec=%lu\n", log_get_prefix (NULL), spec); ev = calloc (1, sizeof *ev); if (!ev) return NULL; @@ -783,7 +809,8 @@ do_pth_event_body (unsigned long spec, va_list arg) ev->u_type = PTH_EVENT_SIGS; ev->val = va_arg (arg, int *); rc = SetConsoleCtrlHandler (sig_handler, TRUE); - fprintf (stderr, "pth_event: sigs rc=%d\n", rc); + fprintf (stderr, "%s: pth_event: sigs rc=%d\n", + log_get_prefix (NULL), rc); } else if (spec & PTH_EVENT_FD) { @@ -794,7 +821,8 @@ do_pth_event_body (unsigned long spec, va_list arg) ev->u_type = PTH_EVENT_FD; va_arg (arg, pth_key_t); ev->u.fd = va_arg (arg, int); - fprintf (stderr, "pth_event: fd=%d\n", ev->u.fd); + fprintf (stderr, "%s: pth_event: fd=%d\n", + log_get_prefix (NULL), ev->u.fd); } else if (spec & PTH_EVENT_TIME) { @@ -908,7 +936,8 @@ wait_for_fd (int fd, int is_read, int nwait) while (1) { n = select (fd+1, &r, &w, NULL, &tv); - fprintf (stderr, "wait_for_fd=%d fd %d (ec=%d)\n", n, fd,(int)WSAGetLastError ()); + fprintf (stderr, "%s: wait_for_fd=%d fd %d (ec=%d)\n", + log_get_prefix (NULL), n, fd,(int)WSAGetLastError ()); if (n == -1) break; if (!n) @@ -1005,7 +1034,8 @@ do_pth_event_occurred (pth_event_t ev) if (sigpresent (ev->u.sig, pth_signo) && WaitForSingleObject (pth_signo_ev, 0) == WAIT_OBJECT_0) { - fprintf (stderr, "pth_event_occurred: sig signaled.\n"); + fprintf (stderr, "%s: pth_event_occurred: sig signaled.\n", + log_get_prefix (NULL)); (*ev->val) = pth_signo; ret = 1; } @@ -1080,7 +1110,7 @@ do_pth_event_free (pth_event_t ev, int mode) free (ev); } - return 0; + return TRUE; } int @@ -1134,11 +1164,13 @@ spawn_helper_thread (void *(*func)(void *), void *arg) sa.lpSecurityDescriptor = NULL; sa.nLength = sizeof sa; - fprintf (stderr, "spawn_helper_thread creating thread ...\n"); + fprintf (stderr, "%s: spawn_helper_thread creating thread ...\n", + log_get_prefix (NULL)); th = CreateThread (&sa, 32*1024, (LPTHREAD_START_ROUTINE)func, arg, 0, &tid); - fprintf (stderr, "spawn_helper_thread created thread %p\n", th); + fprintf (stderr, "%s: spawn_helper_thread created thread %p\n", + log_get_prefix (NULL), th); return th; } @@ -1163,7 +1195,7 @@ wait_fd_thread (void * ctx) pth_event_t ev = ctx; wait_for_fd (ev->u.fd, ev->flags & PTH_UNTIL_FD_READABLE, 3600); - fprintf (stderr, "wait_fd_thread: exit.\n"); + fprintf (stderr, "%s: wait_fd_thread: exit.\n", log_get_prefix (NULL)); SetEvent (ev->hd); ExitThread (0); return NULL; @@ -1177,7 +1209,7 @@ wait_timer_thread (void * ctx) int n = ev->u.tv.tv_sec*1000; Sleep (n); SetEvent (ev->hd); - fprintf (stderr, "wait_timer_thread: exit.\n"); + fprintf (stderr, "%s: wait_timer_thread: exit.\n", log_get_prefix (NULL)); ExitThread (0); return NULL; } @@ -1199,7 +1231,7 @@ do_pth_wait (pth_event_t ev) if (n > MAXIMUM_WAIT_OBJECTS/2) return -1; - fprintf (stderr, "pth_wait: cnt %lu\n", n); + fprintf (stderr, "%s: pth_wait: cnt %lu\n", log_get_prefix (NULL), n); for (tmp = ev; tmp; tmp = tmp->next) { switch (tmp->u_type) @@ -1233,10 +1265,10 @@ do_pth_wait (pth_event_t ev) break; } } - fprintf (stderr, "pth_wait: set %d\n", pos); + fprintf (stderr, "%s: pth_wait: set %d\n", log_get_prefix (NULL), pos); n = WaitForMultipleObjects (pos, waitbuf, FALSE, INFINITE); free_helper_threads (waitbuf, hdidx, i); - fprintf (stderr, "pth_wait: n %ld\n", n); + fprintf (stderr, "%s: pth_wait: n %ld\n", log_get_prefix (NULL), n); if (n != WAIT_TIMEOUT) return 1; diff --git a/scd/ChangeLog b/scd/ChangeLog index 87328f650..68421fc86 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,5 +1,7 @@ 2004-12-18 Werner Koch + * scdaemon.c (main) [W32]: Remove special Pth initialize.. + * scdaemon.h (map_assuan_err): Define in terms of map_assuan_err_with_source. diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 135f0973a..901644edc 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -158,10 +158,8 @@ static char socket_name[128]; #ifndef HAVE_OPENSC #ifdef USE_GNU_PTH -#ifndef HAVE_W32_SYSTEM /* Pth wrapper function definitions. */ GCRY_THREAD_OPTION_PTH_IMPL; -#endif static void *ticker_thread (void *arg); #endif /*USE_GNU_PTH*/ @@ -353,16 +351,12 @@ main (int argc, char **argv ) Note that this will also do the pth_init. */ #ifndef HAVE_OPENSC #ifdef USE_GNU_PTH -# ifdef HAVE_W32_SYSTEM - pth_init (); -# else /*!HAVE_W32_SYSTEM*/ err = gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth); if (err) { log_fatal ("can't register GNU Pth with Libgcrypt: %s\n", gpg_strerror (err)); } -# endif /*!HAVE_W32_SYSTEM*/ #endif /*USE_GNU_PTH*/ #endif /*!HAVE_OPENSC*/ diff --git a/sm/gpgsm.c b/sm/gpgsm.c index f79375da7..709263cba 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -740,6 +740,7 @@ main ( int argc, char **argv) } #ifdef HAVE_W32_SYSTEM + /* For W32 we need pth. */ pth_init (); #endif -- cgit From 8f22f895e94d45755c99470eb76fd9fd508b5b5f Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Sun, 19 Dec 2004 17:44:20 +0000 Subject: * query.c (initialize_module_query): * call-scd.c (initialize_module_call_scd): New. * w32-pth.c (pth_init): Enable debugging depending on env var. (pth_self): New. (pth_mutex_release, pth_mutex_acquire): Implemented directly using the W32 API. --- agent/ChangeLog | 6 +++ agent/agent.h | 2 + agent/call-scd.c | 21 ++++++++- agent/gpg-agent.c | 4 ++ agent/query.c | 21 ++++++++- jnlib/ChangeLog | 3 ++ jnlib/w32-pth.c | 131 ++++++++++++++++++++++++------------------------------ jnlib/w32-pth.h | 12 +++-- scd/scdaemon.c | 5 +++ 9 files changed, 123 insertions(+), 82 deletions(-) (limited to 'scd/scdaemon.c') diff --git a/agent/ChangeLog b/agent/ChangeLog index 52673e5b3..d3c4af337 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,9 @@ +2004-12-19 Werner Koch + + * query.c (initialize_module_query): + + * call-scd.c (initialize_module_call_scd): New. + 2004-12-18 Werner Koch * gpg-agent.c (main): Remove special Pth initialize. diff --git a/agent/agent.h b/agent/agent.h index 7ff692504..e6c8e2a35 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -145,6 +145,7 @@ gpg_error_t agent_key_from_file (ctrl_t ctrl, int agent_key_available (const unsigned char *grip); /*-- query.c --*/ +void initialize_module_query (void); int agent_askpin (ctrl_t ctrl, const char *desc_text, const char *inital_errtext, struct pin_entry_info_s *pininfo); @@ -212,6 +213,7 @@ int divert_generic_cmd (ctrl_t ctrl, /*-- call-scd.c --*/ +void initialize_module_call_scd (void); int agent_reset_scd (ctrl_t ctrl); int agent_card_learn (ctrl_t ctrl, void (*kpinfo_cb)(void*, const char *), diff --git a/agent/call-scd.c b/agent/call-scd.c index 828040772..90c625f8b 100644 --- a/agent/call-scd.c +++ b/agent/call-scd.c @@ -52,7 +52,7 @@ static ASSUAN_CONTEXT scd_ctx = NULL; #ifdef USE_GNU_PTH -static pth_mutex_t scd_lock = PTH_MUTEX_INIT; +static pth_mutex_t scd_lock; #endif /* We need to keep track of the connection currently using the SCD. For a pipe server this is all a NOP because the connection will @@ -153,6 +153,23 @@ get_membuf (struct membuf *mb, size_t *len) +/* This function must be called once to initialize this module. This + has to be done before a second thread is spawned. We can't do the + static initialization because Pth emulation code might not be able + to do a static init; in particualr, it is not possible for W32. */ +void +initialize_module_call_scd (void) +{ +#ifdef USE_GNU_PTH + static int initialized; + + if (!initialized) + if (pth_mutex_init (&scd_lock)) + initialized = 1; +#endif /*USE_GNU_PTH*/ +} + + static int unlock_scd (int rc) { @@ -163,7 +180,7 @@ unlock_scd (int rc) if (!rc) rc = gpg_error (GPG_ERR_INTERNAL); } -#endif +#endif /*USE_GNU_PTH*/ return rc; } diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index ed12fed4f..65d5fd5a6 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -627,6 +627,10 @@ main (int argc, char **argv ) exit (1); } + initialize_module_query (); + initialize_module_call_scd (); + + /* Try to create missing directories. */ create_directories (); if (debug_wait && pipe_server) diff --git a/agent/query.c b/agent/query.c index b514f6f5e..4aafcbe99 100644 --- a/agent/query.c +++ b/agent/query.c @@ -51,7 +51,7 @@ static ASSUAN_CONTEXT entry_ctx = NULL; #ifdef USE_GNU_PTH -static pth_mutex_t entry_lock = PTH_MUTEX_INIT; +static pth_mutex_t entry_lock; #endif /* data to be passed to our callbacks */ @@ -64,6 +64,25 @@ struct entry_parm_s { +/* This function must be called once to initialize this module. This + has to be done before a second thread is spawned. We can't do the + static initialization because Pth emulation code might not be able + to do a static init; in particualr, it is not possible for W32. */ +void +initialize_module_query (void) +{ +#ifdef USE_GNU_PTH + static int initialized; + + if (!initialized) + if (pth_mutex_init (&entry_lock)) + initialized = 1; +#endif /*USE_GNU_PTH*/ +} + + + + /* Unlock the pinentry so that another thread can start one and disconnect that pinentry - we do this after the unlock so that a stalled pinentry does not block other threads. Fixme: We should diff --git a/jnlib/ChangeLog b/jnlib/ChangeLog index e194cea4c..5a566aedb 100644 --- a/jnlib/ChangeLog +++ b/jnlib/ChangeLog @@ -1,6 +1,9 @@ 2004-12-19 Werner Koch * w32-pth.c (pth_init): Enable debugging depending on env var. + (pth_self): New. + (pth_mutex_release, pth_mutex_acquire): Implemented directly using + the W32 API. 2004-12-18 Werner Koch diff --git a/jnlib/w32-pth.c b/jnlib/w32-pth.c index 489b28300..609a03705 100644 --- a/jnlib/w32-pth.c +++ b/jnlib/w32-pth.c @@ -68,7 +68,6 @@ static HANDLE pth_signo_ev; static CRITICAL_SECTION pth_shd; - struct pth_event_s { struct pth_event_s * next; @@ -306,8 +305,6 @@ pth_write (int fd, const void * buffer, size_t size) fprintf (stderr, "%s: pth_write(%d) failed in write: %s\n", log_get_prefix (NULL), fd, w32_strerror (strerr, sizeof strerr)); - fprintf (stderr, "--> fd = %d, handle = %p, size = %lu\n", - fd, (HANDLE)fd, size); n = -1; } else @@ -444,107 +441,94 @@ pth_connect (int fd, struct sockaddr *name, int namelen) int -pth_mutex_release (pth_mutex_t *hd) +pth_mutex_release (pth_mutex_t *mutex) { - if (!hd) - return -1; + int rc; + implicit_init (); enter_pth (__FUNCTION__); - if (hd->mx) + + if (!ReleaseMutex (*mutex)) { - CloseHandle (hd->mx); - hd->mx = NULL; + char strerr[256]; + + fprintf (stderr, "%s: pth_release_mutex %p failed: %s\n", + log_get_prefix (NULL), *mutex, + w32_strerror (strerr, sizeof strerr)); + rc = FALSE; } - free (hd); + else + rc = TRUE; + leave_pth (__FUNCTION__); - return 0; + return rc; } int -pth_mutex_acquire (pth_mutex_t *hd, int tryonly, pth_event_t ev_extra) +pth_mutex_acquire (pth_mutex_t *mutex, int tryonly, pth_event_t ev_extra) { + int code; + int rc; + implicit_init (); enter_pth (__FUNCTION__); - if (!hd || !hd->mx) - { - leave_pth (__FUNCTION__); - return FALSE; - } - -#if 0 - /* still not locked, so simply acquire mutex? */ - if (!(mutex->mx_state & PTH_MUTEX_LOCKED)) - { - mutex->mx_state |= PTH_MUTEX_LOCKED; - mutex->mx_count = 1; - pth_ring_append(&(pth_current->mutexring), &(mutex->mx_node)); - pth_debug1("pth_mutex_acquire: immediately locking mutex"); - return 0; - } - - /* already locked by caller? */ - if (mutex->mx_count >= 1 && mutex->mx_owner == pth_current) - { - /* recursive lock */ - mutex->mx_count++; - pth_debug1("pth_mutex_acquire: recursive locking"); - return 0; - } - - if (tryonly) - { - leave_pth (__FUNCTION__); - return -1; - } - - for (;;) + /* FIXME: ev_extra is not yet supported. */ + + code = WaitForSingleObject (*mutex, INFINITE); + switch (code) { - ev = pth_event(PTH_EVENT_MUTEX|PTH_MODE_STATIC, &ev_key, mutex); - if (ev_extra != NULL) - pth_event_concat (ev, ev_extra, NULL); - pth_wait (ev); - if (ev_extra != NULL) + case WAIT_FAILED: { - pth_event_isolate (ev); - if (do_pth_event_status(ev) == PTH_STATUS_PENDING) - { - leave_pth (__FUNCTION__); - return -1; - } + char strerr[256]; + + fprintf (stderr, "%s: pth_mutex_acquire for %p failed: %s\n", + log_get_prefix (NULL), *mutex, + w32_strerror (strerr, sizeof strerr)); } - if (!(mutex->mx_state & PTH_MUTEX_LOCKED)) + rc = FALSE; + break; + + case WAIT_OBJECT_0: + rc = TRUE; + break; + + default: + fprintf (stderr, "%s: WaitForSingleObject returned unexpected " + "code %d for mutex %p\n", + log_get_prefix (NULL), code, *mutex); + rc = FALSE; break; } -#endif - hd->mx_state |= PTH_MUTEX_LOCKED; leave_pth (__FUNCTION__); - return TRUE; + return rc; } + int -pth_mutex_init (pth_mutex_t *hd) +pth_mutex_init (pth_mutex_t *mutex) { SECURITY_ATTRIBUTES sa; implicit_init (); enter_pth (__FUNCTION__); - if (hd->mx) - { - ReleaseMutex (hd->mx); - CloseHandle (hd->mx); - } memset (&sa, 0, sizeof sa); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL; sa.nLength = sizeof sa; - hd->mx = CreateMutex (&sa, FALSE, NULL); - hd->mx_state = PTH_MUTEX_INITIALIZED; - + *mutex = CreateMutex (&sa, FALSE, NULL); + if (!*mutex) + { + free (*mutex); + *mutex = NULL; + leave_pth (__FUNCTION__); + return FALSE; + } + leave_pth (__FUNCTION__); return TRUE; } @@ -695,6 +679,12 @@ pth_spawn (pth_attr_t hd, void *(*func)(void *), void *arg) } +pth_t +pth_self (void) +{ + return GetCurrentThread (); +} + int pth_join (pth_t hd, void **value) { @@ -1271,9 +1261,6 @@ do_pth_wait (pth_event_t ev) case PTH_EVENT_MUTEX: fprintf (stderr, "pth_wait: add mutex event.\n"); - hdidx[i++] = pos; - waitbuf[pos++] = tmp->u.mx->mx; - /* XXX: Use SetEvent(hd->ev) */ break; } } diff --git a/jnlib/w32-pth.h b/jnlib/w32-pth.h index bcd82659c..5ef0ab240 100644 --- a/jnlib/w32-pth.h +++ b/jnlib/w32-pth.h @@ -53,7 +53,9 @@ enum /* Mutex values. */ #define PTH_MUTEX_INITIALIZED (1<<0) #define PTH_MUTEX_LOCKED (1<<1) -#define PTH_MUTEX_INIT {PTH_MUTEX_INITIALIZED} + +/* Note: We can't do static initialization, thus we don't define the + initializer PTH_MUTEX_INIT. */ #define PTH_KEY_INIT (1<<0) @@ -158,12 +160,7 @@ typedef void *pth_t; /* The Mutex object. */ -struct pth_mutex_s -{ - unsigned mx_state; - W32_PTH_HANDLE_INTERNAL mx; -}; -typedef struct pth_mutex_s pth_mutex_t; +typedef W32_PTH_HANDLE_INTERNAL pth_mutex_t; /* The Event object. */ @@ -214,6 +211,7 @@ int pth_attr_destroy (pth_attr_t hd); int pth_attr_set (pth_attr_t hd, int field, ...); pth_t pth_spawn (pth_attr_t hd, void *(*func)(void *), void *arg); +pth_t pth_self (void); int pth_join (pth_t hd, void **value); int pth_abort (pth_t hd); void pth_exit (void *value); diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 901644edc..49e392e7d 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -351,12 +351,17 @@ main (int argc, char **argv ) Note that this will also do the pth_init. */ #ifndef HAVE_OPENSC #ifdef USE_GNU_PTH +#ifdef HAVE_W32_SYSTEM + /* For W32 we need pth. */ + pth_init (); +#else err = gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth); if (err) { log_fatal ("can't register GNU Pth with Libgcrypt: %s\n", gpg_strerror (err)); } +#endif #endif /*USE_GNU_PTH*/ #endif /*!HAVE_OPENSC*/ -- cgit From 878cf2076633742ad5f4e4008059b0fc8d776d37 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 21 Dec 2004 10:03:00 +0000 Subject: * gpg-agent.c (main): Use default_homedir(). * protect-tool.c (main): Ditto. * signal.c (got_fatal_signal, got_usr_signal) (got_fatal_signal) [DOSISH]: Don't build. * simple-gettext.c: Include sysutils.h * homedir.c: New. * Makefile.am (libcommon_a_SOURCES): Add it. (EXTRA_DIST): Removed mkerror and mkerrtok. * gpgv.c, g10.c (main): Use default_hoemdir (). * scdaemon.c (main): Use default_homedir(). * gpgsm.c (main): Use default_homedir(). --- TODO | 2 ++ agent/ChangeLog | 6 ++++++ agent/gpg-agent.c | 9 +-------- agent/protect-tool.c | 11 ++--------- common/ChangeLog | 10 ++++++++++ common/Makefile.am | 3 +-- common/homedir.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ common/signal.c | 14 ++++++++++---- common/simple-gettext.c | 2 +- common/util.h | 4 ++++ g10/ChangeLog | 4 ++++ g10/g10.c | 11 ++--------- g10/gpgv.c | 11 ++--------- scd/ChangeLog | 4 ++++ scd/command.c | 4 +--- scd/scdaemon.c | 16 +++------------- sm/ChangeLog | 4 ++++ sm/gpgsm.c | 11 ++--------- 18 files changed, 103 insertions(+), 67 deletions(-) create mode 100644 common/homedir.c (limited to 'scd/scdaemon.c') diff --git a/TODO b/TODO index c7eca304e..16311a204 100644 --- a/TODO +++ b/TODO @@ -101,4 +101,6 @@ might want to have an agent context for each service request Fix is to change everything to libestream ** Signals are not support This means we can't reread a configuration +** No card status notifications. + diff --git a/agent/ChangeLog b/agent/ChangeLog index 507a90c9d..cf3569264 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,9 @@ +2004-12-21 Werner Koch + + * gpg-agent.c (main): Use default_homedir(). + * protect-tool.c (main): Ditto. + + 2004-12-20 Werner Koch * gpg-agent.c (main) [W32]: Now that Mutexes work we can remove diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 911afb881..2c3d834a5 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -478,15 +478,8 @@ main (int argc, char **argv ) if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") ) csh_style = 1; + opt.homedir = default_homedir (); - opt.homedir = getenv("GNUPGHOME"); -#ifdef HAVE_W32_SYSTEM - if (!opt.homedir || !*opt.homedir) - opt.homedir = read_w32_registry_string (NULL, - "Software\\GNU\\GnuPG", "HomeDir"); -#endif /*HAVE_W32_SYSTEM*/ - if (!opt.homedir || !*opt.homedir) - opt.homedir = GNUPG_DEFAULT_HOMEDIR; /* Check whether we have a config file on the commandline */ orig_argc = argc; diff --git a/agent/protect-tool.c b/agent/protect-tool.c index 43dd67eea..ee0276a43 100644 --- a/agent/protect-tool.c +++ b/agent/protect-tool.c @@ -84,7 +84,7 @@ struct rsa_secret_key_s }; -static char *opt_homedir; +static const char *opt_homedir; static int opt_armor; static int opt_store; static int opt_force; @@ -1067,14 +1067,7 @@ main (int argc, char **argv ) gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); - opt_homedir = getenv ("GNUPGHOME"); -#ifdef HAVE_W32_SYSTEM - if (!opt_homedir || !*opt_homedir) - opt_homedir = read_w32_registry_string (NULL, - "Software\\GNU\\GnuPG", "HomeDir"); -#endif /*HAVE_W32_SYSTEM*/ - if (!opt_homedir || !*opt_homedir) - opt_homedir = GNUPG_DEFAULT_HOMEDIR; + opt_homedir = default_homedir (); pargs.argc = &argc; diff --git a/common/ChangeLog b/common/ChangeLog index 11bf8029a..667deb51a 100644 --- a/common/ChangeLog +++ b/common/ChangeLog @@ -1,3 +1,13 @@ +2004-12-21 Werner Koch + + * signal.c (got_fatal_signal, got_usr_signal) + (got_fatal_signal) [DOSISH]: Don't build. + * simple-gettext.c: Include sysutils.h + + * homedir.c: New. + * Makefile.am (libcommon_a_SOURCES): Add it. + (EXTRA_DIST): Removed mkerror and mkerrtok. + 2004-12-20 Werner Koch * sysutils.h [W32]: Define sleep. diff --git a/common/Makefile.am b/common/Makefile.am index 795d66a1a..ccbaaad75 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -19,8 +19,6 @@ ## Process this file with automake to produce Makefile.in -EXTRA_DIST = mkerrors mkerrtok - noinst_LIBRARIES = libcommon.a libsimple-pwquery.a AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) @@ -31,6 +29,7 @@ libcommon_a_SOURCES = \ sexp-parse.h \ maperror.c \ sysutils.c sysutils.h \ + homedir.c \ gettime.c \ yesno.c \ b64enc.c \ diff --git a/common/homedir.c b/common/homedir.c new file mode 100644 index 000000000..8b5bc9f05 --- /dev/null +++ b/common/homedir.c @@ -0,0 +1,44 @@ +/* homedir.c - Setup the home directory. + * Copyright (C) 2004 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 "util.h" +#include "sysutils.h" + +/* Set up the default home directory. The usual --homedir option + should be parsed later. */ +const char * +default_homedir (void) +{ + const char *dir; + + dir = getenv("GNUPGHOME"); +#ifdef HAVE_W32_SYSTEM + if (!dir || !*dir) + dir = read_w32_registry_string (NULL, "Software\\GNU\\GnuPG", "HomeDir"); +#endif /*HAVE_W32_SYSTEM*/ + if (!dir || !*dir) + dir = GNUPG_DEFAULT_HOMEDIR; + + return dir; +} diff --git a/common/signal.c b/common/signal.c index dc026c10f..4e773c4c0 100644 --- a/common/signal.c +++ b/common/signal.c @@ -30,15 +30,17 @@ #include "util.h" +#ifndef HAVE_DOSISH_SYSTEM static volatile int caught_fatal_sig; static volatile int caught_sigusr1; +#endif static void (*cleanup_fnc)(void); +#ifndef HAVE_DOSISH_SYSTEM static void init_one_signal (int sig, RETSIGTYPE (*handler)(int), int check_ign ) { -#ifndef HAVE_DOSISH_SYSTEM # ifdef HAVE_SIGACTION struct sigaction oact, nact; @@ -64,9 +66,10 @@ init_one_signal (int sig, RETSIGTYPE (*handler)(int), int check_ign ) signal (sig, SIG_IGN); } # endif -#endif /*!HAVE_DOSISH_SYSTEM*/ } +#endif /*!HAVE_DOSISH_SYSTEM*/ +#ifndef HAVE_DOSISH_SYSTEM static const char * get_signal_name( int signum ) { @@ -76,7 +79,9 @@ get_signal_name( int signum ) return "some signal"; #endif } +#endif /*!HAVE_DOSISH_SYSTEM*/ +#ifndef HAVE_DOSISH_SYSTEM static RETSIGTYPE got_fatal_signal (int sig) { @@ -106,14 +111,15 @@ got_fatal_signal (int sig) #endif /* __riscos__ */ raise( sig ); } +#endif /*!HAVE_DOSISH_SYSTEM*/ - +#ifndef HAVE_DOSISH_SYSTEM static RETSIGTYPE got_usr_signal (int sig) { caught_sigusr1 = 1; } - +#endif /*!HAVE_DOSISH_SYSTEM*/ void gnupg_init_signals (int mode, void (*fast_cleanup)(void)) diff --git a/common/simple-gettext.c b/common/simple-gettext.c index 4287606e3..b6b851c77 100644 --- a/common/simple-gettext.c +++ b/common/simple-gettext.c @@ -40,7 +40,7 @@ #include #include "util.h" - +#include "sysutils.h" /* The magic number of the GNU message catalog format. */ #define MAGIC 0x950412de diff --git a/common/util.h b/common/util.h index 1159da925..4ab55acb4 100644 --- a/common/util.h +++ b/common/util.h @@ -121,6 +121,10 @@ gpg_error_t b64enc_write (struct b64state *state, gpg_error_t b64enc_finish (struct b64state *state); +/*-- homedir. c --*/ +const char *default_homedir (void); + + /*-- miscellaneous.c --*/ /* Same as asprintf but return an allocated buffer suitable to be diff --git a/g10/ChangeLog b/g10/ChangeLog index 6966bbd7b..bd4b54894 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,7 @@ +2004-12-21 Werner Koch + + * gpgv.c, g10.c (main): Use default_hoemdir (). + 2004-12-18 Werner Koch * gpg.h (map_assuan_err): Define in terms of diff --git a/g10/g10.c b/g10/g10.c index 039074f6b..e02ad0d13 100644 --- a/g10/g10.c +++ b/g10/g10.c @@ -1264,16 +1264,9 @@ main( int argc, char **argv ) opt.mangle_dos_filenames = 1; opt.use_agent = 1; -#if defined (_WIN32) - set_homedir ( read_w32_registry_string( NULL, - "Software\\GNU\\GnuPG", "HomeDir" )); -#else - set_homedir ( getenv("GNUPGHOME") ); -#endif - if( !*opt.homedir ) - set_homedir ( GNUPG_DEFAULT_HOMEDIR ); + set_homedir ( default_homedir () ); - /* check whether we have a config file on the commandline */ + /* Check whether we have a config file on the commandline */ orig_argc = argc; orig_argv = argv; pargs.argc = &argc; diff --git a/g10/gpgv.c b/g10/gpgv.c index 015736c33..0a97d56b9 100644 --- a/g10/gpgv.c +++ b/g10/gpgv.c @@ -150,15 +150,8 @@ main( int argc, char **argv ) opt.trust_model = TM_ALWAYS; opt.batch = 1; -#if defined (_WIN32) - opt.homedir = read_w32_registry_string( NULL, "Software\\GNU\\GnuPG", - "HomeDir" ); -#else - opt.homedir = getenv("GNUPGHOME"); -#endif - if( !opt.homedir || !*opt.homedir ) { - opt.homedir = GNUPG_DEFAULT_HOMEDIR; - } + opt.homedir = default_homedir (); + tty_no_terminal(1); tty_batchmode(1); disable_dotlock(); diff --git a/scd/ChangeLog b/scd/ChangeLog index 68421fc86..ece9abc4f 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,7 @@ +2004-12-21 Werner Koch + + * scdaemon.c (main): Use default_homedir(). + 2004-12-18 Werner Koch * scdaemon.c (main) [W32]: Remove special Pth initialize.. diff --git a/scd/command.c b/scd/command.c index b99fffc09..b41e7aa16 100644 --- a/scd/command.c +++ b/scd/command.c @@ -1261,9 +1261,7 @@ scd_update_reader_status_file (void) log_info ("client pid is %d, sending signal %d\n", pid, signo); -#ifdef HAVE_W32_SYSTEM -#warning Need to implement a notification service -#else +#ifndef HAVE_W32_SYSTEM if (pid != (pid_t)(-1) && pid && signo > 0) kill (pid, signo); #endif diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 49e392e7d..88f393bb1 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -351,17 +351,12 @@ main (int argc, char **argv ) Note that this will also do the pth_init. */ #ifndef HAVE_OPENSC #ifdef USE_GNU_PTH -#ifdef HAVE_W32_SYSTEM - /* For W32 we need pth. */ - pth_init (); -#else err = gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth); if (err) { log_fatal ("can't register GNU Pth with Libgcrypt: %s\n", gpg_strerror (err)); } -#endif #endif /*USE_GNU_PTH*/ #endif /*!HAVE_OPENSC*/ @@ -392,14 +387,9 @@ main (int argc, char **argv ) if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") ) csh_style = 1; - /* FIXME: Using this homedir option does only make sense when not - running as a system service. We might want to check for this by - looking at the uid or ebtter use an explict option for this */ - opt.homedir = getenv("GNUPGHOME"); - if (!opt.homedir || !*opt.homedir) - opt.homedir = GNUPG_DEFAULT_HOMEDIR; - - /* check whether we have a config file on the commandline */ + opt.homedir = default_homedir (); + + /* Check whether we have a config file on the commandline */ orig_argc = argc; orig_argv = argv; pargs.argc = &argc; diff --git a/sm/ChangeLog b/sm/ChangeLog index 19e7b0e8d..0dcaa9c20 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,3 +1,7 @@ +2004-12-21 Werner Koch + + * gpgsm.c (main): Use default_homedir(). + 2004-12-20 Werner Koch * call-agent.c (start_agent): Before starting a pipe server start diff --git a/sm/gpgsm.c b/sm/gpgsm.c index 709263cba..935d50474 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -756,16 +756,9 @@ main ( int argc, char **argv) opt.def_cipher_algoid = "1.2.840.113549.3.7"; /*des-EDE3-CBC*/ -#ifdef HAVE_W32_SYSTEM - opt.homedir = read_w32_registry_string ( NULL, - "Software\\GNU\\GnuPG", "HomeDir" ); -#else - opt.homedir = getenv ("GNUPGHOME"); -#endif - if (!opt.homedir || !*opt.homedir ) - opt.homedir = GNUPG_DEFAULT_HOMEDIR; + opt.homedir = default_homedir (); - /* first check whether we have a config file on the commandline */ + /* First check whether we have a config file on the commandline */ orig_argc = argc; orig_argv = argv; pargs.argc = &argc; -- cgit From 3af261572bdf938f0a2fdde4d9aec82153a7e0e4 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 24 Feb 2005 17:36:11 +0000 Subject: * gpg-agent.c (handle_connections): Need to check for events if select returns with -1. * tools.texi (gpg-connect-agent): New. * app-openpgp.c (get_one_do): Never try to get a non cacheable object from the cache. (get_one_do): Add new arg to return an error code. Changed all callers. (do_getattr): Let it return a proper error code. * app.c (select_application): Return an error code and the application context in an new arg. * command.c (open_card): Adjusted for that. Don't use the fallback if no card is present. Return an error if the card has been removed without a reset. (do_reset, cmd_serialno): Clear that error flag. (TEST_CARD_REMOVAL): New. Use it with all command handlers. * scdaemon.c (ticker_thread): Termintate if a shutdown is pending. * apdu.c: Added some PCSC error codes. (pcsc_error_to_sw): New. (reset_pcsc_reader, pcsc_get_status, pcsc_send_apdu) (open_pcsc_reader): Do proper error code mapping. * gpg-connect-agent.c: New. * Makefile.am: Add it. --- agent/ChangeLog | 5 + agent/gpg-agent.c | 5 + common/simple-pwquery.c | 2 +- common/xreadline.c | 2 +- doc/ChangeLog | 4 + doc/gpg-agent.texi | 14 +- doc/tools.texi | 62 +++++++ scd/ChangeLog | 23 +++ scd/apdu.c | 419 +++++++++++++++++++++++++++------------------- scd/app-common.h | 3 +- scd/app-openpgp.c | 45 +++-- scd/app.c | 28 ++-- scd/command.c | 81 +++++++-- scd/scdaemon.c | 3 +- tests/asschk.c | 4 +- tools/ChangeLog | 5 + tools/Makefile.am | 13 +- tools/gpg-connect-agent.c | 362 +++++++++++++++++++++++++++++++++++++++ 18 files changed, 840 insertions(+), 240 deletions(-) create mode 100644 tools/gpg-connect-agent.c (limited to 'scd/scdaemon.c') diff --git a/agent/ChangeLog b/agent/ChangeLog index 47ca2debf..775a44489 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,8 @@ +2005-02-24 Werner Koch + + * gpg-agent.c (handle_connections): Need to check for events if + select returns with -1. + 2005-02-23 Werner Koch * command-ssh.c (get_passphrase): Removed. diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 338248d1a..113489306 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -1463,6 +1463,11 @@ handle_connections (int listen_fd, int listen_fd_ssh) ret = pth_select_ev (FD_SETSIZE, &read_fdset, NULL, NULL, NULL, ev); if (ret == -1) { + if (pth_event_occurred (ev)) + { + handle_signal (signo); + continue; + } log_error (_("pth_select failed: %s - waiting 1s\n"), strerror (errno)); pth_sleep (1); diff --git a/common/simple-pwquery.c b/common/simple-pwquery.c index 0b70ddecc..37a45816c 100644 --- a/common/simple-pwquery.c +++ b/common/simple-pwquery.c @@ -1,4 +1,4 @@ -/* simple-pwquery.c - A simple password query cleint for gpg-agent +/* simple-pwquery.c - A simple password query client for gpg-agent * Copyright (C) 2002, 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. diff --git a/common/xreadline.c b/common/xreadline.c index 85f0af02e..23aa35269 100644 --- a/common/xreadline.c +++ b/common/xreadline.c @@ -39,7 +39,7 @@ memory was enable and ERRNO is set accordingly. If a line has been truncated, the file pointer is moved forward to - the end of the line so that the next read start with tghe next + the end of the line so that the next read start with the next line. Note that MAX_LENGTH must be re-initialzied in this case.. Note: The returned buffer is allocated with enough extra space to diff --git a/doc/ChangeLog b/doc/ChangeLog index 49e8b026f..07f94f338 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,7 @@ +2005-02-24 Werner Koch + + * tools.texi (gpg-connect-agent): New. + 2005-02-14 Werner Koch * gpgsm.texi (Certificate Management): Document --import. diff --git a/doc/gpg-agent.texi b/doc/gpg-agent.texi index 01b4227c6..17bd59c7a 100644 --- a/doc/gpg-agent.texi +++ b/doc/gpg-agent.texi @@ -326,10 +326,8 @@ Ignore requests to change change the current @sc{tty} respective the X window system's @code{DISPLAY} variable. This is useful to lock the pinentry to pop up at the @sc{tty} or display you started the agent. -@item --ssh-support -@itemx --ssh-support -@opindex ssh-support -@opindex ssh +@item --enable-ssh-support +@opindex enable-ssh-support Enable emulation of the OpenSSH Agent protocol. @@ -350,13 +348,11 @@ Once, a key has been added to the gpg-agent this way, the gpg-agent will be ready to use the key. Note: in case the gpg-agent receives a signature request, the user -might need to be prompted for a passphrased, which is necessary for +might need to be prompted for a passphrase, which is necessary for decrypting the stored key. Since the ssh-agent protocol does not contain a mechanism for telling the agent on which display/terminal it -is running, gpg-agent's --ssh-support switch implies --keep-display -and --keep-tty. This strategy causes the gpg-agent to open a pinentry -on the display or on the terminal, on which it (the gpg-agent) was -started. +is running, gpg-agent's ssh-support will use the TTY or X display where +gpg-agent has been started. @end table diff --git a/doc/tools.texi b/doc/tools.texi index 684975e19..7c963622f 100644 --- a/doc/tools.texi +++ b/doc/tools.texi @@ -13,6 +13,7 @@ GnuPG comes with a couple of smaller tools: * gpgconf:: Modify .gnupg home directories. * gpgsm-gencert.sh:: Generate an X.509 certificate request. * gpg-preset-passphrase:: Put a passphrase into the cache. +* gpg-connect-agent:: Communicate with a running agent. @end menu @c @@ -665,3 +666,64 @@ for other users. + +@c +@c GPG-CONNECT-AGENT +@c +@node gpg-connect-agent +@section Communicate with a runnig agent. + +The @command{gpg-connect-agent} is a utility to communicate with a +running @command{gpg-agent}. It is useful to check out the commands +gpg-agent provides using the Assuan interface. It might also be useful +for scripting simple applications. Inputis expected at stdin and out +put gets printed to stdout. + +It is very similar to running @command{gpg-agent} in server mode; but +here we connect to a running instance. + +@menu +* Invoking gpg-connect-agent:: List of all commands and options. +@end menu + + +@node Invoking gpg-connect-agent +@subsection List of all commands and options. + +@noindent +@command{gpg-connect-agent} is invoked this way: + +@example +gpg-connect-agent [options] +@end example + +@noindent +The following options may be used: + +@table @gnupgtabopt +@item -v +@itemx --verbose +@opindex verbose +Output additional information while running. + +@item -q +@item --quiet +@opindex q +@opindex quiet +Try to be as quiet as possible. + +@item --homedir @var{dir} +@opindex homedir +Set the name of the home directory to @var{dir}. If his option is not +used, the home directory defaults to @file{~/.gnupg}. It is only +recognized when given on the command line. It also overrides any home +directory stated through the environment variable @env{GNUPGHOME} or +(on W32 systems) by means on the Registry entry +@var{HKCU\Software\GNU\GnuPG:HomeDir}. + + +@end table + + + + diff --git a/scd/ChangeLog b/scd/ChangeLog index 054463d6d..c78bd011f 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,26 @@ +2005-02-24 Werner Koch + + * app-openpgp.c (get_one_do): Never try to get a non cacheable + object from the cache. + (get_one_do): Add new arg to return an error code. Changed all + callers. + (do_getattr): Let it return a proper error code. + + * app.c (select_application): Return an error code and the + application context in an new arg. + * command.c (open_card): Adjusted for that. Don't use the + fallback if no card is present. Return an error if the card has + been removed without a reset. + (do_reset, cmd_serialno): Clear that error flag. + (TEST_CARD_REMOVAL): New. Use it with all command handlers. + + * scdaemon.c (ticker_thread): Termintate if a shutdown is pending. + + * apdu.c: Added some PCSC error codes. + (pcsc_error_to_sw): New. + (reset_pcsc_reader, pcsc_get_status, pcsc_send_apdu) + (open_pcsc_reader): Do proper error code mapping. + 2005-02-22 Werner Koch * app-openpgp.c (app_local_s): New field PK. diff --git a/scd/apdu.c b/scd/apdu.c index 040de1461..33b0802c1 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -74,7 +74,7 @@ #endif #endif - + #define MAX_READER 4 /* Number of readers we support concurrently. */ @@ -158,14 +158,14 @@ static char (* DLSTDCALL CT_data) (unsigned short ctn, unsigned char *dad, static char (* DLSTDCALL CT_close) (unsigned short ctn); /* PC/SC constants and function pointer. */ -#define PCSC_SCOPE_USER 0 -#define PCSC_SCOPE_TERMINAL 1 -#define PCSC_SCOPE_SYSTEM 2 -#define PCSC_SCOPE_GLOBAL 3 +#define PCSC_SCOPE_USER 0 +#define PCSC_SCOPE_TERMINAL 1 +#define PCSC_SCOPE_SYSTEM 2 +#define PCSC_SCOPE_GLOBAL 3 -#define PCSC_PROTOCOL_T0 1 -#define PCSC_PROTOCOL_T1 2 -#define PCSC_PROTOCOL_RAW 4 +#define PCSC_PROTOCOL_T0 1 +#define PCSC_PROTOCOL_T1 2 +#define PCSC_PROTOCOL_RAW 4 #define PCSC_SHARE_EXCLUSIVE 1 #define PCSC_SHARE_SHARED 2 @@ -176,7 +176,7 @@ static char (* DLSTDCALL CT_close) (unsigned short ctn); #define PCSC_UNPOWER_CARD 2 #define PCSC_EJECT_CARD 3 -#define PCSC_UNKNOWN 0x0001 +#define PCSC_UNKNOWN 0x0001 #define PCSC_ABSENT 0x0002 /* Card is absent. */ #define PCSC_PRESENT 0x0004 /* Card is present. */ #define PCSC_SWALLOWED 0x0008 /* Card is present and electrical connected. */ @@ -196,10 +196,32 @@ static char (* DLSTDCALL CT_close) (unsigned short ctn); #define PCSC_STATE_INUSE 0x0100 /* Shared mode. */ #define PCSC_STATE_MUTE 0x0200 /* Unresponsive card. */ - -struct pcsc_io_request_s +/* Some PC/SC error codes. */ +#define PCSC_E_CANCELLED 0x80100002 +#define PCSC_E_CANT_DISPOSE 0x8010000E +#define PCSC_E_INSUFFICIENT_BUFFER 0x80100008 +#define PCSC_E_INVALID_ATR 0x80100015 +#define PCSC_E_INVALID_HANDLE 0x80100003 +#define PCSC_E_INVALID_PARAMETER 0x80100004 +#define PCSC_E_INVALID_TARGET 0x80100005 +#define PCSC_E_INVALID_VALUE 0x80100011 +#define PCSC_E_NO_MEMORY 0x80100006 +#define PCSC_E_UNKNOWN_READER 0x80100009 +#define PCSC_E_TIMEOUT 0x8010000A +#define PCSC_E_SHARING_VIOLATION 0x8010000B +#define PCSC_E_NO_SMARTCARD 0x8010000C +#define PCSC_E_UNKNOWN_CARD 0x8010000D +#define PCSC_E_PROTO_MISMATCH 0x8010000F +#define PCSC_E_NOT_READY 0x80100010 +#define PCSC_E_SYSTEM_CANCELLED 0x80100012 +#define PCSC_E_NOT_TRANSACTED 0x80100016 +#define PCSC_E_READER_UNAVAILABLE 0x80100017 +#define PCSC_W_REMOVED_CARD 0x80100069 + + +struct pcsc_io_request_s { - unsigned long protocol; + unsigned long protocol; unsigned long pci_len; }; @@ -262,15 +284,15 @@ long (* DLSTDCALL pcsc_set_timeout) (unsigned long context, -/* +/* Helper */ - + /* Find an unused reader slot for PORTSTR and put it into the reader table. Return -1 on error or the index into the reader table. */ -static int -new_reader_slot (void) +static int +new_reader_slot (void) { int i, reader = -1; @@ -387,8 +409,8 @@ apdu_strerror (int rc) -/* - ct API Interface +/* + ct API Interface */ static const char * @@ -425,9 +447,9 @@ 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. */ + dad[0] = 1; /* Destination address: CT. */ sad[0] = 2; /* Source address: Host. */ cmd[0] = 0x20; /* Class byte. */ @@ -446,8 +468,8 @@ ct_activate_card (int slot) return SW_HOST_CARD_IO_ERROR; } - /* Connected, now activate the card. */ - dad[0] = 1; /* Destination address: CT. */ + /* Connected, now activate the card. */ + dad[0] = 1; /* Destination address: CT. */ sad[0] = 2; /* Source address: Host. */ cmd[0] = 0x20; /* Class byte. */ @@ -517,13 +539,13 @@ ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen, 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. */ + dad[0] = 0; /* Destination address: Card. */ sad[0] = 2; /* Source address: Host. */ ctbuflen = *buflen; if (DBG_CARD_IO) @@ -582,6 +604,10 @@ open_ct_reader (int port) } +/* + PC/SC Interface + */ + #ifdef NEED_PCSC_WRAPPER static int writen (int fd, const void *buf, size_t nbytes) @@ -624,7 +650,7 @@ readn (int fd, void *buf, size_t buflen, size_t *nread) #else n = read (fd, buf, nleft); #endif - if (n < 0 && errno == EINTR) + if (n < 0 && errno == EINTR) continue; if (n < 0) return -1; /* read error. */ @@ -637,7 +663,7 @@ readn (int fd, void *buf, size_t buflen, size_t *nread) *nread = buflen - nleft; /* log_printhex (" readn:", orig_buf, *nread); */ - + return 0; } #endif /*NEED_PCSC_WRAPPER*/ @@ -656,48 +682,73 @@ pcsc_error_string (long err) { case 0x0002: s = "cancelled"; break; case 0x000e: s = "can't dispose"; break; - case 0x0008: s = "insufficient buffer"; break; + case 0x0008: s = "insufficient buffer"; break; case 0x0015: s = "invalid ATR"; break; case 0x0003: s = "invalid handle"; break; - case 0x0004: s = "invalid parameter"; break; + case 0x0004: s = "invalid parameter"; break; case 0x0005: s = "invalid target"; break; - case 0x0011: s = "invalid value"; break; - case 0x0006: s = "no memory"; break; - case 0x0013: s = "comm error"; break; - case 0x0001: s = "internal error"; break; - case 0x0014: s = "unknown error"; break; - case 0x0007: s = "waited too long"; break; + case 0x0011: s = "invalid value"; break; + case 0x0006: s = "no memory"; break; + case 0x0013: s = "comm error"; break; + case 0x0001: s = "internal error"; break; + case 0x0014: s = "unknown error"; break; + case 0x0007: s = "waited too long"; break; case 0x0009: s = "unknown reader"; break; - case 0x000a: s = "timeout"; break; - case 0x000b: s = "sharing violation"; break; + case 0x000a: s = "timeout"; break; + case 0x000b: s = "sharing violation"; break; case 0x000c: s = "no smartcard"; break; - case 0x000d: s = "unknown card"; break; - case 0x000f: s = "proto mismatch"; break; - case 0x0010: s = "not ready"; break; - case 0x0012: s = "system cancelled"; break; + case 0x000d: s = "unknown card"; break; + case 0x000f: s = "proto mismatch"; break; + case 0x0010: s = "not ready"; break; + case 0x0012: s = "system cancelled"; break; case 0x0016: s = "not transacted"; break; - case 0x0017: s = "reader unavailable"; break; - case 0x0065: s = "unsupported card"; break; - case 0x0066: s = "unresponsive card"; break; - case 0x0067: s = "unpowered card"; break; - case 0x0068: s = "reset card"; break; - case 0x0069: s = "removed card"; break; - case 0x006a: s = "inserted card"; break; - case 0x001f: s = "unsupported feature"; break; - case 0x0019: s = "PCI too small"; break; - case 0x001a: s = "reader unsupported"; break; - case 0x001b: s = "duplicate reader"; break; - case 0x001c: s = "card unsupported"; break; - case 0x001d: s = "no service"; break; - case 0x001e: s = "service stopped"; break; + case 0x0017: s = "reader unavailable"; break; + case 0x0065: s = "unsupported card"; break; + case 0x0066: s = "unresponsive card"; break; + case 0x0067: s = "unpowered card"; break; + case 0x0068: s = "reset card"; break; + case 0x0069: s = "removed card"; break; + case 0x006a: s = "inserted card"; break; + case 0x001f: s = "unsupported feature"; break; + case 0x0019: s = "PCI too small"; break; + case 0x001a: s = "reader unsupported"; break; + case 0x001b: s = "duplicate reader"; break; + case 0x001c: s = "card unsupported"; break; + case 0x001d: s = "no service"; break; + case 0x001e: s = "service stopped"; break; default: s = "unknown PC/SC error code"; break; } return s; } -/* - PC/SC Interface - */ +/* Map PC/SC error codes to our special host status words. */ +static int +pcsc_error_to_sw (long ec) +{ + int rc; + + switch (ec) + { + case 0: rc = 0; break; + + case PCSC_E_CANCELLED: rc = SW_HOST_ABORTED; break; + case PCSC_E_NO_MEMORY: rc = SW_HOST_OUT_OF_CORE; break; + case PCSC_E_TIMEOUT: rc = SW_HOST_CARD_IO_ERROR; break; + case PCSC_E_SHARING_VIOLATION: rc = SW_HOST_LOCKING_FAILED; break; + case PCSC_E_NO_SMARTCARD: rc = SW_HOST_NO_CARD; break; + case PCSC_W_REMOVED_CARD: rc = SW_HOST_NO_CARD; break; + + case PCSC_E_INVALID_TARGET: + case PCSC_E_INVALID_VALUE: + case PCSC_E_INVALID_HANDLE: + case PCSC_E_INVALID_PARAMETER: + case PCSC_E_INSUFFICIENT_BUFFER: rc = SW_HOST_INV_VALUE; break; + + default: rc = SW_HOST_GENERAL_ERROR; break; + } + + return rc; +} static void dump_pcsc_reader_status (int slot) @@ -713,6 +764,8 @@ dump_pcsc_reader_status (int slot) } +/* Send an PC/SC reset command and return a status word on error or 0 + on success. */ static int reset_pcsc_reader (int slot) { @@ -722,15 +775,16 @@ reset_pcsc_reader (int slot) size_t len; int i, n; unsigned char msgbuf[9]; + int sw = SW_HOST_CARD_IO_ERROR; slotp = reader_table + slot; - if (slotp->pcsc.req_fd == -1 - || slotp->pcsc.rsp_fd == -1 + if (slotp->pcsc.req_fd == -1 + || slotp->pcsc.rsp_fd == -1 || slotp->pcsc.pid == (pid_t)(-1) ) { log_error ("pcsc_get_status: pcsc-wrapper not running\n"); - return SW_HOST_CARD_IO_ERROR; + return sw; } msgbuf[0] = 0x05; /* RESET command. */ @@ -763,16 +817,23 @@ reset_pcsc_reader (int slot) if (len > DIM (slotp->atr)) { log_error ("PC/SC returned a too large ATR (len=%x)\n", len); + sw = SW_HOST_GENERAL_ERROR; goto command_failed; } err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8]; if (err) { - log_error ("PC/SC RESET failed: %s\n", pcsc_error_string (err)); + log_error ("PC/SC RESET failed: %s (0x%lx)\n", + pcsc_error_string (err), err); + /* If the error code is no smart card, we should not considere + this a major error and close the wrapper. */ + sw = pcsc_error_to_sw (err); + if (err == PCSC_E_NO_SMARTCARD) + return sw; goto command_failed; } - /* The open fucntion may return a zero for the ATR length to + /* The open function may return a zero for the ATR length to indicate that no card is present. */ n = len; if (n) @@ -796,7 +857,7 @@ reset_pcsc_reader (int slot) kill (slotp->pcsc.pid, SIGTERM); slotp->pcsc.pid = (pid_t)(-1); slotp->used = 0; - return -1; + return sw; #else /* !NEED_PCSC_WRAPPER */ long err; @@ -827,10 +888,10 @@ reset_pcsc_reader (int slot) log_error ("pcsc_connect failed: %s (0x%lx)\n", pcsc_error_string (err), err); reader_table[slot].pcsc.card = 0; - return SW_HOST_CARD_IO_ERROR; - } + return pcsc_error_to_sw (err); + } + - atrlen = 33; nreader = sizeof reader - 1; err = pcsc_status (reader_table[slot].pcsc.card, @@ -842,7 +903,7 @@ reset_pcsc_reader (int slot) log_error ("pcsc_status failed: %s (0x%lx)\n", pcsc_error_string (err), err); reader_table[slot].atrlen = 0; - return SW_HOST_CARD_IO_ERROR; + return pcsc_error_to_sw (err); } if (atrlen >= DIM (reader_table[0].atr)) log_bug ("ATR returned by pcsc_status is too large\n"); @@ -863,15 +924,16 @@ pcsc_get_status (int slot, unsigned int *status) int i, n; unsigned char msgbuf[9]; unsigned char buffer[12]; + int sw = SW_HOST_CARD_IO_ERROR; slotp = reader_table + slot; - if (slotp->pcsc.req_fd == -1 - || slotp->pcsc.rsp_fd == -1 + if (slotp->pcsc.req_fd == -1 + || slotp->pcsc.rsp_fd == -1 || slotp->pcsc.pid == (pid_t)(-1) ) { log_error ("pcsc_get_status: pcsc-wrapper not running\n"); - return SW_HOST_CARD_IO_ERROR; + return sw; } msgbuf[0] = 0x04; /* STATUS command. */ @@ -906,11 +968,12 @@ pcsc_get_status (int slot, unsigned int *status) { log_error ("pcsc_status failed: %s (0x%lx)\n", pcsc_error_string (err), err); - return SW_HOST_CARD_IO_ERROR; + /* This is a proper error code, so return immediately. */ + return pcsc_error_to_sw (err); } full_len = len; - + n = 8 < len ? 8 : len; if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != 8) { @@ -935,7 +998,7 @@ pcsc_get_status (int slot, unsigned int *status) } full_len -= n; } - + /* We are lucky: The wrapper already returns the data in the required format. */ *status = buffer[3]; @@ -950,26 +1013,26 @@ pcsc_get_status (int slot, unsigned int *status) kill (slotp->pcsc.pid, SIGTERM); slotp->pcsc.pid = (pid_t)(-1); slotp->used = 0; - return -1; + return sw; #else /*!NEED_PCSC_WRAPPER*/ long err; struct pcsc_readerstate_s rdrstates[1]; - + memset (rdrstates, 0, sizeof *rdrstates); rdrstates[0].reader = reader_table[slot].rdrname; rdrstates[0].current_state = PCSC_STATE_UNAWARE; err = pcsc_get_status_change (reader_table[slot].pcsc.context, 0, rdrstates, 1); - if (err == 0x8010000a) /* Timeout. */ - err = 0; + if (err == PCSC_E_TIMEOUT) + err = 0; /* Timeout is no error error here. */ if (err) { log_error ("pcsc_get_status_change failed: %s (0x%lx)\n", pcsc_error_string (err), err); - return SW_HOST_CARD_IO_ERROR; + return pcsc_error_to_sw (err); } @@ -997,8 +1060,8 @@ pcsc_get_status (int slot, unsigned int *status) if ( (*status & 6) == 6 && !(rdrstates[0].event_state & PCSC_STATE_INUSE) ) *status |= 1; - - return 0; + + return 0; #endif /*!NEED_PCSC_WRAPPER*/ } @@ -1016,6 +1079,7 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, size_t len, full_len; int i, n; unsigned char msgbuf[9]; + int sw = SW_HOST_CARD_IO_ERROR; if (!reader_table[slot].atrlen && (err = reset_pcsc_reader (slot))) @@ -1026,12 +1090,12 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, slotp = reader_table + slot; - if (slotp->pcsc.req_fd == -1 - || slotp->pcsc.rsp_fd == -1 + 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; + return sw; } msgbuf[0] = 0x03; /* TRANSMIT command. */ @@ -1067,11 +1131,11 @@ 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 SW_HOST_CARD_IO_ERROR; + return pcsc_error_to_sw (err); } full_len = len; - + n = *buflen < len ? *buflen : len; if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != n) { @@ -1113,14 +1177,14 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, kill (slotp->pcsc.pid, SIGTERM); slotp->pcsc.pid = (pid_t)(-1); slotp->used = 0; - return -1; + return sw; #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; @@ -1141,8 +1205,8 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, if (err) log_error ("pcsc_transmit failed: %s (0x%lx)\n", pcsc_error_string (err), err); - - return err? SW_HOST_CARD_IO_ERROR:0; + + return pcsc_error_to_sw (err); #endif /*!NEED_PCSC_WRAPPER*/ } @@ -1159,8 +1223,8 @@ close_pcsc_reader (int slot) slotp = reader_table + slot; - if (slotp->pcsc.req_fd == -1 - || slotp->pcsc.rsp_fd == -1 + 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"); @@ -1198,10 +1262,10 @@ close_pcsc_reader (int slot) 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 + + /* We will close the wrapper in any case - errors are merely informational. */ - + command_failed: close (slotp->pcsc.req_fd); close (slotp->pcsc.rsp_fd); @@ -1222,6 +1286,7 @@ close_pcsc_reader (int slot) #endif /*!NEED_PCSC_WRAPPER*/ } +/* Note: It is a pitty that we can't return proper error codes. */ static int open_pcsc_reader (const char *portstr) { @@ -1237,6 +1302,7 @@ open_pcsc_reader (const char *portstr) size_t len; unsigned char msgbuf[9]; int err; + int sw = SW_HOST_CARD_IO_ERROR; slot = new_reader_slot (); if (slot == -1) @@ -1246,7 +1312,7 @@ open_pcsc_reader (const char *portstr) /* Fire up the pcsc wrapper. We don't use any fork/exec code from the common directy but implement it direclty so that this file may still be source copied. */ - + if (pipe (rp) == -1) { log_error ("error creating a pipe: %s\n", strerror (errno)); @@ -1261,7 +1327,7 @@ open_pcsc_reader (const char *portstr) slotp->used = 0; return -1; } - + pid = fork (); if (pid == -1) { @@ -1283,7 +1349,7 @@ open_pcsc_reader (const char *portstr) /* Double fork. */ pid = fork (); if (pid == -1) - _exit (31); + _exit (31); if (pid) _exit (0); /* Immediate exit this parent, so that the child gets cleaned up by the init process. */ @@ -1293,7 +1359,7 @@ open_pcsc_reader (const char *portstr) log_fatal ("dup2 stdin failed: %s\n", strerror (errno)); if (rp[1] != 1 && dup2 (rp[1], 1) == -1) log_fatal ("dup2 stdout failed: %s\n", strerror (errno)); - + /* Send stderr to the bit bucket. */ fd = open ("/dev/null", O_WRONLY); if (fd == -1) @@ -1318,7 +1384,7 @@ open_pcsc_reader (const char *portstr) _exit (31); } - /* + /* === Parent === */ close (wp[0]); @@ -1328,9 +1394,9 @@ open_pcsc_reader (const char *portstr) /* Wait for the intermediate child to terminate. */ #ifdef USE_GNU_PTH -#define WAIT pth_waitpid +#define WAIT pth_waitpid #else -#define WAIT waitpid +#define WAIT waitpid #endif while ( (i=WAIT (pid, NULL, 0)) == -1 && errno == EINTR) ; @@ -1373,6 +1439,7 @@ open_pcsc_reader (const char *portstr) if (err) { log_error ("PC/SC OPEN failed: %s\n", pcsc_error_string (err)); + sw = pcsc_error_to_sw (err); goto command_failed; } @@ -1401,7 +1468,7 @@ open_pcsc_reader (const char *portstr) reader_table[slot].send_apdu_reader = pcsc_send_apdu; reader_table[slot].dump_status_reader = dump_pcsc_reader_status; - dump_reader_status (slot); + dump_reader_status (slot); return slot; command_failed: @@ -1412,7 +1479,9 @@ open_pcsc_reader (const char *portstr) kill (slotp->pcsc.pid, SIGTERM); slotp->pcsc.pid = (pid_t)(-1); slotp->used = 0; + /* There is no way to return SW. */ return -1; + #else /*!NEED_PCSC_WRAPPER */ long err; int slot; @@ -1434,7 +1503,7 @@ open_pcsc_reader (const char *portstr) reader_table[slot].used = 0; return -1; } - + err = pcsc_list_readers (reader_table[slot].pcsc.context, NULL, NULL, &nreader); if (!err) @@ -1445,7 +1514,7 @@ open_pcsc_reader (const char *portstr) log_error ("error allocating memory for reader list\n"); pcsc_release_context (reader_table[slot].pcsc.context); reader_table[slot].used = 0; - return -1; + return -1 /*SW_HOST_OUT_OF_CORE*/; } err = pcsc_list_readers (reader_table[slot].pcsc.context, NULL, list, &nreader); @@ -1457,7 +1526,7 @@ open_pcsc_reader (const char *portstr) pcsc_release_context (reader_table[slot].pcsc.context); reader_table[slot].used = 0; xfree (list); - return -1; + return -1 /*pcsc_error_to_sw (err)*/; } listlen = nreader; @@ -1483,7 +1552,7 @@ open_pcsc_reader (const char *portstr) log_error ("error allocating memory for reader name\n"); pcsc_release_context (reader_table[slot].pcsc.context); reader_table[slot].used = 0; - return -1; + return -1 /*SW_HOST_OUT_OF_CORE*/; } strcpy (reader_table[slot].rdrname, portstr? portstr : list); xfree (list); @@ -1494,7 +1563,7 @@ open_pcsc_reader (const char *portstr) PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1, &reader_table[slot].pcsc.card, &reader_table[slot].pcsc.protocol); - if (err == 0x8010000c) /* No smartcard. */ + if (err == PCSC_E_NO_SMARTCARD) reader_table[slot].pcsc.card = 0; else if (err) { @@ -1505,8 +1574,8 @@ open_pcsc_reader (const char *portstr) reader_table[slot].rdrname = NULL; reader_table[slot].used = 0; xfree (list); - return -1; - } + return -1 /*pcsc_error_to_sw (err)*/; + } reader_table[slot].atrlen = 0; reader_table[slot].last_status = 0; @@ -1544,7 +1613,7 @@ open_pcsc_reader (const char *portstr) /* log_debug ("state from pcsc_status: 0x%lx\n", card_state); */ /* log_debug ("protocol from pcsc_status: 0x%lx\n", card_protocol); */ - dump_reader_status (slot); + dump_reader_status (slot); return slot; #endif /*!NEED_PCSC_WRAPPER */ } @@ -1553,7 +1622,7 @@ open_pcsc_reader (const char *portstr) #ifdef HAVE_LIBUSB -/* +/* Internal CCID driver interface. */ @@ -1570,16 +1639,16 @@ close_ccid_reader (int slot) ccid_close_reader (reader_table[slot].ccid.handle); reader_table[slot].used = 0; return 0; -} - +} + static int shutdown_ccid_reader (int slot) { ccid_shutdown_reader (reader_table[slot].ccid.handle); return 0; -} - +} + static int reset_ccid_reader (int slot) @@ -1596,10 +1665,10 @@ reset_ccid_reader (int slot) assert (sizeof slotp->atr >= sizeof atr); slotp->atrlen = atrlen; memcpy (slotp->atr, atr, atrlen); - dump_reader_status (slot); + dump_reader_status (slot); return 0; -} - +} + static int get_status_ccid (int slot, unsigned int *status) @@ -1615,7 +1684,7 @@ get_status_ccid (int slot, unsigned int *status) *status = 1|2|4; else if (bits == 1) *status = 2; - else + else *status = 0; return 0; @@ -1647,8 +1716,8 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen, if (err) log_error ("ccid_transceive failed: (0x%lx)\n", err); - - return err; + + return err; } /* Open the reader and try to read an ATR. */ @@ -1692,7 +1761,7 @@ open_ccid_reader (const char *portstr) reader_table[slot].send_apdu_reader = send_apdu_ccid; reader_table[slot].dump_status_reader = dump_ccid_reader_status; - dump_reader_status (slot); + dump_reader_status (slot); return slot; } @@ -1703,7 +1772,7 @@ open_ccid_reader (const char *portstr) #ifdef HAVE_OPENSC -/* +/* OpenSC Interface. This uses the OpenSC primitives to send APDUs. We need this @@ -1764,7 +1833,7 @@ osc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, if (!apdulen) a.cse = SC_APDU_CASE_1; - else if (apdulen == 1) + else if (apdulen == 1) { a.le = *apdu? *apdu : 256; apdu++; apdulen--; @@ -1784,7 +1853,7 @@ osc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, a.data = data; a.datalen = a.lc; - + if (!apdulen) a.cse = SC_APDU_CASE_3_SHORT; else @@ -1861,7 +1930,7 @@ open_osc_reader (int portno) 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"); @@ -1912,7 +1981,7 @@ open_osc_reader (int portno) reader_table[slot].send_apdu_reader = osc_send_apdu; reader_table[slot].dump_status_reader = NULL; - dump_reader_status (slot); + dump_reader_status (slot); return slot; } @@ -1921,7 +1990,7 @@ open_osc_reader (int portno) #ifdef USE_G10CODE_RAPDU -/* +/* The Remote APDU Interface. This uses the Remote APDU protocol to contact a reader. @@ -1940,9 +2009,9 @@ rapdu_status_to_sw (int status) { case RAPDU_STATUS_SUCCESS: rc = 0; break; - case RAPDU_STATUS_INVCMD: - case RAPDU_STATUS_INVPROT: - case RAPDU_STATUS_INVSEQ: + 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; @@ -2007,7 +2076,7 @@ reset_rapdu_reader (int slot) { log_error ("ATR returned by the RAPDU layer is too large\n"); rapdu_msg_release (msg); - return SW_HOST_INV_VALUE; + return SW_HOST_INV_VALUE; } slotp->atrlen = msg->datalen; memcpy (slotp->atr, msg->data, msg->datalen); @@ -2107,12 +2176,12 @@ my_rapdu_send_apdu (int slot, unsigned char *apdu, size_t apdulen, 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; + return SW_HOST_INV_VALUE; } *buflen = msg->datalen; @@ -2196,11 +2265,11 @@ open_rapdu_reader (int portno, reader_table[slot].send_apdu_reader = my_rapdu_send_apdu; reader_table[slot].dump_status_reader = NULL; - dump_reader_status (slot); + dump_reader_status (slot); rapdu_msg_release (msg); return slot; - failure: + failure: rapdu_msg_release (msg); rapdu_release (slotp->rapdu.handle); slotp->used = 0; @@ -2211,7 +2280,7 @@ open_rapdu_reader (int portno, -/* +/* Driver Access */ @@ -2290,7 +2359,7 @@ apdu_open_reader (const char *portstr) return open_osc_reader (port); } -#endif /* HAVE_OPENSC */ +#endif /* HAVE_OPENSC */ if (opt.ctapi_driver && *opt.ctapi_driver) @@ -2300,7 +2369,7 @@ apdu_open_reader (const char *portstr) if (!ct_api_loaded) { void *handle; - + handle = dlopen (opt.ctapi_driver, RTLD_LAZY); if (!handle) { @@ -2322,7 +2391,7 @@ apdu_open_reader (const char *portstr) return open_ct_reader (port); } - + /* No ctAPI configured, so lets try the PC/SC API */ if (!pcsc_api_loaded) { @@ -2371,16 +2440,16 @@ apdu_open_reader (const char *portstr) pcsc_set_timeout = dlsym (handle, "SCardSetTimeout"); if (!pcsc_establish_context - || !pcsc_release_context - || !pcsc_list_readers + || !pcsc_release_context + || !pcsc_list_readers || !pcsc_get_status_change - || !pcsc_connect + || !pcsc_connect || !pcsc_reconnect || !pcsc_disconnect || !pcsc_status || !pcsc_begin_transaction || !pcsc_end_transaction - || !pcsc_transmit + || !pcsc_transmit /* || !pcsc_set_timeout */) { /* Note that set_timeout is currently not used and also not @@ -2388,21 +2457,21 @@ apdu_open_reader (const char *portstr) log_error ("apdu_open_reader: invalid PC/SC driver " "(%d%d%d%d%d%d%d%d%d%d%d%d)\n", !!pcsc_establish_context, - !!pcsc_release_context, - !!pcsc_list_readers, - !!pcsc_get_status_change, - !!pcsc_connect, - !!pcsc_reconnect, + !!pcsc_release_context, + !!pcsc_list_readers, + !!pcsc_get_status_change, + !!pcsc_connect, + !!pcsc_reconnect, !!pcsc_disconnect, !!pcsc_status, !!pcsc_begin_transaction, !!pcsc_end_transaction, - !!pcsc_transmit, + !!pcsc_transmit, !!pcsc_set_timeout ); dlclose (handle); return -1; } -#endif /*!NEED_PCSC_WRAPPER*/ +#endif /*!NEED_PCSC_WRAPPER*/ pcsc_api_loaded = 1; } @@ -2416,7 +2485,7 @@ apdu_open_reader (const char *portstr) 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, @@ -2436,7 +2505,7 @@ apdu_open_remote_reader (const char *portstr, writefnc, writefnc_value, closefnc, closefnc_value); #else -#ifdef _WIN32 +#ifdef _WIN32 errno = ENOENT; #else errno = ENOSYS; @@ -2489,7 +2558,7 @@ apdu_reset (int slot) if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) return SW_HOST_NO_DRIVER; - + if ((sw = lock_slot (slot))) return sw; @@ -2521,7 +2590,7 @@ apdu_activate (int slot) if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) return SW_HOST_NO_DRIVER; - + if ((sw = trylock_slot (slot))) return sw; @@ -2550,12 +2619,12 @@ apdu_activate (int slot) } } } - + unlock_slot (slot); return sw; } - + unsigned char * apdu_get_atr (int slot, size_t *atrlen) @@ -2564,7 +2633,7 @@ apdu_get_atr (int slot, size_t *atrlen) if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) return NULL; - + buf = xtrymalloc (reader_table[slot].atrlen); if (!buf) return NULL; @@ -2574,7 +2643,7 @@ apdu_get_atr (int slot, size_t *atrlen) } - + /* 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 @@ -2660,7 +2729,7 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen, returned data. The length of that data will be put into *RETBUFLEN. The caller is reponsible for releasing the buffer even in case of errors. */ -int +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) @@ -2682,9 +2751,9 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, class, ins, p0, p1, lc, le); if (lc != -1 && (lc > 255 || lc < 0)) - return SW_WRONG_LENGTH; + return SW_WRONG_LENGTH; if (le != -1 && (le > 256 || le < 1)) - return SW_WRONG_LENGTH; + return SW_WRONG_LENGTH; if ((!data && lc != -1) || (data && lc == -1)) return SW_HOST_INV_VALUE; @@ -2764,7 +2833,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, do { int len = (sw & 0x00ff); - + if (DBG_CARD_IO) log_debug ("apdu_send_simple(%d): %d more bytes available\n", slot, len); @@ -2773,7 +2842,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, apdu[apdulen++] = 0xC0; apdu[apdulen++] = 0; apdu[apdulen++] = 0; - apdu[apdulen++] = len; + apdu[apdulen++] = len; memset (apdu+apdulen, 0, sizeof (apdu) - apdulen); resultlen = RESULTLEN; rc = send_apdu (slot, apdu, apdulen, result, &resultlen); @@ -2822,7 +2891,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, slot, sw); } while ((sw & 0xff00) == SW_MORE_DATA); - + if (retbuf) { *retbuflen = p - *retbuf; @@ -2836,7 +2905,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, if (DBG_CARD_IO && retbuf && sw == SW_SUCCESS) log_printhex (" dump: ", *retbuf, *retbuflen); - + return sw; #undef RESULTLEN } @@ -2850,11 +2919,11 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, data. The length of that data will be put into *RETBUFLEN. The caller is reponsible for releasing the buffer even in case of errors. */ -int +int apdu_send (int slot, int class, int ins, int p0, int p1, int lc, const char *data, unsigned char **retbuf, size_t *retbuflen) { - return apdu_send_le (slot, class, ins, p0, p1, lc, data, 256, + return apdu_send_le (slot, class, ins, p0, p1, lc, data, 256, retbuf, retbuflen); } @@ -2864,7 +2933,7 @@ apdu_send (int slot, int class, int ins, int p0, int p1, also be passed as NULL. The return value is the status word or -1 for an invalid SLOT or other non card related error. No data will be returned. */ -int +int apdu_send_simple (int slot, int class, int ins, int p0, int p1, int lc, const char *data) { @@ -2880,7 +2949,7 @@ apdu_send_simple (int slot, int class, int ins, int p0, int p1, 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 +int apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen, int handle_more, unsigned char **retbuf, size_t *retbuflen) @@ -2951,7 +3020,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen, do { int len = (sw & 0x00ff); - + if (DBG_CARD_IO) log_debug ("apdu_send_direct(%d): %d more bytes available\n", slot, len); @@ -2960,7 +3029,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen, apdu[apdulen++] = 0xC0; apdu[apdulen++] = 0; apdu[apdulen++] = 0; - apdu[apdulen++] = len; + apdu[apdulen++] = len; memset (apdu+apdulen, 0, sizeof (apdu) - apdulen); resultlen = RESULTLEN; rc = send_apdu (slot, apdu, apdulen, result, &resultlen); @@ -3009,7 +3078,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen, slot, sw); } while ((sw & 0xff00) == SW_MORE_DATA); - + if (retbuf) { *retbuflen = p - *retbuf; @@ -3045,9 +3114,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen, if (DBG_CARD_IO && retbuf) log_printhex (" dump: ", *retbuf, *retbuflen); - + return 0; #undef RESULTLEN } - - diff --git a/scd/app-common.h b/scd/app-common.h index ace57d98c..4a2adaa54 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -104,7 +104,8 @@ size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff); /*-- app.c --*/ -app_t select_application (ctrl_t ctrl, int slot, const char *name); +gpg_error_t select_application (ctrl_t ctrl, int slot, const char *name, + app_t *r_app); void release_application (app_t app); int app_munge_serialno (app_t app); int app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp); diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 8d146ba6a..14c802d10 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -306,23 +306,33 @@ flush_cache (app_t app) NULL if not found or a pointer which must be used to release the buffer holding value. */ static void * -get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes) +get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes, + int *r_rc) { int rc, i; unsigned char *buffer; size_t buflen; unsigned char *value; size_t valuelen; + int dummyrc; + + if (!r_rc) + r_rc = &dummyrc; *result = NULL; *nbytes = 0; + *r_rc = 0; for (i=0; data_objects[i].tag && data_objects[i].tag != tag; i++) ; if (app->card_version > 0x0100 && data_objects[i].get_immediate_in_v11) { - if( iso7816_get_data (app->slot, tag, &buffer, &buflen)) - return NULL; + rc = iso7816_get_data (app->slot, tag, &buffer, &buflen); + if (rc) + { + *r_rc = rc; + return NULL; + } *result = buffer; *nbytes = buflen; return buffer; @@ -334,7 +344,8 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes) { rc = get_cached_data (app, data_objects[i].get_from, &buffer, &buflen, - data_objects[i].get_immediate_in_v11); + (data_objects[i].dont_cache + || data_objects[i].get_immediate_in_v11)); if (!rc) { const unsigned char *s; @@ -356,7 +367,8 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes) if (!value) /* Not in a constructed DO, try simple. */ { rc = get_cached_data (app, tag, &buffer, &buflen, - data_objects[i].get_immediate_in_v11); + (data_objects[i].dont_cache + || data_objects[i].get_immediate_in_v11)); if (!rc) { value = buffer; @@ -370,6 +382,7 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes) *result = value; return buffer; } + *r_rc = rc; return NULL; } @@ -488,7 +501,7 @@ parse_login_data (app_t app) app->app_local->flags.def_chv2 = 0; /* Read the DO. */ - relptr = get_one_do (app, 0x005E, &buffer, &buflen); + relptr = get_one_do (app, 0x005E, &buffer, &buflen, NULL); if (!relptr) return; /* Ooops. */ for (; buflen; buflen--, buffer++) @@ -678,7 +691,7 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) { "PRIVATE-DO-4", 0x0104 }, { NULL, 0 } }; - int idx, i; + int idx, i, rc; void *relptr; unsigned char *value; size_t valuelen; @@ -723,7 +736,7 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) return 0; } - relptr = get_one_do (app, table[idx].tag, &value, &valuelen); + relptr = get_one_do (app, table[idx].tag, &value, &valuelen, &rc); if (relptr) { if (table[idx].special == 1) @@ -760,7 +773,7 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) xfree (relptr); } - return 0; + return rc; } @@ -1075,7 +1088,7 @@ verify_chv3 (app_t app, unsigned char *value; size_t valuelen; - relptr = get_one_do (app, 0x00C4, &value, &valuelen); + relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL); if (!relptr || valuelen < 7) { log_error (_("error retrieving CHV status from card\n")); @@ -1442,7 +1455,7 @@ get_sig_counter (app_t app) size_t valuelen; unsigned long ul; - relptr = get_one_do (app, 0x0093, &value, &valuelen); + relptr = get_one_do (app, 0x0093, &value, &valuelen, NULL); if (!relptr) return 0; ul = convert_sig_counter_value (value, valuelen); @@ -1880,7 +1893,7 @@ app_select_openpgp (app_t app) goto leave; } - relptr = get_one_do (app, 0x00C4, &buffer, &buflen); + relptr = get_one_do (app, 0x00C4, &buffer, &buflen, NULL); if (!relptr) { log_error (_("can't access %s - invalid OpenPGP card?\n"), @@ -1890,7 +1903,7 @@ app_select_openpgp (app_t app) app->force_chv1 = (buflen && *buffer == 0); xfree (relptr); - relptr = get_one_do (app, 0x00C0, &buffer, &buflen); + relptr = get_one_do (app, 0x00C0, &buffer, &buflen, NULL); if (!relptr) { log_error (_("can't access %s - invalid OpenPGP card?\n"), @@ -1973,7 +1986,7 @@ app_openpgp_cardinfo (app_t app, if (disp_name) { *disp_name = NULL; - relptr = get_one_do (app, 0x005B, &value, &valuelen); + relptr = get_one_do (app, 0x005B, &value, &valuelen, NULL); if (relptr) { *disp_name = make_printable_string (value, valuelen, 0); @@ -1984,7 +1997,7 @@ app_openpgp_cardinfo (app_t app, if (pubkey_url) { *pubkey_url = NULL; - relptr = get_one_do (app, 0x5F50, &value, &valuelen); + relptr = get_one_do (app, 0x5F50, &value, &valuelen, NULL); if (relptr) { *pubkey_url = make_printable_string (value, valuelen, 0); @@ -1998,7 +2011,7 @@ app_openpgp_cardinfo (app_t app, *fpr2 = NULL; if (fpr3) *fpr3 = NULL; - relptr = get_one_do (app, 0x00C5, &value, &valuelen); + relptr = get_one_do (app, 0x00C5, &value, &valuelen, NULL); if (relptr && valuelen >= 60) { if (fpr1) diff --git a/scd/app.c b/scd/app.c index fad4eba55..857f9e10b 100644 --- a/scd/app.c +++ b/scd/app.c @@ -1,5 +1,5 @@ /* app.c - Application selection. - * Copyright (C) 2003, 2004 Free Software Foundation, Inc. + * Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -49,21 +49,23 @@ is_app_allowed (const char *name) /* If called with NAME as NULL, select the best fitting application and return a context; otherwise select the application with NAME and return a context. SLOT identifies the reader device. Returns - NULL if no application was found or no card is present. */ -APP -select_application (ctrl_t ctrl, int slot, const char *name) + an error code and stores NULL at R_APP if no application was found + or no card is present. */ +gpg_error_t +select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app) { int rc; - APP app; + app_t app; unsigned char *result = NULL; size_t resultlen; + *r_app = NULL; app = xtrycalloc (1, sizeof *app); if (!app) { - rc = gpg_error (gpg_err_code_from_errno (errno)); + rc = gpg_error_from_errno (errno); log_info ("error allocating context: %s\n", gpg_strerror (rc)); - return NULL; + return rc; } app->slot = slot; @@ -75,7 +77,7 @@ select_application (ctrl_t ctrl, int slot, const char *name) if (!rc) rc = iso7816_select_file (slot, 0x2F02, 0, NULL, NULL); if (!rc) - rc = iso7816_read_binary (slot, 0, 0, &result, &resultlen); + rc = iso7816_read_binary (slot, 0, 0, &result, &resultlen); if (!rc) { size_t n; @@ -111,7 +113,12 @@ select_application (ctrl_t ctrl, int slot, const char *name) result = NULL; } + /* For certain error codes, there is no need to try more. */ + if (gpg_err_code (rc) == GPG_ERR_CARD_NOT_PRESENT) + goto leave; + + /* Figure out the application to use. */ rc = gpg_error (GPG_ERR_NOT_FOUND); if (rc && is_app_allowed ("openpgp") && (!name || !strcmp (name, "openpgp"))) @@ -135,11 +142,12 @@ select_application (ctrl_t ctrl, int slot, const char *name) log_info ("no supported card application found: %s\n", gpg_strerror (rc)); xfree (app); - return NULL; + return rc; } app->initialized = 1; - return app; + *r_app = app; + return 0; } diff --git a/scd/command.c b/scd/command.c index 72f48b2b8..a4fb968cf 100644 --- a/scd/command.c +++ b/scd/command.c @@ -45,10 +45,24 @@ static ctrl_t primary_connection; #define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t)) + +/* Macro to flag a a removed card. */ +#define TEST_CARD_REMOVAL(c,r) \ + do { \ + int _r = (r); \ + if (gpg_err_code (_r) == GPG_ERR_CARD_NOT_PRESENT \ + || gpg_err_code (_r) == GPG_ERR_CARD_REMOVED) \ + (c)->server_local->card_removed = 1; \ + } while (0) + + /* Data used to associate an Assuan context with local server data */ struct server_local_s { - ASSUAN_CONTEXT assuan_ctx; + assuan_context_t assuan_ctx; int event_signal; /* Or 0 if not used. */ + int card_removed; /* True if the card has been removed and a + reset is required to continue + operation. */ }; @@ -89,6 +103,7 @@ do_reset (ctrl_t ctrl, int do_close) ctrl->reader_slot = -1; } } + ctrl->server_local->card_removed = 0; } @@ -122,11 +137,18 @@ option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value) /* If the card has not yet been opened, do it. Note that this function returns an Assuan error, so don't map the error a second time */ -static AssuanError +static assuan_error_t open_card (ctrl_t ctrl, const char *apptype) { + gpg_error_t err; int slot; + /* If we ever got a card not present error code, return that. Only + the SERIALNO command and a reset are able to clear from that + state. */ + if (ctrl->server_local->card_removed) + return map_to_assuan_status (gpg_error (GPG_ERR_CARD_REMOVED)); + if (ctrl->app_ctx) return 0; /* Already initialized for one specific application. */ if (ctrl->card_ctx) @@ -137,24 +159,28 @@ open_card (ctrl_t ctrl, const char *apptype) else slot = apdu_open_reader (opt.reader_port); ctrl->reader_slot = slot; - if (slot != -1) - ctrl->app_ctx = select_application (ctrl, slot, apptype); - if (!ctrl->app_ctx) - { /* No application found - fall back to old mode. */ + if (slot == -1) + err = gpg_error (GPG_ERR_CARD); + else + err = select_application (ctrl, slot, apptype, &ctrl->app_ctx); + if (!ctrl->app_ctx + && gpg_err_code (err) != GPG_ERR_CARD_NOT_PRESENT) + { + /* No application found - fall back to old mode. */ /* Note that we should rework the old code to use the application paradigma too. */ - int rc; - /* If an APPTYPE was requested and it is not pkcs#15, we return an error here. */ if (apptype && !(!strcmp (apptype, "P15") || !strcmp (apptype, "p15"))) - rc = gpg_error (GPG_ERR_NOT_SUPPORTED); + err = gpg_error (GPG_ERR_NOT_SUPPORTED); else - rc = card_open (&ctrl->card_ctx); - if (rc) - return map_to_assuan_status (rc); + err = card_open (&ctrl->card_ctx); } - return 0; + + if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT) + ctrl->server_local->card_removed = 1; + + return map_to_assuan_status (err); } @@ -215,12 +241,15 @@ percent_plus_unescape (unsigned char *string) static int cmd_serialno (ASSUAN_CONTEXT ctx, char *line) { - CTRL ctrl = assuan_get_pointer (ctx); + ctrl_t ctrl = assuan_get_pointer (ctx); int rc = 0; char *serial_and_stamp; char *serial; time_t stamp; + /* Clear the remove flag so that the open_card is able to reread it. */ + ctrl->server_local->card_removed = 0; + if ((rc = open_card (ctrl, *line? line:NULL))) return rc; @@ -443,6 +472,7 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line) if (rc == -1) rc = 0; + TEST_CARD_REMOVAL (ctrl, rc); return map_to_assuan_status (rc); } @@ -485,6 +515,7 @@ cmd_readcert (ASSUAN_CONTEXT ctx, char *line) return rc; } + TEST_CARD_REMOVAL (ctrl, rc); return map_to_assuan_status (rc); } @@ -575,6 +606,7 @@ cmd_readkey (assuan_context_t ctx, char *line) leave: ksba_cert_release (kc); xfree (cert); + TEST_CARD_REMOVAL (ctrl, rc); return map_to_assuan_status (rc); } @@ -697,6 +729,7 @@ cmd_pksign (ASSUAN_CONTEXT ctx, char *line) return rc; /* that is already an assuan error code */ } + TEST_CARD_REMOVAL (ctrl, rc); return map_to_assuan_status (rc); } @@ -743,6 +776,7 @@ cmd_pkauth (ASSUAN_CONTEXT ctx, char *line) return rc; /* that is already an assuan error code */ } + TEST_CARD_REMOVAL (ctrl, rc); return map_to_assuan_status (rc); } @@ -789,6 +823,7 @@ cmd_pkdecrypt (ASSUAN_CONTEXT ctx, char *line) return rc; /* that is already an assuan error code */ } + TEST_CARD_REMOVAL (ctrl, rc); return map_to_assuan_status (rc); } @@ -824,6 +859,7 @@ cmd_getattr (ASSUAN_CONTEXT ctx, char *line) rc = app_getattr (ctrl->app_ctx, ctrl, keyword); + TEST_CARD_REMOVAL (ctrl, rc); return map_to_assuan_status (rc); } @@ -871,6 +907,7 @@ cmd_setattr (ASSUAN_CONTEXT ctx, char *orig_line) rc = app_setattr (ctrl->app_ctx, keyword, pin_cb, ctx, line, nbytes); xfree (linebuf); + TEST_CARD_REMOVAL (ctrl, rc); return map_to_assuan_status (rc); } @@ -927,6 +964,8 @@ cmd_genkey (ASSUAN_CONTEXT ctx, char *line) return ASSUAN_Out_Of_Core; rc = app_genkey (ctrl->app_ctx, ctrl, keyno, force? 1:0, pin_cb, ctx); xfree (keyno); + + TEST_CARD_REMOVAL (ctrl, rc); return map_to_assuan_status (rc); } @@ -966,6 +1005,7 @@ cmd_random (ASSUAN_CONTEXT ctx, char *line) } xfree (buffer); + TEST_CARD_REMOVAL (ctrl, rc); return map_to_assuan_status (rc); } @@ -1010,6 +1050,8 @@ cmd_passwd (ASSUAN_CONTEXT ctx, char *line) if (rc) log_error ("command passwd failed: %s\n", gpg_strerror (rc)); xfree (chvnostr); + + TEST_CARD_REMOVAL (ctrl, rc); return map_to_assuan_status (rc); } @@ -1044,6 +1086,7 @@ cmd_checkpin (ASSUAN_CONTEXT ctx, char *line) if (rc) log_error ("app_check_pin failed: %s\n", gpg_strerror (rc)); + TEST_CARD_REMOVAL (ctrl, rc); return map_to_assuan_status (rc); } @@ -1226,7 +1269,9 @@ send_status_info (CTRL ctrl, const char *keyword, ...) } - +/* This fucntion is called by the ticker thread to check for changes + of the reader stati. It updates the reader status files and if + requested by the caller also send a signal to the caller. */ void scd_update_reader_status_file (void) { @@ -1239,10 +1284,10 @@ scd_update_reader_status_file (void) int used; unsigned int status, changed; - /* Note, that we only try to get the status, becuase it does not + /* Note, that we only try to get the status, because it does not make sense to wait here for a operation to complete. If we are - so busy working with the card, delays in the status file updated - are should be acceptable. */ + busy working with a card, delays in the status file update should + be acceptable. */ for (slot=0; (slot < DIM(last) &&!apdu_enum_reader (slot, &used)); slot++) if (used && !apdu_get_status (slot, 0, &status, &changed)) diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 88f393bb1..0fefbd960 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -936,7 +936,7 @@ ticker_thread (void *dummy_arg) sigs_ev = NULL; #endif - for (;;) + while (!shutdown_pending) { if (!time_ev) { @@ -968,6 +968,7 @@ ticker_thread (void *dummy_arg) } pth_event_free (sigs_ev, PTH_FREE_ALL); + return NULL; } #endif /*USE_GNU_PTH*/ #endif /*!HAVE_OPENSC*/ diff --git a/tests/asschk.c b/tests/asschk.c index 83a8ca5af..59c0d107a 100644 --- a/tests/asschk.c +++ b/tests/asschk.c @@ -59,10 +59,10 @@ Print VALUE. openfile - Open file FILENAME for read access and retrun the file descriptor. + Open file FILENAME for read access and return the file descriptor. createfile - Create file FILENAME, open for write access and retrun the file + Create file FILENAME, open for write access and return the file descriptor. pipeserver diff --git a/tools/ChangeLog b/tools/ChangeLog index 38b9e9cf4..6895198c5 100644 --- a/tools/ChangeLog +++ b/tools/ChangeLog @@ -1,3 +1,8 @@ +2005-02-24 Werner Koch + + * gpg-connect-agent.c: New. + * Makefile.am: Add it. + 2004-12-21 Werner Koch * gpgconf-comp.c (get_config_pathname) [DOSISH]: Detect absolute diff --git a/tools/Makefile.am b/tools/Makefile.am index 112c77e7c..5a56d65ce 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -24,15 +24,13 @@ EXTRA_DIST = Manifest watchgnupg.c \ AM_CPPFLAGS = -I$(top_srcdir)/intl -I$(top_srcdir)/common include $(top_srcdir)/am/cmacros.am -# Note, that we require GPG_ERROR_CFLAGS only because some share header files -# require that file. It is not actually used in gpgconf. -AM_CFLAGS = @GPG_ERROR_CFLAGS@ +AM_CFLAGS = $(GPG_ERROR_CFLAGS) $(LIBASSUAN_CFLAGS) sbin_SCRIPTS = addgnupghome bin_SCRIPTS = gpgsm-gencert.sh -bin_PROGRAMS = gpgconf +bin_PROGRAMS = gpgconf gpg-connect-agent if !HAVE_W32_SYSTEM bin_PROGRAMS += watchgnupg endif @@ -41,4 +39,9 @@ gpgconf_SOURCES = gpgconf.c gpgconf.h gpgconf-comp.c no-libgcrypt.c gpgconf_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a @LIBINTL@ -watchgnupg_SOURCES = watchgnupg.c +watchgnupg_SOURCES = watchgnupg.c + +gpg_connect_agent_SOURCES = gpg-connect-agent.c no-libgcrypt.c +gpg_connect_agent_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \ + $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) $(LIBINTL) + diff --git a/tools/gpg-connect-agent.c b/tools/gpg-connect-agent.c new file mode 100644 index 000000000..399a5d369 --- /dev/null +++ b/tools/gpg-connect-agent.c @@ -0,0 +1,362 @@ +/* gpg-connect-agent.c - Tool to connect to the agent. + * Copyright (C) 2005 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 "i18n.h" +#include "../common/util.h" +#include "../common/asshelp.h" + + + +/* Constants to identify the commands and options. */ +enum cmd_and_opt_values + { + aNull = 0, + oQuiet = 'q', + oVerbose = 'v', + + oNoVerbose = 500, + oHomedir + + }; + + +/* The list of commands and options. */ +static ARGPARSE_OPTS opts[] = + { + { 301, NULL, 0, N_("@\nOptions:\n ") }, + + { oVerbose, "verbose", 0, N_("verbose") }, + { oQuiet, "quiet", 0, N_("quiet") }, + + /* hidden options */ + { oNoVerbose, "no-verbose", 0, "@"}, + { oHomedir, "homedir", 2, "@" }, + {0} + }; + + +/* We keep all global options in the structure OPT. */ +struct +{ + int verbose; /* Verbosity level. */ + int quiet; /* Be extra quiet. */ + const char *homedir; /* Configuration directory name */ + +} opt; + + +/*-- local prototypes --*/ +static int read_and_print_response (assuan_context_t ctx); +static assuan_context_t start_agent (void); + + + + +/* Print usage information and and provide strings for help. */ +static const char * +my_strusage( int level ) +{ + const char *p; + + switch (level) + { + case 11: p = "gpg-connect-agent (GnuPG)"; + break; + case 13: p = VERSION; break; + case 17: p = PRINTABLE_OS_NAME; break; + case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n"); + break; + case 1: + case 40: p = _("Usage: gpg-connect-agent [options] (-h for help)"); + break; + case 41: + p = _("Syntax: gpg-connect-agent [options]\n" + "Connect to a running agent and send commands\n"); + break; + case 31: p = "\nHome: "; break; + case 32: p = opt.homedir; break; + case 33: p = "\n"; break; + + default: p = NULL; break; + } + return p; +} + + +/* Initialize the gettext system. */ +static void +i18n_init(void) +{ +#ifdef USE_SIMPLE_GETTEXT + set_gettext_file (PACKAGE_GT); +#else +# ifdef ENABLE_NLS + setlocale (LC_ALL, "" ); + bindtextdomain (PACKAGE_GT, LOCALEDIR); + textdomain (PACKAGE_GT); +# endif +#endif +} + + +/* gpg-connect-agent's entry point. */ +int +main (int argc, char **argv) +{ + ARGPARSE_ARGS pargs; + const char *fname; + int no_more_options = 0; + assuan_context_t ctx; + char *line; + size_t linesize; + int rc; + + set_strusage (my_strusage); + log_set_prefix ("gpg-connect-agent", 1); + + i18n_init(); + + opt.homedir = default_homedir (); + + /* Parse the command line. */ + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags = 1; /* Do not remove the args. */ + while (!no_more_options && optfile_parse (NULL, NULL, NULL, &pargs, opts)) + { + switch (pargs.r_opt) + { + case oQuiet: opt.quiet = 1; break; + case oVerbose: opt.verbose++; break; + case oNoVerbose: opt.verbose = 0; break; + case oHomedir: opt.homedir = pargs.r.ret_str; break; + + default: pargs.err = 2; break; + } + } + + if (log_get_errorcount (0)) + exit (2); + + fname = argc ? *argv : NULL; + + ctx = start_agent (); + line = NULL; + linesize = 0; + for (;;) + { + int n; + size_t maxlength; + + maxlength = 2048; + n = read_line (stdin, &line, &linesize, &maxlength); + if (n < 0) + { + log_error (_("error reading input: %s\n"), strerror (errno)); + exit (1); + } + if (!n) + break; /* EOF */ + if (!maxlength) + { + log_error (_("line too long - skipped\n")); + continue; + } + if (memchr (line, 0, n)) + log_info (_("line shortened due to embedded Nul character\n")); + if (line[n-1] == '\n') + line[n-1] = 0; + rc = assuan_write_line (ctx, line); + if (rc) + { + log_info (_("sending line failed: %s\n"), assuan_strerror (rc) ); + continue; + } + if (*line == '#' || !*line) + continue; /* Don't expect a response for a coment line. */ + + rc = read_and_print_response (ctx); + if (rc) + log_info (_("receiving line failed: %s\n"), assuan_strerror (rc) ); + } + + if (opt.verbose) + log_info ("closing connection to agent\n"); + + return 0; +} + + +/* Read all response lines from server and print them. Returns 0 on + success or an assuan error code. */ +static int +read_and_print_response (assuan_context_t ctx) +{ + char *line; + int linelen; + assuan_error_t rc; + + for (;;) + { + do + { + rc = assuan_read_line (ctx, &line, &linelen); + if (rc) + return rc; + } + while (*line == '#' || !linelen); + + if (linelen >= 1 + && line[0] == 'D' && line[1] == ' ') + { + fwrite (line, linelen, 1, stdout); + putchar ('\n'); + } + else if (linelen >= 1 + && line[0] == 'S' + && (line[1] == '\0' || line[1] == ' ')) + { + fwrite (line, linelen, 1, stdout); + putchar ('\n'); + } + else if (linelen >= 2 + && line[0] == 'O' && line[1] == 'K' + && (line[2] == '\0' || line[2] == ' ')) + { + fwrite (line, linelen, 1, stdout); + putchar ('\n'); + return 0; + } + else if (linelen >= 3 + && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' + && (line[3] == '\0' || line[3] == ' ')) + { + fwrite (line, linelen, 1, stdout); + putchar ('\n'); + return 0; + } + else if (linelen >= 7 + && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q' + && line[3] == 'U' && line[4] == 'I' && line[5] == 'R' + && line[6] == 'E' + && (line[7] == '\0' || line[7] == ' ')) + { + fwrite (line, linelen, 1, stdout); + putchar ('\n'); + return 0; + } + else if (linelen >= 3 + && line[0] == 'E' && line[1] == 'N' && line[2] == 'D' + && (line[3] == '\0' || line[3] == ' ')) + { + fwrite (line, linelen, 1, stdout); + putchar ('\n'); + /* Received from server, thus more responses are expected. */ + } + else + return ASSUAN_Invalid_Response; + } +} + + + + +/* Connect to teh agebnt and send the standard options. */ +static assuan_context_t +start_agent (void) +{ + int rc = 0; + char *infostr, *p; + assuan_context_t ctx; + + infostr = getenv ("GPG_AGENT_INFO"); + if (!infostr || !*infostr) + { + char *sockname; + + /* Check whether we can connect at the standard socket. */ + sockname = make_filename (opt.homedir, "S.gpg-agent", NULL); + rc = assuan_socket_connect (&ctx, sockname, 0); + xfree (sockname); + } + else + { + int prot; + int pid; + + infostr = xstrdup (infostr); + if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr) + { + log_error (_("malformed GPG_AGENT_INFO environment variable\n")); + xfree (infostr); + exit (1); + } + *p++ = 0; + pid = atoi (p); + while (*p && *p != ':') + p++; + prot = *p? atoi (p+1) : 0; + if (prot != 1) + { + log_error (_("gpg-agent protocol version %d is not supported\n"), + prot); + xfree (infostr); + exit (1); + } + + rc = assuan_socket_connect (&ctx, infostr, pid); + xfree (infostr); + } + + if (rc) + { + log_error ("can't connect to the agent: %s\n", assuan_strerror (rc)); + exit (1); + } + + if (opt.verbose) + log_info ("connection to agent established\n"); + + rc = assuan_transact (ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + { + log_error (_("error sending %s command: %s\n"), "RESET", + assuan_strerror (rc)); + exit (1); + } + + rc = send_pinentry_environment (ctx, GPG_ERR_SOURCE_DEFAULT, + NULL, NULL, NULL, NULL, NULL); + if (rc) + { + log_error (_("error sending standard options: %s\n"), gpg_strerror (rc)); + exit (1); + } + + return ctx; +} -- cgit From a22750dc1e53d4cff4163326019569ef5a780e6e Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 27 Apr 2005 12:09:21 +0000 Subject: * configure.ac: Removed OpenSC detection and options. * acinclude.m4: Ditto. * scdaemon.texi: Removed OpenSC specific options. * app-p15.c: New. Basic support for pkcs15 cards without OpenSC. There are quite a couple of things missing but at least I can use my old TCOS cards from the Aegypten-1 development for signing. * app.c (select_application): Detect pkcs15 applications. * Makefile.am (scdaemon_SOURCES): Removed card.c, card-common.h and card-p15.c because they are now obsolete. Added app-p15.c. Removed all OpenSC stuff. * command.c (do_reset, open_card, cmd_serialno, cmd_learn) (cmd_readcert, cmd_readkey, cmd_pksign, cmd_pkdecrypt): Removed all special cases for the old card.c based mechanisms. * scdaemon.c, apdu.c: Removed all special cases for OpenSC. --- ChangeLog | 5 + NEWS | 5 + acinclude.m4 | 67 -- configure.ac | 18 - doc/ChangeLog | 4 + doc/scdaemon.texi | 18 +- scd/ChangeLog | 16 + scd/Makefile.am | 27 +- scd/apdu.c | 246 +---- scd/app-common.h | 2 +- scd/app-nks.c | 4 +- scd/app-p15.c | 2844 +++++++++++++++++++++++++++++++++++++++++++++++------ scd/app.c | 6 +- scd/command.c | 223 +---- scd/scdaemon.c | 27 +- scd/scdaemon.h | 25 - 16 files changed, 2632 insertions(+), 905 deletions(-) (limited to 'scd/scdaemon.c') diff --git a/ChangeLog b/ChangeLog index 5538a4503..4f58b9198 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2005-04-27 Werner Koch + + * configure.ac: Removed OpenSC detection and options. + * acinclude.m4: Ditto. + 2005-04-21 Werner Koch Released 1.9.16. diff --git a/NEWS b/NEWS index 2c5440a9a..2d43d70ff 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,11 @@ Noteworthy changes in version 1.9.17 ------------------------------------------------- + * [scdaemon] Support for OpenSC has been removed. Instead a new and + starightforward pkcs#15 modules has been written. As of now it + does allows only signing using TCOS cards but we are going to + enhance it to match all the old capabilities. + Noteworthy changes in version 1.9.16 (2005-04-21) ------------------------------------------------- diff --git a/acinclude.m4 b/acinclude.m4 index e933e6ee0..b8af93245 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -390,70 +390,3 @@ fi -dnl AM_PATH_OPENSC([MINIMUM-VERSION, -dnl [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]]) -dnl Test for OpenSC and define OPENSC_CFLAGS and OPENSC_LIBS -dnl -AC_DEFUN([AM_PATH_OPENSC], -[ AC_ARG_WITH(opensc-prefix, - AC_HELP_STRING([--with-opensc-prefix=PFX], - [prefix where OpenSC is installed (optional)]), - opensc_config_prefix="$withval", opensc_config_prefix="") - if test x$opensc_config_prefix != x ; then - opensc_config_args="$opensc_config_args --prefix=$opensc_config_prefix" - if test x${OPENSC_CONFIG+set} != xset ; then - OPENSC_CONFIG=$opensc_config_prefix/bin/opensc-config - fi - fi - - AC_PATH_PROG(OPENSC_CONFIG, opensc-config, no) - min_opensc_version=ifelse([$1], ,0.7.0,$1) - AC_MSG_CHECKING(for OpenSC - version >= $min_opensc_version) - ok=no - if test "$OPENSC_CONFIG" != "no" ; then - req_major=`echo $min_opensc_version | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'` - req_minor=`echo $min_opensc_version | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'` - req_micro=`echo $min_opensc_version | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'` - opensc_config_version=`$OPENSC_CONFIG $opensc_config_args --version 2>/dev/null || echo 0.0.0` - major=`echo $opensc_config_version | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'` - minor=`echo $opensc_config_version | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'` - micro=`echo $opensc_config_version | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\3/'` - if test "$major" -gt "$req_major"; then - ok=yes - else - if test "$major" -eq "$req_major"; then - if test "$minor" -gt "$req_minor"; then - ok=yes - else - if test "$minor" -eq "$req_minor"; then - if test "$micro" -ge "$req_micro"; then - ok=yes - fi - fi - fi - fi - fi - fi - if test $ok = yes; then - OPENSC_CFLAGS=`$OPENSC_CONFIG $opensc_config_args --cflags` - OPENSC_LIBS=`$OPENSC_CONFIG $opensc_config_args --libs` - OPENSC_LIBS="$OPENSC_LIBS -lpcsclite -lpthread" - AC_MSG_RESULT(yes) - ifelse([$2], , :, [$2]) - else - OPENSC_CFLAGS="" - OPENSC_LIBS="" - AC_MSG_RESULT(no) - ifelse([$3], , :, [$3]) - fi - AC_SUBST(OPENSC_CFLAGS) - AC_SUBST(OPENSC_LIBS) -]) - - diff --git a/configure.ac b/configure.ac index 10f45ee92..dee0a9f09 100644 --- a/configure.ac +++ b/configure.ac @@ -38,9 +38,6 @@ NEED_LIBASSUAN_VERSION=0.6.9 NEED_KSBA_VERSION=0.9.11 -NEED_OPENSC_VERSION=0.8.0 - - PACKAGE=$PACKAGE_NAME PACKAGE_GT=${PACKAGE_NAME}2 @@ -59,7 +56,6 @@ have_gpg_error=no have_libgcrypt=no have_libassuan=no have_ksba=no -have_opensc=no have_pth=no GNUPG_BUILD_PROGRAM(gpg, no) @@ -503,16 +499,6 @@ AC_PATH_PROG(SHRED, shred, /usr/bin/shred) AC_DEFINE_UNQUOTED(SHRED, "${SHRED}", [defines the filename of the shred program]) -# -# OpenSC is needed by the SCdaemon - if it is not availbale we can only -# build a limited SCdaemon -# -AM_PATH_OPENSC("$NEED_OPENSC_VERSION",have_opensc=yes,have_opensc=no) -if test $have_opensc = yes; then - AC_DEFINE(HAVE_OPENSC,1, - [defined if the OpenSC library is available]) -fi -AM_CONDITIONAL(HAVE_OPENSC, test "$have_opensc" = "yes") # # Check whether the (highly desirable) GNU Pth library is available @@ -1052,10 +1038,6 @@ if test "$build_scdaemon" = "yes"; then tmp=", " missing_pth=yes fi - if test $have_opensc = no; then - build_scdaemon_extra="${build_scdaemon_extra}${tmp}no pkcs#15" - tmp=", " - fi if test -n "$build_scdaemon_extra"; then build_scdaemon_extra="(${build_scdaemon_extra})" fi diff --git a/doc/ChangeLog b/doc/ChangeLog index 4ad8b18c8..093e04fa8 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,7 @@ +2005-04-27 Werner Koch + + * scdaemon.texi: Removed OpenSC specific options. + 2005-04-20 Werner Koch * gpg-agent.texi (Agent Configuration): New section. diff --git a/doc/scdaemon.texi b/doc/scdaemon.texi index cb165da35..971234e52 100644 --- a/doc/scdaemon.texi +++ b/doc/scdaemon.texi @@ -1,4 +1,4 @@ -@c Copyright (C) 2002 Free Software Foundation, Inc. +g@c Copyright (C) 2002 Free Software Foundation, Inc. @c This is part of the GnuPG manual. @c For copying conditions, see the file gnupg.texi. @@ -157,10 +157,6 @@ When running in server mode, wait @var{n} seconds before entering the actual processing loop and print the pid. This gives time to attach a debugger. -@item --debug-sc @var{n} -@opindex debug-sc -Set the debug level of the OpenSC library to @var{n}. - @item --no-detach @opindex no-detach Don't detach the process from the console. This is manly usefule for @@ -172,10 +168,9 @@ Append all logging output to @var{file}. This is very helpful in seeing what the agent actually does. @item --reader-port @var{number} -When the program has been build without OpenSC support, this option must -be used to specify the port of the card terminal. A value of 0 refers -to the first serial device; add 32768 to access USB devices. The -default is 32768 (first USB device). +This option may be used to specify the port of the card terminal. A +value of 0 refers to the first serial device; add 32768 to access USB +devices. The default is 32768 (first USB device). @item --ctapi-driver @var{library} Use @var{library} to access the smartcard reader. The current default @@ -245,9 +240,8 @@ the German signature law and its bylaws (SigG and SigV). @node PKCS#15 Card @subsection The PKCS#15 card application ``p15'' -This is common fraqmework for smart card applications; support is only -available if compiled with support for the OpenSC library. It is used -by @command{gpgsm}. +This is common fraqmework for smart card applications. It is used by +@command{gpgsm}. diff --git a/scd/ChangeLog b/scd/ChangeLog index c5a1062b8..4ce4c656d 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,19 @@ +2005-04-27 Werner Koch + + Removal of the old OpenSC based code. + + * app-p15.c: New. Basic support for pkcs15 cards without OpenSC. + There are quite a couple of things missing but at least I can use + my old TCOS cards from the Aegypten-1 development for signing. + * app.c (select_application): Detect pkcs15 applications. + * Makefile.am (scdaemon_SOURCES): Removed card.c, card-common.h + and card-p15.c because they are now obsolete. Added app-p15.c. + Removed all OpenSC stuff. + * command.c (do_reset, open_card, cmd_serialno, cmd_learn) + (cmd_readcert, cmd_readkey, cmd_pksign, cmd_pkdecrypt): Removed + all special cases for the old card.c based mechanisms. + * scdaemon.c, apdu.c: Removed all special cases for OpenSC. + 2005-04-20 Werner Koch * command.c: Use GPG_ERR_LOCKED instead of EBUSY. diff --git a/scd/Makefile.am b/scd/Makefile.am index fba006c5a..c3688da5f 100644 --- a/scd/Makefile.am +++ b/scd/Makefile.am @@ -1,4 +1,4 @@ -# Copyright (C) 2002, 2003 Free Software Foundation, Inc. +# Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc. # # This file is part of GnuPG. # @@ -27,24 +27,15 @@ AM_CPPFLAGS = -I$(top_srcdir)/intl -I$(top_srcdir)/common include $(top_srcdir)/am/cmacros.am -# avoid linking against Pth if we are using OpenSC. -if HAVE_OPENSC -pth_libs = -else -pth_libs = $(PTH_LIBS) -endif - -AM_CFLAGS = $(OPENSC_CFLAGS) $(LIBGCRYPT_CFLAGS) \ - $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) $(PTH_CFLAGS) +AM_CFLAGS = $(LIBGCRYPT_CFLAGS) \ + $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) $(PTH_CFLAGS) -card_apps = app-openpgp.c app-nks.c app-dinsig.c +card_apps = app-openpgp.c app-nks.c app-dinsig.c app-p15.c scdaemon_SOURCES = \ scdaemon.c scdaemon.h \ - command.c card.c \ - card-common.h \ - card-p15.c \ + command.c \ apdu.c apdu.h \ ccid-driver.c ccid-driver.h \ iso7816.c iso7816.h \ @@ -53,8 +44,8 @@ scdaemon_SOURCES = \ scdaemon_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \ - $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(pth_libs) $(LIBASSUAN_LIBS) \ - $(LIBUSB_LIBS) $(OPENSC_LIBS) -lgpg-error $(LIBINTL) $(DL_LIBS) + $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(PTH_LIBS) $(LIBASSUAN_LIBS) \ + $(LIBUSB_LIBS) -lgpg-error $(LIBINTL) $(DL_LIBS) sc_copykeys_SOURCES = \ sc-copykeys.c scdaemon.h \ @@ -68,8 +59,8 @@ sc_copykeys_SOURCES = \ sc_copykeys_LDADD = \ ../jnlib/libjnlib.a ../common/libcommon.a \ ../common/libsimple-pwquery.a \ - $(LIBGCRYPT_LIBS) $(pth_libs) $(KSBA_LIBS) $(LIBASSUAN_LIBS) \ - $(LIBUSB_LIBS) $(OPENSC_LIBS) \ + $(LIBGCRYPT_LIBS) $(PTH_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) \ + $(LIBUSB_LIBS) \ -lgpg-error @LIBINTL@ @DL_LIBS@ pcsc_wrapper_SOURCES = pcsc-wrapper.c diff --git a/scd/apdu.c b/scd/apdu.c index 33b0802c1..d23a4adc9 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -32,12 +32,7 @@ # include # include #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. */ @@ -119,12 +114,6 @@ struct reader_table_s { pid_t pid; #endif /*NEED_PCSC_WRAPPER*/ } pcsc; -#ifdef HAVE_OPENSC - struct { - struct sc_context *ctx; - struct sc_card *scard; - } osc; -#endif /*HAVE_OPENSC*/ #ifdef USE_G10CODE_RAPDU struct { rapdu_t handle; @@ -1770,224 +1759,6 @@ open_ccid_reader (const char *portstr) #endif /* HAVE_LIBUSB */ - -#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 -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) -{ - 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); - - 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 /* @@ -2130,7 +1901,7 @@ my_rapdu_get_status (int slot, unsigned int *status) /* 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. */ + set to BUFLEN. Returns: APDU error code. */ static int my_rapdu_send_apdu (int slot, unsigned char *apdu, size_t apdulen, unsigned char *buffer, size_t *buflen) @@ -2325,8 +2096,7 @@ unlock_slot (int slot) /* 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 PC/SC the first listed reader). If - OpenSC support is compiled in, we first try to use OpenSC. */ + the first USB reader. For PC/SC the first listed reader). */ int apdu_open_reader (const char *portstr) { @@ -2352,16 +2122,6 @@ apdu_open_reader (const char *portstr) #endif /* HAVE_LIBUSB */ -#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; diff --git a/scd/app-common.h b/scd/app-common.h index 4a2adaa54..594f93850 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -175,7 +175,7 @@ int app_select_nks (app_t app); int app_select_dinsig (app_t app); /*-- app-p15.c --*/ -int app_select_p15 (app_t app); +gpg_error_t app_select_p15 (app_t app); #endif diff --git a/scd/app-nks.c b/scd/app-nks.c index e69b59879..f14b67972 100644 --- a/scd/app-nks.c +++ b/scd/app-nks.c @@ -305,7 +305,7 @@ verify_pin (app_t app, void *pincb_arg) { /* Note that force_chv1 is never set but we do it here anyway so - that other applications may euse this function. For example it + that other applications may reuse this function. For example it makes sense to set force_chv1 for German signature law cards. NKS is very similar to the DINSIG draft standard. */ if (!app->did_chv1 || app->force_chv1 ) @@ -444,7 +444,7 @@ do_decipher (app_t app, const char *keyidstr, { static const unsigned char mse_parm[] = { 0x80, 1, 0x10, /* Select algorithm RSA. */ - 0x84, 1, 0x81 /* Select locak secret key 1 for descryption. */ + 0x84, 1, 0x81 /* Select local secret key 1 for decryption. */ }; int rc, i; int fid; diff --git a/scd/app-p15.c b/scd/app-p15.c index af2eed465..7a92da10b 100644 --- a/scd/app-p15.c +++ b/scd/app-p15.c @@ -1,5 +1,5 @@ /* app-p15.c - The pkcs#15 card application. - * Copyright (C) 2004 Free Software Foundation, Inc. + * Copyright (C) 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -31,14 +31,232 @@ #include "iso7816.h" #include "app-common.h" #include "tlv.h" +#include "apdu.h" /* fixme: we should move the card detection to a + separate file */ + +/* Types of cards we know and which needs special treatment. */ +typedef enum + { + CARD_TYPE_UNKNOWN, + CARD_TYPE_TCOS, + CARD_TYPE_MICARDO + } card_type_t; + +/* A list card types with ATRs noticed with these cards. */ +static struct +{ + size_t atrlen; + unsigned char *atr; + card_type_t type; +} card_atr_list[] = { + { 19, "\x3B\xBA\x13\x00\x81\x31\x86\x5D\x00\x64\x05\x0A\x02\x01\x31\x80" + "\x90\x00\x8B", + CARD_TYPE_TCOS }, /* SLE44 */ + { 19, "\x3B\xBA\x14\x00\x81\x31\x86\x5D\x00\x64\x05\x14\x02\x02\x31\x80" + "\x90\x00\x91", + CARD_TYPE_TCOS }, /* SLE66S */ + { 19, "\x3B\xBA\x96\x00\x81\x31\x86\x5D\x00\x64\x05\x60\x02\x03\x31\x80" + "\x90\x00\x66", + CARD_TYPE_TCOS }, /* SLE66P */ + { 27, "\x3B\xFF\x94\x00\xFF\x80\xB1\xFE\x45\x1F\x03\x00\x68\xD2\x76\x00" + "\x00\x28\xFF\x05\x1E\x31\x80\x00\x90\x00\x23", + CARD_TYPE_MICARDO }, /* German BMI card */ + { 26, "\x3B\xFE\x94\x00\xFF\x80\xB1\xFA\x45\x1F\x03\x45\x73\x74\x45\x49" + "\x44\x20\x76\x65\x72\x20\x31\x2E\x30\x43", + CARD_TYPE_MICARDO }, /* EstEID (Estonian Big Brother card) */ + { 0 } +}; + + +/* The Pin Types as defined in pkcs#15 v1.1 */ +typedef enum + { + PIN_TYPE_BCD = 0, + PIN_TYPE_ASCII_NUMERIC = 1, + PIN_TYPE_UTF8 = 2, + PIN_TYPE_HALF_NIBBLE_BCD = 3, + PIN_TYPE_ISO9564_1 = 4 + } pin_type_t; + + +/* A bit array with for the key usage flags from the + commonKeyAttributes. */ +struct keyusage_flags_s +{ + unsigned int encrypt: 1; + unsigned int decrypt: 1; + unsigned int sign: 1; + unsigned int sign_recover: 1; + unsigned int wrap: 1; + unsigned int unwrap: 1; + unsigned int verify: 1; + unsigned int verify_recover: 1; + unsigned int derive: 1; + unsigned int non_repudiation: 1; +}; +typedef struct keyusage_flags_s keyusage_flags_t; + + + +/* This is an object to store information about a Certificate + Directory File (CDF) in a format suitable for further processing by + us. To keep memory management, simple we use a linked list of + items; i.e. one such object represents one certificate and the list + the entire CDF. */ +struct cdf_object_s +{ + /* Link to next item when used in a linked list. */ + struct cdf_object_s *next; + + /* Length and allocated buffer with the Id of this object. */ + size_t objidlen; + unsigned char *objid; + + /* To avoid reading a certificate more than once, we cache it in an + allocated memory IMAGE of IMAGELEN. */ + size_t imagelen; + unsigned char *image; + + /* Set to true if a length and offset is available. */ + int have_off; + /* The offset and length of the object. They are only valid if + HAVE_OFF is true and set to 0 if HAVE_OFF is false. */ + unsigned long off, len; + + /* The length of the path as given in the CDF and the path itself. + path[0] is the top DF (usually 0x3f00). The path will never be + empty. */ + size_t pathlen; + unsigned short path[1]; +}; +typedef struct cdf_object_s *cdf_object_t; + + +/* This is an object to store information about a Private Key + Directory File (PrKDF) in a format suitable for further processing + by us. To keep memory management, simple we use a linked list of + items; i.e. one such object represents one certificate and the list + the entire PrKDF. */ +struct prkdf_object_s +{ + /* Link to next item when used in a linked list. */ + struct prkdf_object_s *next; + + /* Length and allocated buffer with the Id of this object. */ + size_t objidlen; + unsigned char *objid; + + /* Length and allocated buffer with the authId of this object or + NULL if no authID is known. */ + size_t authidlen; + unsigned char *authid; + + /* The key's usage flags. */ + keyusage_flags_t usageflags; + + /* The keyReference and a flag telling whether it is valid. */ + unsigned long key_reference; + int key_reference_valid; + + /* Set to true if a length and offset is available. */ + int have_off; + /* The offset and length of the object. They are only valid if + HAVE_OFF is true and set to 0 if HAVE_OFF is false. */ + unsigned long off, len; + + /* The length of the path as given in the PrKDF and the path itself. + path[0] is the top DF (usually 0x3f00). */ + size_t pathlen; + unsigned short path[1]; +}; +typedef struct prkdf_object_s *prkdf_object_t; + + +/* This is an object to store information about a Authentication + Object Directory File (AODF) in a format suitable for further + processing by us. To keep memory management, simple we use a linked + list of items; i.e. one such object represents one authentication + object and the list the entire AOKDF. */ +struct aodf_object_s +{ + /* Link to next item when used in a linked list. */ + struct aodf_object_s *next; + + /* Length and allocated buffer with the Id of this object. */ + size_t objidlen; + unsigned char *objid; + + /* Length and allocated buffer with the authId of this object or + NULL if no authID is known. */ + size_t authidlen; + unsigned char *authid; + + /* The PIN Flags. */ + struct + { + unsigned int case_sensitive: 1; + unsigned int local: 1; + unsigned int change_disabled: 1; + unsigned int unblock_disabled: 1; + unsigned int initialized: 1; + unsigned int needs_padding: 1; + unsigned int unblocking_pin: 1; + unsigned int so_pin: 1; + unsigned int disable_allowed: 1; + unsigned int integrity_protected: 1; + unsigned int confidentiality_protected: 1; + unsigned int exchange_ref_data: 1; + } pinflags; + + /* The PIN Type. */ + pin_type_t pintype; + + /* The minimum length of a PIN. */ + unsigned long min_length; + + /* The stored length of a PIN. */ + unsigned long stored_length; + + /* The maximum length of a PIN and a flag telling whether it is valid. */ + unsigned long max_length; + int max_length_valid; + + /* The pinReference and a flag telling whether it is valid. */ + unsigned long pin_reference; + int pin_reference_valid; + + /* The padChar and a flag telling whether it is valid. */ + char pad_char; + int pad_char_valid; + + + /* Set to true if a length and offset is available. */ + int have_off; + /* The offset and length of the object. They are only valid if + HAVE_OFF is true and set to 0 if HAVE_OFF is false. */ + unsigned long off, len; + + /* The length of the path as given in the Aodf and the path itself. + path[0] is the top DF (usually 0x3f00). PATH is optional and thus + may be NULL. Malloced.*/ + size_t pathlen; + unsigned short *path; +}; +typedef struct aodf_object_s *aodf_object_t; /* Context local to this application. */ struct app_local_s { - unsigned short home_df; /* The home DF. Note, that we don't yet - support a multilevel hierachy. Thus we - assume this is directly below the MF. */ + /* The home DF. Note, that we don't yet support a multilevel + hierachy. Thus we assume this is directly below the MF. */ + unsigned short home_df; + + /* The type of the card. */ + card_type_t card_type; + + /* Structure with the EFIDs of the objects described in the ODF + file. */ struct { unsigned short private_keys; @@ -52,16 +270,104 @@ struct app_local_s unsigned short auth_objects; } odf; + /* Information on all certificates. */ + cdf_object_t certificate_info; + /* Information on all trusted certificates. */ + cdf_object_t trusted_certificate_info; + /* Information on all useful certificates. */ + cdf_object_t useful_certificate_info; + + /* Information on all private keys. */ + prkdf_object_t private_key_info; + + /* Information on all authentication objects. */ + aodf_object_t auth_object_info; }; +/*** Local prototypes. ***/ +static gpg_error_t readcert_by_cdf (app_t app, cdf_object_t cdf, + unsigned char **r_cert, size_t *r_certlen); + + + +/* Release the CDF object A */ +static void +release_cdflist (cdf_object_t a) +{ + while (a) + { + cdf_object_t tmp = a->next; + xfree (a->image); + xfree (a->objid); + xfree (a); + a = tmp; + } +} + +/* Release the PrKDF object A. */ +static void +release_prkdflist (prkdf_object_t a) +{ + while (a) + { + prkdf_object_t tmp = a->next; + xfree (a->objid); + xfree (a->authid); + xfree (a); + a = tmp; + } +} + +/* Release just one aodf object. */ +void +release_aodf_object (aodf_object_t a) +{ + if (a) + { + xfree (a->objid); + xfree (a->authid); + xfree (a->path); + xfree (a); + } +} + +/* Release the AODF list A. */ +static void +release_aodflist (aodf_object_t a) +{ + while (a) + { + aodf_object_t tmp = a->next; + release_aodf_object (a); + a = tmp; + } +} + + +/* Release all local resources. */ +static void +do_deinit (app_t app) +{ + if (app && app->app_local) + { + release_cdflist (app->app_local->certificate_info); + release_cdflist (app->app_local->trusted_certificate_info); + release_cdflist (app->app_local->useful_certificate_info); + release_prkdflist (app->app_local->private_key_info); + release_aodflist (app->app_local->auth_object_info); + xfree (app->app_local); + app->app_local = NULL; + } +} + -/* Do a select and a read for the file with EFID. EFID is a +/* Do a select and a read for the file with EFID. EFID_DESC is a desctription of the EF to be used with error messages. On success BUFFER and BUFLEN contain the entire content of the EF. The caller - must free BUFFER but only on success. */ + must free BUFFER only on success. */ static gpg_error_t select_and_read_binary (int slot, unsigned short efid, const char *efid_desc, unsigned char **buffer, size_t *buflen) @@ -86,10 +392,151 @@ select_and_read_binary (int slot, unsigned short efid, const char *efid_desc, } +/* This function calls select file to read a file suing a complete + path which may or may not start at the master file (MF). */ +static gpg_error_t +select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen) +{ + gpg_error_t err; + int i, j; + + /* FIXME: Need code to remember the last PATH so that we can decide + what select commands to send in case the path does not start off + with 3F00. We might also want to use direct path selection if + supported by the card. */ + if (pathlen && *path != 0x3f00 ) + log_debug ("WARNING: relative path selection not yet implemented\n"); + + for (i=0; i < pathlen; i++) + { + err = iso7816_select_file (app->slot, path[i], + !(i+1 == pathlen), NULL, NULL); + if (err) + { + log_error ("error selecting part %d from path ", i); + for (j=0; j < pathlen; j++) + log_printf ("%04hX", path[j]); + log_printf (": %s\n", gpg_strerror (err)); + return err; + } + } + return 0; +} + +/* Parse a cert Id string (or a key Id string) and return the binary + object Id string in a newly allocated buffer stored at R_OBJID and + R_OBJIDLEN. On Error NULL will be stored there and an error code + returned. On success caller needs to free the buffer at R_OBJID. */ +static gpg_error_t +parse_certid (app_t app, const char *certid, + unsigned char **r_objid, size_t *r_objidlen) +{ + char tmpbuf[10]; + const char *s; + size_t objidlen; + unsigned char *objid; + int i; + + *r_objid = NULL; + *r_objidlen = 0; + + if (app->app_local->home_df) + sprintf (tmpbuf, "P15-%04hX.", (app->app_local->home_df & 0xffff)); + else + strcpy (tmpbuf, "P15."); + if (strncmp (certid, tmpbuf, strlen (tmpbuf)) ) + { + if (!strncmp (certid, "P15.", 4) + || (!strncmp (certid, "P15-", 4) + && hexdigitp (certid+4) + && hexdigitp (certid+5) + && hexdigitp (certid+6) + && hexdigitp (certid+7) + && certid[8] == '.')) + return gpg_error (GPG_ERR_NOT_FOUND); + return gpg_error (GPG_ERR_INV_ID); + } + certid += strlen (tmpbuf); + + for (s=certid, objidlen=0; hexdigitp (s); s++, objidlen++) + ; + if (*s || !objidlen || (objidlen%2)) + return gpg_error (GPG_ERR_INV_ID); + objidlen /= 2; + objid = xtrymalloc (objidlen); + if (!objid) + return gpg_error_from_errno (errno); + for (s=certid, i=0; i < objidlen; i++, s+=2) + objid[i] = xtoi_2 (s); + *r_objid = objid; + *r_objidlen = objidlen; + return 0; +} + + +/* Find a certificate object by the certificate ID CERTID and store a + pointer to it at R_CDF. */ +static gpg_error_t +cdf_object_from_certid (app_t app, const char *certid, cdf_object_t *r_cdf) +{ + gpg_error_t err; + size_t objidlen; + unsigned char *objid; + cdf_object_t cdf; + + err = parse_certid (app, certid, &objid, &objidlen); + if (err) + return err; + + for (cdf = app->app_local->certificate_info; cdf; cdf = cdf->next) + if (cdf->objidlen == objidlen && !memcmp (cdf->objid, objid, objidlen)) + break; + if (!cdf) + for (cdf = app->app_local->trusted_certificate_info; cdf; cdf = cdf->next) + if (cdf->objidlen == objidlen && !memcmp (cdf->objid, objid, objidlen)) + break; + if (!cdf) + for (cdf = app->app_local->useful_certificate_info; cdf; cdf = cdf->next) + if (cdf->objidlen == objidlen && !memcmp (cdf->objid, objid, objidlen)) + break; + xfree (objid); + if (!cdf) + return gpg_error (GPG_ERR_NOT_FOUND); + *r_cdf = cdf; + return 0; +} + + +/* Find a private key object by the key Id string KEYIDSTR and store a + pointer to it at R_PRKDF. */ +static gpg_error_t +prkdf_object_from_keyidstr (app_t app, const char *keyidstr, + prkdf_object_t *r_prkdf) +{ + gpg_error_t err; + size_t objidlen; + unsigned char *objid; + prkdf_object_t prkdf; + + err = parse_certid (app, keyidstr, &objid, &objidlen); + if (err) + return err; + + for (prkdf = app->app_local->private_key_info; prkdf; prkdf = prkdf->next) + if (prkdf->objidlen == objidlen && !memcmp (prkdf->objid, objid, objidlen)) + break; + xfree (objid); + if (!prkdf) + return gpg_error (GPG_ERR_NOT_FOUND); + *r_prkdf = prkdf; + return 0; +} + + /* Read and parse the Object Directory File and store away the - pointers. + pointers. ODF_FID shall contain the FID of the ODF. Example of such a file: @@ -99,32 +546,49 @@ select_and_read_binary (int slot, unsigned short efid, const char *efid_desc, A7 06 30 04 04 02 60 37 = DataObjects A8 06 30 04 04 02 60 38 = AuthObjects - These are all PathOrObjects using the path CHOICE. The paths are - octet strings of length 2. Using this Path CHOICE is recommended, - so we only implement that for now. + These are all PathOrObjects using the path CHOICE element. The + paths are octet strings of length 2. Using this Path CHOICE + element is recommended, so we only implement that for now. */ static gpg_error_t -read_ef_odf (app_t app) +read_ef_odf (app_t app, unsigned short odf_fid) { gpg_error_t err; unsigned char *buffer, *p; size_t buflen; unsigned short value; + size_t offset; - err = select_and_read_binary (app->slot, 0x5031, "ODF", &buffer, &buflen); + err = select_and_read_binary (app->slot, odf_fid, "ODF", &buffer, &buflen); if (err) return err; - if (len < 8) + if (buflen < 8) { log_error ("error: ODF too short\n"); xfree (buffer); return gpg_error (GPG_ERR_INV_OBJ); } - for (p=buffer; buflen >= 8; p += 8, buflen -= 8) + p = buffer; + while (buflen && *p && *p != 0xff) { - if ( (p[0] & 0xf0) != 0xA0 - || memcmp (p+1, "\x06\x30\x04\x04\x02", 5) ) + if ( buflen >= 8 + && (p[0] & 0xf0) == 0xA0 + && !memcmp (p+1, "\x06\x30\x04\x04\x02", 5) ) + { + offset = 6; + } + else if ( buflen >= 12 + && (p[0] & 0xf0) == 0xA0 + && !memcmp (p+1, "\x0a\x30\x08\x04\x06\x3F\x00\x50\x15", 9) + && app->app_local->home_df == 0x5015 ) + { + /* This format using a full path is used by a self-created + test card of mine. I have not checked whether this is + legal. We assume a home DF of 0x5015 here. */ + offset = 10; + } + else { log_error ("ODF format is not supported by us\n"); xfree (buffer); @@ -145,10 +609,10 @@ read_ef_odf (app_t app) } if (value) { - log_error ("duplicate object type %d in ODF ignored\n",(p[0)&0x0f)); + log_error ("duplicate object type %d in ODF ignored\n",(p[0]&0x0f)); continue; } - value = ((p[6] << 8) | p[7]); + value = ((p[offset] << 8) | p[offset+1]); switch ((p[0] & 0x0f)) { case 0: app->app_local->odf.private_keys = value; break; @@ -161,8 +625,14 @@ read_ef_odf (app_t app) case 7: app->app_local->odf.data_objects = value; break; case 8: app->app_local->odf.auth_objects = value; break; default: - log_error ("unknown object type %d in ODF ignored\n", (p[0)&0x0f)); + log_error ("unknown object type %d in ODF ignored\n", (p[0]&0x0f)); } + offset += 2; + + if (buflen < offset) + break; + p += offset; + buflen -= offset; } if (buflen) @@ -173,6 +643,70 @@ read_ef_odf (app_t app) } +/* Parse the BIT STRING with the keyUsageFlags from teh + CommonKeyAttributes. */ +static gpg_error_t +parse_keyusage_flags (const unsigned char *der, size_t derlen, + keyusage_flags_t *usageflags) +{ + unsigned int bits, mask; + int i, unused, full; + + memset (usageflags, 0, sizeof *usageflags); + if (!derlen) + return gpg_error (GPG_ERR_INV_OBJ); + + unused = *der++; derlen--; + if ((!derlen && unused) || unused/8 > derlen) + return gpg_error (GPG_ERR_ENCODING_PROBLEM); + full = derlen - (unused+7)/8; + unused %= 8; + mask = 0; + for (i=1; unused; i <<= 1, unused--) + mask |= i; + + /* First octet */ + if (derlen) + { + bits = *der++; derlen--; + if (full) + full--; + else + { + bits &= ~mask; + mask = 0; + } + } + else + bits = 0; + if ((bits & 0x80)) usageflags->encrypt = 1; + if ((bits & 0x40)) usageflags->decrypt = 1; + if ((bits & 0x20)) usageflags->sign = 1; + if ((bits & 0x10)) usageflags->sign_recover = 1; + if ((bits & 0x08)) usageflags->wrap = 1; + if ((bits & 0x04)) usageflags->unwrap = 1; + if ((bits & 0x02)) usageflags->verify = 1; + if ((bits & 0x01)) usageflags->verify_recover = 1; + + /* Second octet. */ + if (derlen) + { + bits = *der++; derlen--; + if (full) + full--; + else + { + bits &= ~mask; + mask = 0; + } + } + else + bits = 0; + if ((bits & 0x80)) usageflags->derive = 1; + if ((bits & 0x40)) usageflags->non_repudiation = 1; + + return 0; +} /* Read and parse the Private Key Directory Files. */ /* @@ -195,135 +729,77 @@ read_ef_odf (app_t app) 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ -*/ -static gpg_error_t -read_ef_prkdf (app_t app) -{ - - -} - -/* Read and parse the Public Key Directory Files. */ -static gpg_error_t -read_ef_pukdf (app_t app) -{ - - -} - - -/* Read and parse the Certificate Directory Files. */ -/* - -6035 (certificates) - -30 2A 30 15 0C 0C 43 5F 58 35 30 39 2E 43 48 2E 0*0...C_X509.CH. -44 53 03 02 06 40 04 01 0A 30 03 04 01 01 A1 0C DS...@...0...... -30 0A 30 08 04 06 3F 00 40 16 C0 00 30 2A 30 15 0.0...?.@...0*0. -0C 0C 43 5F 58 35 30 39 2E 43 48 2E 4B 45 03 02 ..C_X509.CH.KE.. -06 40 04 01 0A 30 03 04 01 0C A1 0C 30 0A 30 08 .@...0......0.0. -04 06 3F 00 40 16 C2 00 30 2B 30 16 0C 0D 43 5F ..?.@...0+0...C_ -58 35 30 39 2E 43 48 2E 41 55 54 03 02 06 40 04 X509.CH.AUT...@. -01 0A 30 03 04 01 0D A1 0C 30 0A 30 08 04 06 3F ..0......0.0...? -00 40 16 C5 00 30 2E 30 19 0C 10 43 5F 58 35 30 .@...0.0...C_X50 -39 2E 43 48 2E 44 53 2D 53 50 58 03 02 06 40 04 9.CH.DS-SPX...@. -01 0A 30 03 04 01 02 A1 0C 30 0A 30 08 04 06 3F ..0......0.0...? -00 40 16 C1 20 00 00 00 00 00 00 00 00 00 00 00 .@.. ........... -00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ -00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ -00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ -00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ - - 0 42: SEQUENCE { - 2 21: SEQUENCE { -- commonObjectAttributes - 4 12: UTF8String 'C_X509.CH.DS' - 18 2: BIT STRING 6 unused bits - : '10'B (bit 1) - 22 1: OCTET STRING 0A - : } - 25 3: SEQUENCE { -- commonCertificateAttributes - 27 1: OCTET STRING 01 - : } - 30 12: [1] { -- certAttributes - 32 10: SEQUENCE { - 34 8: SEQUENCE { - 36 6: OCTET STRING 3F 00 40 16 C0 00 - : } - : } - : } - : } - - - -6036 (trustedcertificates) - -30 35 30 06 03 02 00 00 04 00 30 16 04 14 2D 36 050.......0...-6 -33 39 33 33 39 34 30 33 39 37 37 36 34 30 31 32 3933940397764012 -31 36 A1 13 30 11 30 0F 04 06 3F 00 40 16 C7 08 16..0.0...?.@... -02 01 00 80 02 02 29 30 35 30 06 03 02 00 00 04 ......)050...... -00 30 16 04 14 2D 34 30 31 39 30 35 32 37 32 36 .0...-4019052726 -38 30 31 36 39 33 34 39 32 A1 13 30 11 30 0F 04 801693492..0.0.. -06 3F 00 40 16 C7 0E 02 01 00 80 02 04 12 30 34 .?.@..........04 -30 06 03 02 00 00 04 00 30 15 04 13 37 39 36 33 0.......0...7963 -32 38 33 36 35 30 37 36 36 34 38 32 39 36 30 A1 283650766482960. -13 30 11 30 0F 04 06 3F 00 40 16 C0 08 02 01 00 .0.0...?.@...... -80 02 04 11 00 00 00 00 00 00 00 00 00 00 00 00 ................ -00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ - - 0 53: SEQUENCE { - 2 6: SEQUENCE { - 4 2: BIT STRING - : '00000000'B - : Error: Spurious zero bits in bitstring. - 8 0: OCTET STRING - : Error: Object has zero length. - : } - 10 22: SEQUENCE { - 12 20: OCTET STRING '-6393394039776401216' - : } - 34 19: [1] { - 36 17: SEQUENCE { - 38 15: SEQUENCE { - 40 6: OCTET STRING 3F 00 40 16 C7 08 - 48 1: INTEGER 0 -- index - 51 2: [0] 02 29 -- length - : } - : } - : } - : } + 0 30 51: SEQUENCE { + 2 30 17: SEQUENCE { -- commonObjectAttributes + 4 0C 8: UTF8String 'SK.CH.DS' + 14 03 2: BIT STRING 6 unused bits + : '01'B (bit 0) + 18 04 1: OCTET STRING --authid + : 07 + : } + 21 30 12: SEQUENCE { -- commonKeyAttributes + 23 04 1: OCTET STRING + : 01 + 26 03 3: BIT STRING 6 unused bits + : '1000000000'B (bit 9) + 31 02 2: INTEGER 80 -- keyReference (optional) + : } + 35 A1 16: [1] { -- keyAttributes + 37 30 14: SEQUENCE { -- privateRSAKeyAttributes + 39 30 8: SEQUENCE { -- objectValue + 41 04 6: OCTET STRING --path + : 3F 00 40 16 00 50 + : } + 49 02 2: INTEGER 1024 -- modulus + : } + : } + : } */ static gpg_error_t -read_ef_cdf (app_t app) +read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result) { gpg_error_t err; unsigned char *buffer = NULL; size_t buflen; - unsigned short value; - unsigned short fid; const unsigned char *p; size_t n, objlen, hdrlen; int class, tag, constructed, ndef; + prkdf_object_t prkdflist = NULL; + int i; - fid = app->app_local->odf.certificates; if (!fid) - return 0; /* No certificates. */ + return gpg_error (GPG_ERR_NO_DATA); /* No private keys. */ - err = select_and_read_binary (app->slot, fid, "CDF", &buffer, &buflen); + err = select_and_read_binary (app->slot, fid, "PrKDF", &buffer, &buflen); if (err) return err; p = buffer; n = buflen; + /* FIXME: This shares a LOT of code with read_ef_cdf! */ + /* Loop over the records. We stop as soon as we detect a new record - starting with 0x00 or 0xff as these values are commonly used to pad - the the read datablocks and are no valid ASN.1 encoding. */ - while (n && *p && *p == 0xff) + starting with 0x00 or 0xff as these values are commonly used to + pad data blocks and are no valid ASN.1 encoding. */ + while (n && *p && *p != 0xff) { const unsigned char *pp; size_t nn; + int where; + const char *errstr = NULL; + prkdf_object_t prkdf = NULL; + unsigned long ul; + const unsigned char *objid; + size_t objidlen; + const unsigned char *authid = NULL; + size_t authidlen = 0; + keyusage_flags_t usageflags; + unsigned long key_reference = 0; + int key_reference_valid = 0; + const char *s; err = parse_ber_header (&p, &n, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); @@ -331,7 +807,7 @@ read_ef_cdf (app_t app) err = gpg_error (GPG_ERR_INV_OBJ); if (err) { - log_error ("error parsing CDF record: %s\n", gpg_strerror (err)); + log_error ("error parsing PrKDF record: %s\n", gpg_strerror (err)); goto leave; } pp = p; @@ -339,144 +815,1299 @@ read_ef_cdf (app_t app) p += objlen; n -= objlen; - /* Skip the commonObjectAttributes. */ + /* Parse the commonObjectAttributes. */ + where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || tag != TAG_SEQUENCE)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) - { - log_error ("error parsing CDF record: %s - skipped\n", - gpg_strerror (err)); - continue; - } - pp += objlen; - nn -= objlen; - - /* Skip the commonCertificateAttributes. */ + goto parse_error; + { + const unsigned char *ppp = pp; + size_t nnn = objlen; + + pp += objlen; + nn -= objlen; + + /* Search the optional AuthId. We need to skip the optional + Label (UTF8STRING) and the optional CommonObjectFlags + (BITSTRING). */ + where = __LINE__; + err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nnn || class != CLASS_UNIVERSAL)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (gpg_err_code (err) == GPG_ERR_EOF) + goto no_authid; + if (err) + goto parse_error; + if (tag == TAG_UTF8_STRING) + { + ppp += objlen; /* Skip the Label. */ + nnn -= objlen; + + where = __LINE__; + err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nnn || class != CLASS_UNIVERSAL)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (gpg_err_code (err) == GPG_ERR_EOF) + goto no_authid; + if (err) + goto parse_error; + } + if (tag == TAG_BIT_STRING) + { + ppp += objlen; /* Skip the CommonObjectFlags. */ + nnn -= objlen; + + where = __LINE__; + err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nnn || class != CLASS_UNIVERSAL)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (gpg_err_code (err) == GPG_ERR_EOF) + goto no_authid; + if (err) + goto parse_error; + } + if (tag == TAG_OCTET_STRING && objlen) + { + authid = ppp; + authidlen = objlen; + } + no_authid: + ; + } + + /* Parse the commonKeyAttributes. */ + where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || tag != TAG_SEQUENCE)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) - { - log_error ("error parsing CDF record: %s - skipped\n", - gpg_strerror (err)); - continue; - } - pp += objlen; - nn -= objlen; - - /* FIXME: Check that this is a reference to a certificate. */ - - - } - + goto parse_error; + { + const unsigned char *ppp = pp; + size_t nnn = objlen; - leave: - xfree (buffer); - return err; -} + pp += objlen; + nn -= objlen; -/* Read and parse Authentication Object Directory Files. */ -static gpg_error_t -read_ef_aodf (app_t app) -{ - -} + /* Get the Id. */ + where = __LINE__; + err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nnn + || class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + objid = ppp; + objidlen = objlen; + ppp += objlen; + nnn -= objlen; + + /* Get the KeyUsageFlags. */ + where = __LINE__; + err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nnn + || class != CLASS_UNIVERSAL || tag != TAG_BIT_STRING)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + err = parse_keyusage_flags (ppp, objlen, &usageflags); + if (err) + goto parse_error; + ppp += objlen; + nnn -= objlen; + + /* Find the keyReference */ + where = __LINE__; + err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (gpg_err_code (err) == GPG_ERR_EOF) + goto leave_cki; + if (!err && objlen > nnn) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + if (class == CLASS_UNIVERSAL && tag == TAG_BOOLEAN) + { + /* Skip the native element. */ + ppp += objlen; + nnn -= objlen; + + err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (gpg_err_code (err) == GPG_ERR_EOF) + goto leave_cki; + if (!err && objlen > nnn) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + } + if (class == CLASS_UNIVERSAL && tag == TAG_BIT_STRING) + { + /* Skip the accessFlags. */ + ppp += objlen; + nnn -= objlen; + + err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (gpg_err_code (err) == GPG_ERR_EOF) + goto leave_cki; + if (!err && objlen > nnn) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + } + if (class == CLASS_UNIVERSAL && tag == TAG_INTEGER) + { + /* Yep, this is the keyReference. */ + for (ul=0; objlen; objlen--) + { + ul <<= 8; + ul |= (*ppp++) & 0xff; + nnn--; + } + key_reference = ul; + key_reference_valid = 1; + } + leave_cki: + ; + } -/* 6037 (dataobjects) - -30 1E 30 0B 0C 06 45 46 2E 47 44 4F 04 01 0A 30 0.0...EF.GDO...0 -02 0C 00 A1 0B 30 09 04 04 3F 00 2F 02 80 01 0E .....0...?./.... -30 30 30 18 0C 0F 64 69 73 70 6C 61 79 20 6D 65 000...display me -73 73 61 67 65 03 02 06 C0 04 01 0A 30 05 0C 03 ssage.......0... -42 53 53 A1 0D 30 0B 04 06 3F 00 40 16 D0 00 80 BSS..0...?.@.... -01 20 30 2B 30 0C 0C 03 53 53 4F 03 02 06 C0 04 . 0+0...SSO..... -01 0A 30 0B 0C 09 53 61 66 65 47 75 61 72 64 A1 ..0...SafeGuard. -0E 30 0C 04 06 3F 00 0F FF 30 02 80 02 03 00 30 .0...?...0.....0 -30 30 11 0C 08 53 47 41 53 64 61 74 61 03 02 06 00...SGASdata... -C0 04 01 0A 30 0B 0C 09 53 61 66 65 47 75 61 72 ....0...SafeGuar -64 A1 0E 30 0C 04 06 3F 00 0F FF 40 01 80 02 00 d..0...?...@.... -80 30 30 30 11 0C 08 55 73 65 72 64 61 74 61 03 .000...Userdata. -02 06 40 04 01 0A 30 0B 0C 09 53 61 66 65 47 75 ..@...0...SafeGu -61 72 64 A1 0E 30 0C 04 06 3F 00 0F FF 30 01 80 ard..0...?...0.. -02 01 00 30 2C 30 13 0C 0A 62 61 73 69 63 20 64 ...0,0...basic d -61 74 61 03 02 06 C0 04 01 0A 30 05 0C 03 49 44 ata.......0...ID -44 A1 0E 30 0C 04 06 3F 00 40 17 D0 01 80 02 02 D..0...?.@...... -00 30 2F 30 16 0C 0D 65 78 74 65 6E 64 65 64 20 .0/0...extended -64 61 74 61 03 02 06 C0 04 01 0A 30 05 0C 03 49 data.......0...I -44 44 A1 0E 30 0C 04 06 3F 00 40 17 D0 02 80 02 DD..0...?.@..... -08 00 30 34 30 1B 0C 12 73 70 65 63 69 61 6C 20 ..040...special -70 72 69 76 69 6C 65 67 65 73 03 02 06 C0 04 01 privileges...... -0A 30 05 0C 03 49 44 44 A1 0E 30 0C 04 06 3F 00 .0...IDD..0...?. -40 17 D0 03 80 02 04 00 @....... - - 0 30: SEQUENCE { - 2 11: SEQUENCE { - 4 6: UTF8String 'EF.GDO' - 12 1: OCTET STRING 0A - : } - 15 2: SEQUENCE { - 17 0: UTF8String - : Error: Object has zero length. - : } - 19 11: [1] { - 21 9: SEQUENCE { - 23 4: OCTET STRING 3F 00 2F 02 - 29 1: [0] 0E - : } - : } - : } + /* Skip subClassAttributes. */ + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && objlen > nn) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + if (class == CLASS_CONTEXT && tag == 0) + { + pp += objlen; + nn -= objlen; + + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + } + /* Parse the keyAttributes. */ + if (!err && (objlen > nn || class != CLASS_CONTEXT || tag != 1)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + nn = objlen; + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && objlen > nn) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE) + ; /* RSA */ + else if (class == CLASS_CONTEXT) + { + switch (tag) + { + case 0: errstr = "EC key objects are not supported"; break; + case 1: errstr = "DH key objects are not supported"; break; + case 2: errstr = "DSA key objects are not supported"; break; + case 3: errstr = "KEA key objects are not supported"; break; + default: errstr = "unknown privateKeyObject"; break; + } + goto parse_error; + } + else + { + err = gpg_error (GPG_ERR_INV_OBJ); + goto parse_error; + } -6038 (authobjects) - -30 2A 30 0B 0C 05 62 61 73 69 63 03 02 00 C0 30 0*0...basic....0 -03 04 01 0A A1 16 30 14 03 03 00 0C 10 0A 01 01 ......0......... -02 01 06 02 01 06 02 01 08 80 01 01 30 51 30 19 ............0Q0. -0C 13 73 70 65 63 69 66 69 63 20 50 49 4E 20 66 ..specific PIN f -6F 72 20 44 53 03 02 00 C0 30 03 04 01 07 A1 2F or DS....0...../ -30 2D 03 03 00 4C 10 0A 01 01 02 01 06 02 01 06 0-...L.......... -02 01 08 80 01 02 18 0F 32 30 30 32 30 34 31 39 ........20020419 -31 32 31 33 34 31 5A 30 06 04 04 3F 00 40 16 121341Z0...?.@. - - 0 42: SEQUENCE { - 2 11: SEQUENCE { - 4 5: UTF8String 'basic' - 11 2: BIT STRING - : '00000011'B - : Error: Spurious zero bits in bitstring. - : } - 15 3: SEQUENCE { - 17 1: OCTET STRING 0A - : } - 20 22: [1] { - 22 20: SEQUENCE { - 24 3: BIT STRING - : '0000100000110000'B - : Error: Spurious zero bits in bitstring. - 29 1: ENUMERATED 1 - 32 1: INTEGER 6 - 35 1: INTEGER 6 - 38 1: INTEGER 8 - 41 1: [0] 01 - : } - : } - : } + nn = objlen; + /* Check that the reference is a Path object. */ + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && objlen > nn) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + if (class != CLASS_UNIVERSAL || tag != TAG_SEQUENCE) + { + errstr = "unsupported reference type"; + goto parse_error; + } + nn = objlen; + /* Parse the Path object. */ + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && objlen > nn) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; -*/ + /* Make sure that the next element is a non zero path and of + even length (FID are two bytes each). */ + if (class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING + || !objlen || (objlen & 1) ) + { + errstr = "invalid path reference"; + goto parse_error; + } + /* Create a new PrKDF list item. */ + prkdf = xtrycalloc (1, (sizeof *prkdf + - sizeof(unsigned short) + + objlen/2 * sizeof(unsigned short))); + if (!prkdf) + { + err = gpg_error_from_errno (errno); + goto leave; + } + prkdf->objidlen = objidlen; + prkdf->objid = xtrymalloc (objidlen); + if (!prkdf->objid) + { + err = gpg_error_from_errno (errno); + xfree (prkdf); + goto leave; + } + memcpy (prkdf->objid, objid, objidlen); + if (authid) + { + prkdf->authidlen = authidlen; + prkdf->authid = xtrymalloc (authidlen); + if (!prkdf->authid) + { + err = gpg_error_from_errno (errno); + xfree (prkdf->objid); + xfree (prkdf); + goto leave; + } + memcpy (prkdf->authid, authid, authidlen); + } + prkdf->pathlen = objlen/2; + for (i=0; i < prkdf->pathlen; i++, pp += 2, nn -= 2) + prkdf->path[i] = ((pp[0] << 8) | pp[1]); -/* Read and parse the EF(TokenInfo). + prkdf->usageflags = usageflags; + prkdf->key_reference = key_reference; + prkdf->key_reference_valid = key_reference_valid; -TokenInfo ::= SEQUENCE { - version INTEGER {v1(0)} (v1,...), - serialNumber OCTET STRING, + if (nn) + { + /* An index and length follows. */ + prkdf->have_off = 1; + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nn + || class != CLASS_UNIVERSAL || tag != TAG_INTEGER)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + + for (ul=0; objlen; objlen--) + { + ul <<= 8; + ul |= (*pp++) & 0xff; + nn--; + } + prkdf->off = ul; + + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nn + || class != CLASS_CONTEXT || tag != 0)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + + for (ul=0; objlen; objlen--) + { + ul <<= 8; + ul |= (*pp++) & 0xff; + nn--; + } + prkdf->len = ul; + } + + + log_debug ("PrKDF %04hX: id=", fid); + for (i=0; i < prkdf->objidlen; i++) + log_printf ("%02X", prkdf->objid[i]); + log_printf (" path="); + for (i=0; i < prkdf->pathlen; i++) + log_printf ("%04hX", prkdf->path[i]); + if (prkdf->have_off) + log_printf ("[%lu/%lu]", prkdf->off, prkdf->len); + if (prkdf->authid) + { + log_printf (" authid="); + for (i=0; i < prkdf->authidlen; i++) + log_printf ("%02X", prkdf->authid[i]); + } + if (prkdf->key_reference_valid) + log_printf (" keyref=0x%02lX", prkdf->key_reference); + log_printf (" usage="); + s = ""; + if (prkdf->usageflags.encrypt) log_printf ("%sencrypt", s), s = ","; + if (prkdf->usageflags.decrypt) log_printf ("%sdecrypt", s), s = ","; + if (prkdf->usageflags.sign ) log_printf ("%ssign", s), s = ","; + if (prkdf->usageflags.sign_recover) + log_printf ("%ssign_recover", s), s = ","; + if (prkdf->usageflags.wrap ) log_printf ("%swrap", s), s = ","; + if (prkdf->usageflags.unwrap ) log_printf ("%sunwrap", s), s = ","; + if (prkdf->usageflags.verify ) log_printf ("%sverify", s), s = ","; + if (prkdf->usageflags.verify_recover) + log_printf ("%sverify_recover", s), s = ","; + if (prkdf->usageflags.derive ) log_printf ("%sderive", s), s = ","; + if (prkdf->usageflags.non_repudiation) + log_printf ("%snon_repudiation", s), s = ","; + log_printf ("\n"); + + /* Put it into the list. */ + prkdf->next = prkdflist; + prkdflist = prkdf; + prkdf = NULL; + continue; /* Ready. */ + + parse_error: + log_error ("error parsing PrKDF record (%d): %s - skipped\n", + where, errstr? errstr : gpg_strerror (err)); + if (prkdf) + { + xfree (prkdf->objid); + xfree (prkdf->authid); + xfree (prkdf); + } + err = 0; + } /* End looping over all records. */ + + leave: + xfree (buffer); + if (err) + release_prkdflist (prkdflist); + else + *result = prkdflist; + return err; +} + + +/* Read and parse the Certificate Directory Files identified by FID. + On success a newlist of CDF object gets stored at RESULT and the + caller is then responsible of releasing this list. On error a + error code is returned and RESULT won't get changed. */ +static gpg_error_t +read_ef_cdf (app_t app, unsigned short fid, cdf_object_t *result) +{ + gpg_error_t err; + unsigned char *buffer = NULL; + size_t buflen; + const unsigned char *p; + size_t n, objlen, hdrlen; + int class, tag, constructed, ndef; + cdf_object_t cdflist = NULL; + int i; + + if (!fid) + return gpg_error (GPG_ERR_NO_DATA); /* No certificates. */ + + err = select_and_read_binary (app->slot, fid, "CDF", &buffer, &buflen); + if (err) + return err; + + p = buffer; + n = buflen; + + /* Loop over the records. We stop as soon as we detect a new record + starting with 0x00 or 0xff as these values are commonly used to + pad data blocks and are no valid ASN.1 encoding. */ + while (n && *p && *p != 0xff) + { + const unsigned char *pp; + size_t nn; + int where; + const char *errstr = NULL; + cdf_object_t cdf = NULL; + unsigned long ul; + const unsigned char *objid; + size_t objidlen; + + err = parse_ber_header (&p, &n, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > n || tag != TAG_SEQUENCE)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + { + log_error ("error parsing CDF record: %s\n", gpg_strerror (err)); + goto leave; + } + pp = p; + nn = objlen; + p += objlen; + n -= objlen; + + /* Skip the commonObjectAttributes. */ + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nn || tag != TAG_SEQUENCE)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + pp += objlen; + nn -= objlen; + + /* Parse the commonCertificateAttributes. */ + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nn || tag != TAG_SEQUENCE)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + { + const unsigned char *ppp = pp; + size_t nnn = objlen; + + pp += objlen; + nn -= objlen; + + /* Get the Id. */ + where = __LINE__; + err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nnn + || class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + objid = ppp; + objidlen = objlen; + } + + /* Parse the certAttribute. */ + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nn || class != CLASS_CONTEXT || tag != 1)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + nn = objlen; + + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nn + || class != CLASS_UNIVERSAL || tag != TAG_SEQUENCE)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + nn = objlen; + + /* Check that the reference is a Path object. */ + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && objlen > nn) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + if (class != CLASS_UNIVERSAL || tag != TAG_SEQUENCE) + { + errstr = "unsupported reference type"; + continue; + } + nn = objlen; + + /* Parse the Path object. */ + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && objlen > nn) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + + /* Make sure that the next element is a non zero path and of + even length (FID are two bytes each). */ + if (class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING + || !objlen || (objlen & 1) ) + { + errstr = "invalid path reference"; + goto parse_error; + } + /* Create a new CDF list item. */ + cdf = xtrycalloc (1, (sizeof *cdf + - sizeof(unsigned short) + + objlen/2 * sizeof(unsigned short))); + if (!cdf) + { + err = gpg_error_from_errno (errno); + goto leave; + } + cdf->objidlen = objidlen; + cdf->objid = xtrymalloc (objidlen); + if (!cdf->objid) + { + err = gpg_error_from_errno (errno); + xfree (cdf); + goto leave; + } + memcpy (cdf->objid, objid, objidlen); + + cdf->pathlen = objlen/2; + for (i=0; i < cdf->pathlen; i++, pp += 2, nn -= 2) + cdf->path[i] = ((pp[0] << 8) | pp[1]); + + if (nn) + { + /* An index and length follows. */ + cdf->have_off = 1; + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nn + || class != CLASS_UNIVERSAL || tag != TAG_INTEGER)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + + for (ul=0; objlen; objlen--) + { + ul <<= 8; + ul |= (*pp++) & 0xff; + nn--; + } + cdf->off = ul; + + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nn + || class != CLASS_CONTEXT || tag != 0)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + + for (ul=0; objlen; objlen--) + { + ul <<= 8; + ul |= (*pp++) & 0xff; + nn--; + } + cdf->len = ul; + } + + log_debug ("CDF %04hX: id=", fid); + for (i=0; i < cdf->objidlen; i++) + log_printf ("%02X", cdf->objid[i]); + log_printf (" path="); + for (i=0; i < cdf->pathlen; i++) + log_printf ("%04hX", cdf->path[i]); + if (cdf->have_off) + log_printf ("[%lu/%lu]", cdf->off, cdf->len); + log_printf ("\n"); + + /* Put it into the list. */ + cdf->next = cdflist; + cdflist = cdf; + cdf = NULL; + continue; /* Ready. */ + + parse_error: + log_error ("error parsing CDF record (%d): %s - skipped\n", + where, errstr? errstr : gpg_strerror (err)); + xfree (cdf); + err = 0; + } /* End looping over all records. */ + + leave: + xfree (buffer); + if (err) + release_cdflist (cdflist); + else + *result = cdflist; + return err; +} + + +/* +SEQUENCE { + SEQUENCE { -- CommonObjectAttributes + UTF8String 'specific PIN for DS' + BIT STRING 0 unused bits + '00000011'B + } + SEQUENCE { -- CommonAuthenticationObjectAttributes + OCTET STRING + 07 -- iD + } + + [1] { -- typeAttributes + SEQUENCE { -- PinAttributes + BIT STRING 0 unused bits + '0000100000110010'B -- local,initialized,needs-padding + -- exchangeRefData + ENUMERATED 1 -- ascii-numeric + INTEGER 6 -- minLength + INTEGER 6 -- storedLength + INTEGER 8 -- maxLength + [0] + 02 -- pinReference + GeneralizedTime 19/04/2002 12:12 GMT -- lastPinChange + SEQUENCE { + OCTET STRING + 3F 00 40 16 -- path to DF of PIN + } + } + } + } + +*/ +/* Read and parse an Authentication Object Directory File identified + by FID. On success a newlist of AODF objects gets stored at RESULT + and the caller is responsible of releasing this list. On error a + error code is returned and RESULT won't get changed. */ +static gpg_error_t +read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result) +{ + gpg_error_t err; + unsigned char *buffer = NULL; + size_t buflen; + const unsigned char *p; + size_t n, objlen, hdrlen; + int class, tag, constructed, ndef; + aodf_object_t aodflist = NULL; + int i; + + if (!fid) + return gpg_error (GPG_ERR_NO_DATA); /* No authentication objects. */ + + err = select_and_read_binary (app->slot, fid, "AODF", &buffer, &buflen); + if (err) + return err; + + p = buffer; + n = buflen; + + /* FIXME: This shares a LOT of code with read_ef_prkdf! */ + + /* Loop over the records. We stop as soon as we detect a new record + starting with 0x00 or 0xff as these values are commonly used to + pad data blocks and are no valid ASN.1 encoding. */ + while (n && *p && *p != 0xff) + { + const unsigned char *pp; + size_t nn; + int where; + const char *errstr = NULL; + aodf_object_t aodf = NULL; + unsigned long ul; + const char *s; + + err = parse_ber_header (&p, &n, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > n || tag != TAG_SEQUENCE)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + { + log_error ("error parsing AODF record: %s\n", gpg_strerror (err)); + goto leave; + } + pp = p; + nn = objlen; + p += objlen; + n -= objlen; + + /* Allocate memory for a new AODF list item. */ + aodf = xtrycalloc (1, sizeof *aodf); + if (!aodf) + goto no_core; + + /* Parse the commonObjectAttributes. */ + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nn || tag != TAG_SEQUENCE)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + { + const unsigned char *ppp = pp; + size_t nnn = objlen; + + pp += objlen; + nn -= objlen; + + /* Search the optional AuthId. We need to skip the optional + Label (UTF8STRING) and the optional CommonObjectFlags + (BITSTRING). */ + where = __LINE__; + err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nnn || class != CLASS_UNIVERSAL)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (gpg_err_code (err) == GPG_ERR_EOF) + goto no_authid; + if (err) + goto parse_error; + if (tag == TAG_UTF8_STRING) + { + ppp += objlen; /* Skip the Label. */ + nnn -= objlen; + + where = __LINE__; + err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nnn || class != CLASS_UNIVERSAL)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (gpg_err_code (err) == GPG_ERR_EOF) + goto no_authid; + if (err) + goto parse_error; + } + if (tag == TAG_BIT_STRING) + { + ppp += objlen; /* Skip the CommonObjectFlags. */ + nnn -= objlen; + + where = __LINE__; + err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nnn || class != CLASS_UNIVERSAL)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (gpg_err_code (err) == GPG_ERR_EOF) + goto no_authid; + if (err) + goto parse_error; + } + if (tag == TAG_OCTET_STRING && objlen) + { + aodf->authidlen = objlen; + aodf->authid = xtrymalloc (objlen); + if (!aodf->authid) + goto no_core; + memcpy (aodf->authid, ppp, objlen); + } + no_authid: + ; + } + + /* Parse the CommonAuthenticationObjectAttributes. */ + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nn || tag != TAG_SEQUENCE)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + { + const unsigned char *ppp = pp; + size_t nnn = objlen; + + pp += objlen; + nn -= objlen; + + /* Get the Id. */ + where = __LINE__; + err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nnn + || class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + + aodf->objidlen = objlen; + aodf->objid = xtrymalloc (objlen); + if (!aodf->objid) + goto no_core; + memcpy (aodf->objid, ppp, objlen); + } + + /* Parse the typeAttributes. */ + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nn || class != CLASS_CONTEXT || tag != 1)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + nn = objlen; + + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && objlen > nn) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE) + ; /* PinAttributes */ + else if (class == CLASS_CONTEXT) + { + switch (tag) + { + case 0: errstr = "biometric auth types are not supported"; break; + case 1: errstr = "authKey auth types are not supported"; break; + case 2: errstr = "external auth type are not supported"; break; + default: errstr = "unknown privateKeyObject"; break; + } + goto parse_error; + } + else + { + err = gpg_error (GPG_ERR_INV_OBJ); + goto parse_error; + } + + nn = objlen; + + /* PinFlags */ + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nn || !objlen + || class != CLASS_UNIVERSAL || tag != TAG_BIT_STRING)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + + { + unsigned int bits, mask; + int unused, full; + + unused = *pp++; nn--; objlen--; + if ((!objlen && unused) || unused/8 > objlen) + { + err = gpg_error (GPG_ERR_ENCODING_PROBLEM); + goto parse_error; + } + full = objlen - (unused+7)/8; + unused %= 8; + mask = 0; + for (i=1; unused; i <<= 1, unused--) + mask |= i; + + /* The first octet */ + bits = 0; + if (objlen) + { + bits = *pp++; nn--; objlen--; + if (full) + full--; + else + { + bits &= ~mask; + mask = 0; + } + } + if ((bits & 0x80)) /* ASN.1 bit 0. */ + aodf->pinflags.case_sensitive = 1; + if ((bits & 0x40)) /* ASN.1 bit 1. */ + aodf->pinflags.local = 1; + if ((bits & 0x20)) + aodf->pinflags.change_disabled = 1; + if ((bits & 0x10)) + aodf->pinflags.unblock_disabled = 1; + if ((bits & 0x08)) + aodf->pinflags.initialized = 1; + if ((bits & 0x04)) + aodf->pinflags.needs_padding = 1; + if ((bits & 0x02)) + aodf->pinflags.unblocking_pin = 1; + if ((bits & 0x01)) + aodf->pinflags.so_pin = 1; + /* The second octet. */ + bits = 0; + if (objlen) + { + bits = *pp++; nn--; objlen--; + if (full) + full--; + else + { + bits &= ~mask; + mask = 0; + } + } + if ((bits & 0x80)) + aodf->pinflags.disable_allowed = 1; + if ((bits & 0x40)) + aodf->pinflags.integrity_protected = 1; + if ((bits & 0x20)) + aodf->pinflags.confidentiality_protected = 1; + if ((bits & 0x10)) + aodf->pinflags.exchange_ref_data = 1; + /* Skip remaining bits. */ + pp += objlen; + nn -= objlen; + } + + + /* PinType */ + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nn + || class != CLASS_UNIVERSAL || tag != TAG_ENUMERATED)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (!err && (objlen > sizeof (pin_type_t) || objlen > sizeof (ul))) + err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); + if (err) + goto parse_error; + + for (ul=0; objlen; objlen--) + { + ul <<= 8; + ul |= (*pp++) & 0xff; + nn--; + } + aodf->pintype = ul; + + + /* minLength */ + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nn + || class != CLASS_UNIVERSAL || tag != TAG_INTEGER)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (!err && objlen > sizeof (ul)) + err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); + if (err) + goto parse_error; + for (ul=0; objlen; objlen--) + { + ul <<= 8; + ul |= (*pp++) & 0xff; + nn--; + } + aodf->min_length = ul; + + + /* storedLength */ + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nn + || class != CLASS_UNIVERSAL || tag != TAG_INTEGER)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (!err && objlen > sizeof (ul)) + err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); + if (err) + goto parse_error; + for (ul=0; objlen; objlen--) + { + ul <<= 8; + ul |= (*pp++) & 0xff; + nn--; + } + aodf->stored_length = ul; + + /* optional maxLength */ + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (gpg_err_code (err) == GPG_ERR_EOF) + goto ready; + if (!err && objlen > nn) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + if (class == CLASS_UNIVERSAL && tag == TAG_INTEGER) + { + if (objlen > sizeof (ul)) + { + err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); + goto parse_error; + } + for (ul=0; objlen; objlen--) + { + ul <<= 8; + ul |= (*pp++) & 0xff; + nn--; + } + aodf->max_length = ul; + aodf->max_length_valid = 1; + + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (gpg_err_code (err) == GPG_ERR_EOF) + goto ready; + if (!err && objlen > nn) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + } + + /* Optional pinReference. */ + if (class == CLASS_CONTEXT && tag == 0) + { + if (objlen > sizeof (ul)) + { + err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); + goto parse_error; + } + for (ul=0; objlen; objlen--) + { + ul <<= 8; + ul |= (*pp++) & 0xff; + nn--; + } + aodf->pin_reference = ul; + aodf->pin_reference_valid = 1; + + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (gpg_err_code (err) == GPG_ERR_EOF) + goto ready; + if (!err && objlen > nn) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + } + + /* Optional padChar. */ + if (class == CLASS_UNIVERSAL && tag == TAG_OCTET_STRING) + { + if (objlen != 1) + { + errstr = "padChar is not of size(1)"; + goto parse_error; + } + aodf->pad_char = *pp++; nn--; + aodf->pad_char_valid = 1; + + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (gpg_err_code (err) == GPG_ERR_EOF) + goto ready; + if (!err && objlen > nn) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + } + + /* Skip optional lastPinChange. */ + if (class == CLASS_UNIVERSAL && tag == TAG_GENERALIZED_TIME) + { + pp += objlen; + nn -= objlen; + + where = __LINE__; + err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (gpg_err_code (err) == GPG_ERR_EOF) + goto ready; + if (!err && objlen > nn) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + } + + /* Optional Path object. */ + if (class == CLASS_UNIVERSAL || tag == TAG_SEQUENCE) + { + const unsigned char *ppp = pp; + size_t nnn = objlen; + + pp += objlen; + nn -= objlen; + + where = __LINE__; + err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && objlen > nnn) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + + /* Make sure that the next element is a non zero FID and of + even length (FID are two bytes each). */ + if (class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING + || !objlen || (objlen & 1) ) + { + errstr = "invalid path reference"; + goto parse_error; + } + + aodf->pathlen = objlen/2; + aodf->path = xtrymalloc (aodf->pathlen); + if (!aodf->path) + goto no_core; + for (i=0; i < aodf->pathlen; i++, ppp += 2, nnn -= 2) + aodf->path[i] = ((ppp[0] << 8) | ppp[1]); + + if (nnn) + { + /* An index and length follows. */ + aodf->have_off = 1; + where = __LINE__; + err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nnn + || class != CLASS_UNIVERSAL || tag != TAG_INTEGER)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + + for (ul=0; objlen; objlen--) + { + ul <<= 8; + ul |= (*ppp++) & 0xff; + nnn--; + } + aodf->off = ul; + + where = __LINE__; + err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > nnn + || class != CLASS_CONTEXT || tag != 0)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto parse_error; + + for (ul=0; objlen; objlen--) + { + ul <<= 8; + ul |= (*ppp++) & 0xff; + nnn--; + } + aodf->len = ul; + } + } + + /* Igonore further objects which might be there due to future + extensions of pkcs#15. */ + + ready: + log_debug ("AODF %04hX: id=", fid); + for (i=0; i < aodf->objidlen; i++) + log_printf ("%02X", aodf->objid[i]); + if (aodf->authid) + { + log_printf (" authid="); + for (i=0; i < aodf->authidlen; i++) + log_printf ("%02X", aodf->authid[i]); + } + log_printf (" flags="); + s = ""; + if (aodf->pinflags.case_sensitive) + log_printf ("%scase_sensitive", s), s = ","; + if (aodf->pinflags.local) + log_printf ("%slocal", s), s = ","; + if (aodf->pinflags.change_disabled) + log_printf ("%schange_disabled", s), s = ","; + if (aodf->pinflags.unblock_disabled) + log_printf ("%sunblock_disabled", s), s = ","; + if (aodf->pinflags.initialized) + log_printf ("%sinitialized", s), s = ","; + if (aodf->pinflags.needs_padding) + log_printf ("%sneeds_padding", s), s = ","; + if (aodf->pinflags.unblocking_pin) + log_printf ("%sunblocking_pin", s), s = ","; + if (aodf->pinflags.so_pin) + log_printf ("%sso_pin", s), s = ","; + if (aodf->pinflags.disable_allowed) + log_printf ("%sdisable_allowed", s), s = ","; + if (aodf->pinflags.integrity_protected) + log_printf ("%sintegrity_protected", s), s = ","; + if (aodf->pinflags.confidentiality_protected) + log_printf ("%sconfidentiality_protected", s), s = ","; + if (aodf->pinflags.exchange_ref_data) + log_printf ("%sexchange_ref_data", s), s = ","; + { + char numbuf[50]; + switch (aodf->pintype) + { + case PIN_TYPE_BCD: s = "bcd"; break; + case PIN_TYPE_ASCII_NUMERIC: s = "ascii-numeric"; break; + case PIN_TYPE_UTF8: s = "utf8"; break; + case PIN_TYPE_HALF_NIBBLE_BCD: s = "half-nibble-bcd"; break; + case PIN_TYPE_ISO9564_1: s = "iso9564-1"; break; + default: + sprintf (numbuf, "%lu", (unsigned long)aodf->pintype); + s = numbuf; + } + log_printf (" type=%s", s); + } + log_printf (" min=%lu", aodf->min_length); + log_printf (" stored=%lu", aodf->stored_length); + if (aodf->max_length_valid) + log_printf (" max=%lu", aodf->max_length); + if (aodf->pad_char_valid) + log_printf (" pad=0x%02x", aodf->pad_char); + if (aodf->pin_reference_valid) + log_printf (" pinref=0x%02lX", aodf->pin_reference); + if (aodf->pathlen) + { + log_printf (" path="); + for (i=0; i < aodf->pathlen; i++) + log_printf ("%04hX", aodf->path[i]); + if (aodf->have_off) + log_printf ("[%lu/%lu]", aodf->off, aodf->len); + } + log_printf ("\n"); + + /* Put it into the list. */ + aodf->next = aodflist; + aodflist = aodf; + aodf = NULL; + continue; /* Ready. */ + + no_core: + err = gpg_error_from_errno (errno); + release_aodf_object (aodf); + goto leave; + + parse_error: + log_error ("error parsing AODF record (%d): %s - skipped\n", + where, errstr? errstr : gpg_strerror (err)); + err = 0; + release_aodf_object (aodf); + } /* End looping over all records. */ + + leave: + xfree (buffer); + if (err) + release_aodflist (aodflist); + else + *result = aodflist; + return err; +} + + + + + +/* Read and parse the EF(TokenInfo). + +TokenInfo ::= SEQUENCE { + version INTEGER {v1(0)} (v1,...), + serialNumber OCTET STRING, manufacturerID Label OPTIONAL, label [0] Label OPTIONAL, tokenflags TokenFlags, @@ -521,105 +2152,678 @@ TokenFlags ::= BIT STRING { */ +/* static gpg_error_t */ +/* read_ef_tokeninfo (app_t app) */ +/* { */ +/* unsigned short efid = 0x5032; */ +/* return 0; */ +/* } */ + + +/* Get all the basic information from the pkcs#15 card, check the + structure and initialize our local context. This is used once at + application initialization. */ static gpg_error_t -read_ef_tokeninfo (app_t app) +read_p15_info (app_t app) { - unsigned short efid = 0x5032; + gpg_error_t err; + + /* Fixme: We might need to read the tokeninfo to get a non-standard + ODF FID. */ + + /* Read the ODF so that we know the location of all directory + files. */ + err = read_ef_odf (app, 0x5031); + if (err) + return err; + + /* Read certificate information. */ + assert (!app->app_local->certificate_info); + assert (!app->app_local->trusted_certificate_info); + assert (!app->app_local->useful_certificate_info); + err = read_ef_cdf (app, app->app_local->odf.certificates, + &app->app_local->certificate_info); + if (!err || gpg_err_code (err) == GPG_ERR_NO_DATA) + err = read_ef_cdf (app, app->app_local->odf.trusted_certificates, + &app->app_local->trusted_certificate_info); + if (!err || gpg_err_code (err) == GPG_ERR_NO_DATA) + err = read_ef_cdf (app, app->app_local->odf.useful_certificates, + &app->app_local->useful_certificate_info); + if (gpg_err_code (err) == GPG_ERR_NO_DATA) + err = 0; + if (err) + return err; + /* Read information about private keys. */ + assert (!app->app_local->private_key_info); + err = read_ef_prkdf (app, app->app_local->odf.private_keys, + &app->app_local->private_key_info); + if (gpg_err_code (err) == GPG_ERR_NO_DATA) + err = 0; + if (err) + return err; + + /* Read information about authentication objects. */ + assert (!app->app_local->auth_object_info); + err = read_ef_aodf (app, app->app_local->odf.auth_objects, + &app->app_local->auth_object_info); + if (gpg_err_code (err) == GPG_ERR_NO_DATA) + err = 0; + + + return err; } -/* Get all the basic information from the pkcs#15 card, check the - structure and init our context. This is used once at application - initialization. */ +/* Helper to do_learn_status: Send information about all certificates + listed in CERTINFO back. Use CERTTYPE as type of the + certificate. */ static gpg_error_t -read_p15_info (app_t app) +send_certinfo (app_t app, ctrl_t ctrl, const char *certtype, + cdf_object_t certinfo) { - gpg_error_t err; + for (; certinfo; certinfo = certinfo->next) + { + char *buf, *p; + int i; + + buf = xtrymalloc (9 + certinfo->objidlen*2 + 1); + if (!buf) + return gpg_error_from_errno (errno); + p = stpcpy (buf, "P15"); + if (app->app_local->home_df) + { + sprintf (p, "-%04hX", (app->app_local->home_df & 0xffff)); + p += 5; + } + p = stpcpy (p, "."); + for (i=0; i < certinfo->objidlen; i++) + { + sprintf (p, "%02X", certinfo->objid[i]); + p += 2; + } + + send_status_info (ctrl, "CERTINFO", + certtype, strlen (certtype), + buf, strlen (buf), + NULL, (size_t)0); + xfree (buf); + } + return 0; +} - err = read_ed_odf (app); + +/* Get the keygrip of the private key object PRKDF. On success the + keygrip gets returned in the caller provided 41 byte buffer + R_GRIPSTR. */ +static gpg_error_t +keygripstr_from_prkdf (app_t app, prkdf_object_t prkdf, char *r_gripstr) +{ + gpg_error_t err; + cdf_object_t cdf; + unsigned char *der; + size_t derlen; + ksba_cert_t cert; + + /* FIXME: We should check whether a public key directory file and a + matching public key for PRKDF is available. This should make + extraction of the key much easier. My current test card doesn't + have one, so we can only use the fallback solution bu looking for + a matching certificate and extract the key from there. */ + + /* Look for a matching certificate. A certificate matches if the Id + matches the obne of the private key info. */ + for (cdf = app->app_local->certificate_info; cdf; cdf = cdf->next) + if (cdf->objidlen == prkdf->objidlen + && !memcmp (cdf->objid, prkdf->objid, prkdf->objidlen)) + break; + if (!cdf) + for (cdf = app->app_local->trusted_certificate_info; cdf; cdf = cdf->next) + if (cdf->objidlen == prkdf->objidlen + && !memcmp (cdf->objid, prkdf->objid, prkdf->objidlen)) + break; + if (!cdf) + for (cdf = app->app_local->useful_certificate_info; cdf; cdf = cdf->next) + if (cdf->objidlen == prkdf->objidlen + && !memcmp (cdf->objid, prkdf->objid, prkdf->objidlen)) + break; + if (!cdf) + return gpg_error (GPG_ERR_NOT_FOUND); + + err = readcert_by_cdf (app, cdf, &der, &derlen); if (err) return err; + err = ksba_cert_new (&cert); + if (!err) + err = ksba_cert_init_from_mem (cert, der, derlen); + xfree (der); + if (!err) + err = app_help_get_keygrip_string (cert, r_gripstr); + ksba_cert_release (cert); + + return err; +} + + + + +/* Helper to do_learn_status: Send information about all known + keypairs back. FIXME: much code duplication from + send_sertinfo(). */ +static gpg_error_t +send_keypairinfo (app_t app, ctrl_t ctrl, prkdf_object_t keyinfo) +{ + gpg_error_t err; + + for (; keyinfo; keyinfo = keyinfo->next) + { + char gripstr[40+1]; + char *buf, *p; + int i, j; + + buf = xtrymalloc (9 + keyinfo->objidlen*2 + 1); + if (!buf) + return gpg_error_from_errno (errno); + p = stpcpy (buf, "P15"); + if (app->app_local->home_df) + { + sprintf (p, "-%04hX", (app->app_local->home_df & 0xffff)); + p += 5; + } + p = stpcpy (p, "."); + for (i=0; i < keyinfo->objidlen; i++) + { + sprintf (p, "%02X", keyinfo->objid[i]); + p += 2; + } + + err = keygripstr_from_prkdf (app, keyinfo, gripstr); + if (err) + { + log_error ("can't get keygrip from "); + for (j=0; j < keyinfo->pathlen; j++) + log_printf ("%04hX", keyinfo->path[j]); + log_printf (": %s\n", gpg_strerror (err)); + } + else + { + assert (strlen (gripstr) == 40); + send_status_info (ctrl, "KEYPAIRINFO", + gripstr, 40, + buf, strlen (buf), + NULL, (size_t)0); + } + xfree (buf); + } + return 0; +} + + + +/* This is the handler for the LEARN command. */ +static int /* FIXME: change this to gpg_error_t */ +do_learn_status (app_t app, ctrl_t ctrl) +{ + gpg_error_t err; + + err = send_certinfo (app, ctrl, "100", app->app_local->certificate_info); + if (!err) + err = send_certinfo (app, ctrl, "101", + app->app_local->trusted_certificate_info); + if (!err) + err = send_certinfo (app, ctrl, "102", + app->app_local->useful_certificate_info); + if (!err) + err = send_keypairinfo (app, ctrl, app->app_local->private_key_info); + + return err; +} + + +/* Read a certifciate using the information in CDF and return the + certificate in a newly llocated buffer R_CERT and its length + R_CERTLEN. */ +static gpg_error_t +readcert_by_cdf (app_t app, cdf_object_t cdf, + unsigned char **r_cert, size_t *r_certlen) +{ + gpg_error_t err; + unsigned char *buffer = NULL; + const unsigned char *p, *save_p; + size_t buflen, n; + int class, tag, constructed, ndef; + size_t totobjlen, objlen, hdrlen; + int rootca; + int i; + + *r_cert = NULL; + *r_certlen = 0; + + /* First check whether it has been cached. */ + if (cdf->image) + { + *r_cert = xtrymalloc (cdf->imagelen); + if (!*r_cert) + return gpg_error_from_errno (errno); + memcpy (*r_cert, cdf->image, cdf->imagelen); + *r_certlen = cdf->imagelen; + return 0; + } + + /* Read the entire file. fixme: This could be optimized by first + reading the header to figure out how long the certificate + actually is. */ + err = select_ef_by_path (app, cdf->path, cdf->pathlen); + if (err) + goto leave; + + err = iso7816_read_binary (app->slot, cdf->off, cdf->len, &buffer, &buflen); + if (!err && (!buflen || *buffer == 0xff)) + err = gpg_error (GPG_ERR_NOT_FOUND); + if (err) + { + log_error ("error reading certificate with Id "); + for (i=0; i < cdf->objidlen; i++) + log_printf ("%02X", cdf->objid[i]); + log_printf (": %s\n", gpg_strerror (err)); + goto leave; + } + + /* Check whether this is really a certificate. */ + p = buffer; + n = buflen; + err = parse_ber_header (&p, &n, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (err) + goto leave; + + if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed) + rootca = 0; + else if ( class == CLASS_UNIVERSAL && tag == TAG_SET && constructed ) + rootca = 1; + else + { + err = gpg_error (GPG_ERR_INV_OBJ); + goto leave; + } + totobjlen = objlen + hdrlen; + assert (totobjlen <= buflen); + + err = parse_ber_header (&p, &n, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (err) + goto leave; + + if (!rootca + && class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !constructed) + { + /* The certificate seems to be contained in a userCertificate + container. Skip this and assume the following sequence is + the certificate. */ + if (n < objlen) + { + err = gpg_error (GPG_ERR_INV_OBJ); + goto leave; + } + p += objlen; + n -= objlen; + save_p = p; + err = parse_ber_header (&p, &n, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (err) + goto leave; + if ( !(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed) ) + { + err = gpg_error (GPG_ERR_INV_OBJ); + goto leave; + } + totobjlen = objlen + hdrlen; + assert (save_p + totobjlen <= buffer + buflen); + memmove (buffer, save_p, totobjlen); + } + + *r_cert = buffer; + buffer = NULL; + *r_certlen = totobjlen; + + /* Try to cache it. */ + if (!cdf->image && (cdf->image = xtrymalloc (*r_certlen))) + { + memcpy (cdf->image, *r_cert, *r_certlen); + cdf->imagelen = *r_certlen; + } + + + leave: + xfree (buffer); + return err; +} + + + +/* Handler for the READCERT command. + + Read the certificate with id CERTID (as returned by learn_status in + the CERTINFO status lines) and return it in the freshly allocated + buffer to be stored at R_CERT and its length at R_CERTLEN. A error + code will be returned on failure and R_CERT and R_CERTLEN will be + set to NULL/0. */ +static int /* FIXME: change this to gpg_error_t */ +do_readcert (app_t app, const char *certid, + unsigned char **r_cert, size_t *r_certlen) +{ + gpg_error_t err; + cdf_object_t cdf; + + *r_cert = NULL; + *r_certlen = 0; + err = cdf_object_from_certid (app, certid, &cdf); + if (!err) + err =readcert_by_cdf (app, cdf, r_cert, r_certlen); + return err; } -static int -do_learn_status (APP app, CTRL ctrl) + +/* Handler for the PKSIGN command. + + Create the signature and return the allocated result in OUTDATA. + If a PIN is required, the PINCB will be used to ask for the PIN; + that callback should return the PIN in an allocated buffer and + store that as the 3rd argument. */ +static int +do_sign (app_t app, const char *keyidstr, int hashalgo, + int (pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen ) { + static unsigned char sha1_prefix[15] = /* Object ID is 1.3.14.3.2.26 */ + { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, + 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 }; + static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */ + { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03, + 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 }; + gpg_error_t err; - char ct_buf[100], id_buf[100]; int i; + unsigned char data[35]; /* Must be large enough for a SHA-1 digest + + the largest OID prefix above. */ + prkdf_object_t prkdf; /* The private key object. */ + aodf_object_t aodf; /* The associated authentication object. */ + + if (!keyidstr || !*keyidstr) + return gpg_error (GPG_ERR_INV_VALUE); + if (indatalen != 20 && indatalen != 16 && indatalen != 35) + return gpg_error (GPG_ERR_INV_VALUE); + + err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf); + if (err) + return err; + if (!prkdf->authid) + { + log_error ("no authentication object defined for %s\n", keyidstr); + /* fixme: we might want to go ahead and do without PIN + verification. */ + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + } + + /* Find the authentication object to this private key object. */ + for (aodf = app->app_local->auth_object_info; aodf; aodf = aodf->next) + if (aodf->objidlen == prkdf->authidlen + && !memcmp (aodf->objid, prkdf->authid, prkdf->authidlen)) + break; + if (!aodf) + { + log_error ("authentication object for %s missing\n", keyidstr); + return gpg_error (GPG_ERR_INV_CARD); + } + if (aodf->authid) + { + log_error ("PIN verification is protected by an " + "additional authentication token\n"); + return gpg_error (GPG_ERR_BAD_PIN_METHOD); + } + if (aodf->pinflags.integrity_protected + || aodf->pinflags.confidentiality_protected) + { + log_error ("PIN verification requires unsupported protecion method\n"); + return gpg_error (GPG_ERR_BAD_PIN_METHOD); + } + if (!aodf->stored_length && aodf->pinflags.needs_padding) + { + log_error ("PIN verification requires padding but no length known\n"); + return gpg_error (GPG_ERR_INV_CARD); + } - /* Output information about all useful objects. */ - for (i=0; objlist[i].fid; i++) + /* Now that we have all the information available, prepare and run + the PIN verification.*/ + if (1) { - if (filelist[i].certtype) + char *pinvalue; + size_t pinvaluelen; + const char *errstr; + const char *s; + + err = pincb (pincb_arg, "PIN", &pinvalue); + if (err) + { + log_info ("PIN callback returned error: %s\n", gpg_strerror (err)); + return err; + } + + /* We might need to cope with UTF8 things here. Not sure how + min_length etc. are exactly defined, for now we take them as + a plain octet count. */ + + if (strlen (pinvalue) < aodf->min_length) + { + log_error ("PIN is too short; minimum length is %lu\n", + aodf->min_length); + err = gpg_error (GPG_ERR_BAD_PIN); + } + else if (aodf->stored_length && strlen (pinvalue) > aodf->stored_length) + { + /* This would otherwise truncate the PIN silently. */ + log_error ("PIN is too large; maximum length is %lu\n", + aodf->stored_length); + err = gpg_error (GPG_ERR_BAD_PIN); + } + else if (aodf->max_length_valid && strlen (pinvalue) > aodf->max_length) + { + log_error ("PIN is too large; maximum length is %lu\n", + aodf->max_length); + err = gpg_error (GPG_ERR_BAD_PIN); + } + + if (err) { - size_t len; + xfree (pinvalue); + return err; + } - len = app_help_read_length_of_cert (app->slot, - filelist[i].fid, NULL); - if (len) + errstr = NULL; + err = 0; + switch (aodf->pintype) + { + case PIN_TYPE_BCD: + errstr = "PIN type BCD is not supported"; + break; + case PIN_TYPE_ASCII_NUMERIC: + for (s=pinvalue; digitp (s); s++) + ; + if (*s) { - /* FIXME: We should store the length in the application's - context so that a following readcert does only need to - read that many bytes. */ - sprintf (ct_buf, "%d", filelist[i].certtype); - sprintf (id_buf, "P15-DF01.%04X", filelist[i].fid); - send_status_info (ctrl, "CERTINFO", - ct_buf, strlen (ct_buf), - id_buf, strlen (id_buf), - NULL, (size_t)0); + errstr = "Non-numeric digits found in PIN"; + err = gpg_error (GPG_ERR_BAD_PIN); } + break; + case PIN_TYPE_UTF8: + break; + case PIN_TYPE_HALF_NIBBLE_BCD: + errstr = "PIN type Half-Nibble-BCD is not supported"; + break; + case PIN_TYPE_ISO9564_1: + errstr = "PIN type ISO9564-1 is not supported"; + break; + default: + errstr = "Unknown PIN type"; + break; } - else if (filelist[i].iskeypair) + if (errstr) { - char gripstr[40+1]; + log_error ("can't verify PIN: %s\n", errstr); + xfree (pinvalue); + return err? err : gpg_error (GPG_ERR_BAD_PIN_METHOD); + } - err = keygripstr_from_pk_file (app->slot, filelist[i].fid, gripstr); - if (err) - log_error ("can't get keygrip from FID 0x%04X: %s\n", - filelist[i].fid, gpg_strerror (err)); - else + if (aodf->pinflags.needs_padding) + { + char *paddedpin; + + paddedpin = xtrymalloc (aodf->stored_length+1); + if (!paddedpin) { - sprintf (id_buf, "P15-DF01.%04X", filelist[i].fid); - send_status_info (ctrl, "KEYPAIRINFO", - gripstr, 40, - id_buf, strlen (id_buf), - NULL, (size_t)0); + err = gpg_error_from_errno (errno); + xfree (pinvalue); + return err; } + for (i=0, s=pinvalue; i < aodf->stored_length && *s; i++, s++) + paddedpin[i] = *s; + /* Not sure what padding char to use if none has been set. + For now we use 0x00; maybe a space would be better. */ + for (; i < aodf->stored_length; i++) + paddedpin[i] = aodf->pad_char_valid? aodf->pad_char : 0; + paddedpin[i] = 0; + pinvaluelen = i; + xfree (pinvalue); + pinvalue = paddedpin; + } + else + pinvaluelen = strlen (pinvalue); + + err = iso7816_verify (app->slot, + aodf->pin_reference_valid? aodf->pin_reference : 0, + pinvalue, pinvaluelen); + xfree (pinvalue); + if (err) + { + log_error ("PIN verification failed: %s\n", gpg_strerror (err)); + return err; } + log_debug ("PIN verification succeeded\n"); } - return 0; -} + /* Prepare the DER object from INDATA. */ + if (indatalen == 35) + { + /* Alright, the caller was so kind to send us an already + prepared DER object. Check that it is what we want and that + it matches the hash algorithm. */ + if (hashalgo == GCRY_MD_SHA1 && !memcmp (indata, sha1_prefix, 15)) + ; + else if (hashalgo == GCRY_MD_RMD160 + && !memcmp (indata, rmd160_prefix, 15)) + ; + else + return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + memcpy (data, indata, indatalen); + } + else + { + /* Need to prepend the prefix. */ + if (hashalgo == GCRY_MD_SHA1) + memcpy (data, sha1_prefix, 15); + else if (hashalgo == GCRY_MD_RMD160) + memcpy (data, rmd160_prefix, 15); + else + return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + memcpy (data+15, indata, indatalen); + } + + /* Manage security environment needs to be weaked for certain cards. */ + if (app->app_local->card_type == CARD_TYPE_TCOS) + { + /* TCOS creates signatures always using the local key 0. MSE + may not be used. */ + } + else if (app->app_local->card_type == CARD_TYPE_MICARDO) + { + /* Micardo cards are very special in that they need to restore a + security environment using a infomration from a special + file. */ + log_error ("WARNING: support for MICARDO cards is not yet available\n"); + } + else if (prkdf->key_reference_valid) + { + unsigned char mse[3]; + + mse[0] = 0x84; /* Select asym. key. */ + mse[1] = 1; + mse[2] = prkdf->key_reference; + err = iso7816_manage_security_env (app->slot, + 0x41, 0xB6, + mse, sizeof mse); + if (err) + { + log_error ("MSE failed: %s\n", gpg_strerror (err)); + return err; + } + } -/* Release all resources. */ -static void -do_deinit (app_t app) -{ - if (app && app->app_local) - { - xfree (app->app_local); - app->app_local = NULL; - } + err = iso7816_compute_ds (app->slot, data, 35, outdata, outdatalen); + return err; } + + + + + /* Select the PKCS#15 application on the card in SLOT. */ -int -app_select_p15 (APP app) +gpg_error_t +app_select_p15 (app_t app) { static char const aid[] = { 0xA0, 0, 0, 0, 0x63, 0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35 }; int slot = app->slot; int rc; + unsigned short def_home_df = 0; + card_type_t card_type = CARD_TYPE_UNKNOWN; rc = iso7816_select_application (slot, aid, sizeof aid); + if (rc) + { + def_home_df = 0x5015; + rc = iso7816_select_file (slot, def_home_df, 1, NULL, NULL); + } + if (!rc) + { + /* We need to know the ATR for tweaking some security operations. */ + unsigned char *atr; + size_t atrlen; + int i; + + atr = apdu_get_atr (app->slot, &atrlen); + if (!atr) + rc = gpg_error (GPG_ERR_INV_CARD); + else + { + for (i=0; card_atr_list[i].atrlen; i++) + if (card_atr_list[i].atrlen == atrlen + && !memcmp (card_atr_list[i].atr, atr, atrlen)) + { + card_type = card_atr_list[i].type; + break; + } + xfree (atr); + } + } if (!rc) { app->apptype = "P15"; @@ -631,37 +2835,49 @@ app_select_p15 (APP app) goto leave; } + /* Set the home DF. Note that we currently can't do that if the + selection via application ID worked. This will store 0 there + instead. FIXME: We either need to figure the home_df via the + DIR file or using the return values from the select file + APDU. */ + app->app_local->home_df = def_home_df; + + /* Store the card type. FIXME: We might want to put this into + the common APP structure. */ + app->app_local->card_type = card_type; + /* Read basic information and check whether this is a real card. */ rc = read_p15_info (app); - - /* Special serial number munging. We need to do one case here - because we need to access the EF(TokenInfo). */ + if (rc) + goto leave; + + /* Special serial number munging. We need to check for a German + prototype card right here because we need to access to + EF(TokenInfo). We mark such a serial number by the using a + prefix of FF0100. */ if (app->serialnolen == 12 - && !memcmp (app->serial, "\xD2\x76\0\0\0\0\0\0\0\0\0\0", 12)) + && !memcmp (app->serialno, "\xD2\x76\0\0\0\0\0\0\0\0\0\0", 12)) { /* This is a German card with a silly serial number. Try to get - the serial number from the EF(TokenInfo). We indicate such a - serial number by the using the prefix: "FF0100". */ - const char *efser = card->p15card->serial_number; - char *p; - - if (!efser) - efser = ""; + the serial number from the EF(TokenInfo). . */ + unsigned char *p; + + /* FIXME: actually get it from EF(TokenInfo). */ - xfree (*serial); - *serial = NULL; - p = xtrymalloc (strlen (efser) + 7); + p = xtrymalloc (3 + app->serialnolen); if (!p) rc = gpg_error (gpg_err_code_from_errno (errno)); else { - strcpy (p, "FF0100"); - strcpy (p+6, efser); - *serial = p; + memcpy (p, "\xff\x01", 3); + memcpy (p+3, app->serialno, app->serialnolen); + app->serialnolen += 3; + xfree (app->serialno); + app->serialno = p; } } - else + else /* Use standard munging code. */ rc = app_munge_serialno (app); app->fnc.deinit = do_deinit; @@ -672,17 +2888,13 @@ app_select_p15 (APP app) app->fnc.genkey = NULL; app->fnc.sign = do_sign; app->fnc.auth = NULL; - app->fnc.decipher = do_decipher; + app->fnc.decipher = NULL; app->fnc.change_pin = NULL; app->fnc.check_pin = NULL; leave: if (rc) - { - xfree (app->app_local); - app->app_local = NULL; - } - + do_deinit (app); } return rc; diff --git a/scd/app.c b/scd/app.c index e035e9b89..b6d46326b 100644 --- a/scd/app.c +++ b/scd/app.c @@ -125,8 +125,8 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app) rc = app_select_openpgp (app); if (rc && is_app_allowed ("nks") && (!name || !strcmp (name, "nks"))) rc = app_select_nks (app); -/* if (rc && is_app_allowed ("p12") && (!name || !strcmp (name, "p12"))) */ -/* rc = app_select_p12 (app); */ + if (rc && is_app_allowed ("p15") && (!name || !strcmp (name, "p15"))) + rc = app_select_p15 (app); if (rc && is_app_allowed ("dinsig") && (!name || !strcmp (name, "dinsig"))) rc = app_select_dinsig (app); if (rc && name) @@ -177,7 +177,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(TokeInfo is used instead. + serial number from the EF(TokenInfo) is used instead. All other serial number not starting with FF are used as they are. */ diff --git a/scd/command.c b/scd/command.c index 7d777d8b7..c8eebaac0 100644 --- a/scd/command.c +++ b/scd/command.c @@ -129,13 +129,6 @@ do_reset (ctrl_t ctrl, int do_close) { int slot = ctrl->reader_slot; - if (ctrl->card_ctx) - { - card_close (ctrl->card_ctx); - ctrl->card_ctx = NULL; - xfree (ctrl->in_data.value); - ctrl->in_data.value = NULL; - } if (ctrl->app_ctx) { release_application (ctrl->app_ctx); @@ -259,8 +252,6 @@ open_card (ctrl_t ctrl, const char *apptype) if (ctrl->app_ctx) return 0; /* Already initialized for one specific application. */ - if (ctrl->card_ctx) - return 0; /* Already initialized using a card context. */ if ( IS_LOCKED (ctrl) ) return gpg_error (GPG_ERR_LOCKED); @@ -274,19 +265,6 @@ open_card (ctrl_t ctrl, const char *apptype) err = gpg_error (GPG_ERR_CARD); else err = select_application (ctrl, slot, apptype, &ctrl->app_ctx); - if (!ctrl->app_ctx - && gpg_err_code (err) != GPG_ERR_CARD_NOT_PRESENT) - { - /* No application found - fall back to old mode. */ - /* Note that we should rework the old code to use the - application paradigma too. */ - /* If an APPTYPE was requested and it is not pkcs#15, we return - an error here. */ - if (apptype && !(!strcmp (apptype, "P15") || !strcmp (apptype, "p15"))) - err = gpg_error (GPG_ERR_NOT_SUPPORTED); - else - err = card_open (&ctrl->card_ctx); - } TEST_CARD_REMOVAL (ctrl, err); return map_to_assuan_status (err); @@ -367,12 +345,10 @@ cmd_serialno (assuan_context_t ctx, char *line) if ((rc = open_card (ctrl, *line? line:NULL))) return rc; - if (ctrl->app_ctx) - rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp); - else - rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp); + rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp); if (rc) return map_to_assuan_status (rc); + rc = asprintf (&serial_and_stamp, "%s %lu", serial, (unsigned long)stamp); xfree (serial); if (rc < 0) @@ -453,7 +429,6 @@ cmd_learn (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); int rc = 0; - int idx; if ((rc = open_card (ctrl, NULL))) return rc; @@ -467,10 +442,7 @@ cmd_learn (assuan_context_t ctx, char *line) char *serial; time_t stamp; - if (ctrl->app_ctx) - rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp); - else - rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp); + rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp); if (rc) return map_to_assuan_status (rc); rc = asprintf (&serial_and_stamp, "%s %lu", serial, (unsigned long)stamp); @@ -506,87 +478,11 @@ cmd_learn (assuan_context_t ctx, char *line) free (serial_and_stamp); } - /* If we are using the modern application paradigma, let the - application print out its collection of useful status + /* Let the application print out its collection of useful status information. */ - if (!rc && ctrl->app_ctx) + if (!rc) rc = app_write_learn_status (ctrl->app_ctx, ctrl); - /* Return information about the certificates. FIXME: Move this into - an app-p15.c*/ - for (idx=0; !rc && !ctrl->app_ctx; idx++) - { - char *certid; - int certtype; - - rc = card_enum_certs (ctrl->card_ctx, idx, &certid, &certtype); - if (!rc) - { - char *buf; - - buf = xtrymalloc (40 + 1 + strlen (certid) + 1); - if (!buf) - rc = gpg_error (gpg_err_code_from_errno (errno)); - else - { - sprintf (buf, "%d %s", certtype, certid); - assuan_write_status (ctx, "CERTINFO", buf); - xfree (buf); - } - } - xfree (certid); - } - if (rc == -1) - rc = 0; - - /* Return information about the keys. FIXME: Move this into an - app-p15.c */ - for (idx=0; !rc && !ctrl->app_ctx; idx++) - { - unsigned char keygrip[20]; - char *keyid; - int no_cert = 0; - - rc = card_enum_keypairs (ctrl->card_ctx, idx, keygrip, &keyid); - if (gpg_err_code (rc) == GPG_ERR_MISSING_CERT && keyid) - { - /* This does happen with an incomplete personalized - card; i.e. during the time we have stored the key on the - card but not stored the certificate; probably becuase it - has not yet been received back from the CA. Note that we - must release KEYID in this case. */ - rc = 0; - no_cert = 1; - } - if (!rc) - { - char *buf, *p; - - buf = p = xtrymalloc (40 + 1 + strlen (keyid) + 1); - if (!buf) - rc = gpg_error (gpg_err_code_from_errno (errno)); - else - { - int i; - - if (no_cert) - *p++ = 'X'; - else - { - for (i=0; i < 20; i++, p += 2) - sprintf (p, "%02X", keygrip[i]); - } - *p++ = ' '; - strcpy (p, keyid); - assuan_write_status (ctx, "KEYPAIRINFO", buf); - xfree (buf); - } - } - xfree (keyid); - } - if (rc == -1) - rc = 0; - TEST_CARD_REMOVAL (ctrl, rc); return map_to_assuan_status (rc); } @@ -595,7 +491,7 @@ cmd_learn (assuan_context_t ctx, char *line) /* READCERT - Note, that this function may be even be used on a locked card. + Note, that this function may even be used on a locked card. */ static int cmd_readcert (assuan_context_t ctx, char *line) @@ -609,18 +505,9 @@ cmd_readcert (assuan_context_t ctx, char *line) return rc; line = xstrdup (line); /* Need a copy of the line. */ - if (ctrl->app_ctx) - { - rc = app_readcert (ctrl->app_ctx, line, &cert, &ncert); - if (rc) - log_error ("app_readcert failed: %s\n", gpg_strerror (rc)); - } - else - { - rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert); - if (rc) - log_error ("card_read_cert failed: %s\n", gpg_strerror (rc)); - } + rc = app_readcert (ctrl->app_ctx, line, &cert, &ncert); + if (rc) + log_error ("app_readcert failed: %s\n", gpg_strerror (rc)); xfree (line); line = NULL; if (!rc) @@ -641,7 +528,7 @@ cmd_readcert (assuan_context_t ctx, char *line) Return the public key for the given cert or key ID as an standard S-Expression. - Note, that this function may be even be used on a locked card. + Note, that this function may even be used on a locked card. */ static int cmd_readkey (assuan_context_t ctx, char *line) @@ -652,44 +539,34 @@ cmd_readkey (assuan_context_t ctx, char *line) size_t ncert, n; ksba_cert_t kc = NULL; ksba_sexp_t p; + unsigned char *pk; + size_t pklen; if ((rc = open_card (ctrl, NULL))) return rc; line = xstrdup (line); /* Need a copy of the line. */ - if (ctrl->app_ctx) - { - unsigned char *pk; - size_t pklen; - - /* If the application supports the READKEY function we use that. - Otherwise we use the old way by extracting it from the - certificate. */ - rc = app_readkey (ctrl->app_ctx, line, &pk, &pklen); - if (!rc) - { /* Yeah, got that key - send it back. */ - rc = assuan_send_data (ctx, pk, pklen); - xfree (pk); - rc = map_assuan_err (rc); - xfree (line); - line = NULL; - goto leave; - } - - if (gpg_err_code (rc) != GPG_ERR_UNSUPPORTED_OPERATION) - log_error ("app_readkey failed: %s\n", gpg_strerror (rc)); - else - { - rc = app_readcert (ctrl->app_ctx, line, &cert, &ncert); - if (rc) - log_error ("app_readcert failed: %s\n", gpg_strerror (rc)); - } + /* If the application supports the READKEY function we use that. + Otherwise we use the old way by extracting it from the + certificate. */ + rc = app_readkey (ctrl->app_ctx, line, &pk, &pklen); + if (!rc) + { /* Yeah, got that key - send it back. */ + rc = assuan_send_data (ctx, pk, pklen); + xfree (pk); + rc = map_assuan_err (rc); + xfree (line); + line = NULL; + goto leave; } - else + + if (gpg_err_code (rc) != GPG_ERR_UNSUPPORTED_OPERATION) + log_error ("app_readkey failed: %s\n", gpg_strerror (rc)); + else { - rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert); + rc = app_readcert (ctrl->app_ctx, line, &cert, &ncert); if (rc) - log_error ("card_read_cert failed: %s\n", gpg_strerror (rc)); + log_error ("app_readcert failed: %s\n", gpg_strerror (rc)); } xfree (line); line = NULL; @@ -786,8 +663,8 @@ pin_cb (void *opaque, const char *info, char **retstr) if (rc < 0) return gpg_error (gpg_err_code_from_errno (errno)); - /* FIXME: Write an inquire function which returns the result in - secure memory */ + /* Fixme: Write an inquire function which returns the result in + secure memory and check all futher handling of the PIN. */ rc = assuan_inquire (ctx, command, &value, &valuelen, MAXLEN_PIN); free (command); if (rc) @@ -829,18 +706,12 @@ cmd_pksign (assuan_context_t ctx, char *line) if (!keyidstr) return ASSUAN_Out_Of_Core; - if (ctrl->app_ctx) - rc = app_sign (ctrl->app_ctx, - keyidstr, GCRY_MD_SHA1, - pin_cb, ctx, - ctrl->in_data.value, ctrl->in_data.valuelen, - &outdata, &outdatalen); - else - rc = card_sign (ctrl->card_ctx, - keyidstr, GCRY_MD_SHA1, - pin_cb, ctx, - ctrl->in_data.value, ctrl->in_data.valuelen, - &outdata, &outdatalen); + rc = app_sign (ctrl->app_ctx, + keyidstr, GCRY_MD_SHA1, + pin_cb, ctx, + ctrl->in_data.value, ctrl->in_data.valuelen, + &outdata, &outdatalen); + xfree (keyidstr); if (rc) { @@ -929,18 +800,12 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line) keyidstr = xtrystrdup (line); if (!keyidstr) return ASSUAN_Out_Of_Core; - if (ctrl->app_ctx) - rc = app_decipher (ctrl->app_ctx, - keyidstr, - pin_cb, ctx, - ctrl->in_data.value, ctrl->in_data.valuelen, - &outdata, &outdatalen); - else - rc = card_decipher (ctrl->card_ctx, - keyidstr, - pin_cb, ctx, - ctrl->in_data.value, ctrl->in_data.valuelen, - &outdata, &outdatalen); + rc = app_decipher (ctrl->app_ctx, + keyidstr, + pin_cb, ctx, + ctrl->in_data.value, ctrl->in_data.valuelen, + &outdata, &outdatalen); + xfree (keyidstr); if (rc) { diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 0fefbd960..7b0f31cdb 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -108,7 +108,6 @@ static ARGPARSE_OPTS opts[] = { { oDebugAll, "debug-all" ,0, "@"}, { oDebugLevel, "debug-level" ,2, "@"}, { oDebugWait,"debug-wait",1, "@"}, - { 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", 2, N_("|N|connect to reader at port N")}, @@ -121,17 +120,14 @@ static ARGPARSE_OPTS opts[] = { "@" #endif /* end --disable-ccid */}, - { oDisableOpenSC, "disable-opensc", 0, -#ifdef HAVE_OPENSC - N_("do not use the OpenSC layer") -#else - "@" -#endif - /* end --disable-opensc */}, { oAllowAdmin, "allow-admin", 0, N_("allow the use of admin card commands")}, { oDenyAdmin, "deny-admin", 0, "@" }, { oDisableApplication, "disable-application", 2, "@"}, + /* Dummy options to be removed at some point. */ + { oDebugSC, "debug-sc", 1, "@" }, + { oDisableOpenSC, "disable-opensc", 0, "@" }, + {0} }; @@ -156,14 +152,12 @@ static int maybe_setuid = 1; static char socket_name[128]; -#ifndef HAVE_OPENSC #ifdef USE_GNU_PTH /* Pth wrapper function definitions. */ GCRY_THREAD_OPTION_PTH_IMPL; static void *ticker_thread (void *arg); #endif /*USE_GNU_PTH*/ -#endif /*!HAVE_OPENSC*/ static const char * my_strusage (int level) @@ -349,7 +343,6 @@ main (int argc, char **argv ) /* Libgcrypt requires us to register the threading model first. Note that this will also do the pth_init. */ -#ifndef HAVE_OPENSC #ifdef USE_GNU_PTH err = gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth); if (err) @@ -358,7 +351,6 @@ main (int argc, char **argv ) gpg_strerror (err)); } #endif /*USE_GNU_PTH*/ -#endif /*!HAVE_OPENSC*/ /* Check that the libraries are suitable. Do it here because the option parsing may need services of the library */ @@ -469,7 +461,7 @@ main (int argc, char **argv ) case oDebugAll: opt.debug = ~0; break; case oDebugLevel: debug_level = pargs.r.ret_str; break; case oDebugWait: debug_wait = pargs.r.ret_int; break; - case oDebugSC: opt.debug_sc = pargs.r.ret_int; break; + case oDebugSC: break; case oOptions: /* config files may not be nested (silently ignore them) */ @@ -495,7 +487,7 @@ main (int argc, char **argv ) case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break; case opcscDriver: opt.pcsc_driver = pargs.r.ret_str; break; case oDisableCCID: opt.disable_ccid = 1; break; - case oDisableOpenSC: opt.disable_opensc = 1; break; + case oDisableOpenSC: break; case oAllowAdmin: opt.allow_admin = 1; break; case oDenyAdmin: opt.allow_admin = 0; break; @@ -592,9 +584,6 @@ main (int argc, char **argv ) GC_OPT_FLAG_DEFAULT, DEFAULT_PCSC_DRIVER ); #ifdef HAVE_LIBUSB printf ("disable-ccid:%lu:\n", GC_OPT_FLAG_NONE ); -#endif -#ifdef HAVE_LIBUSB - printf ("disable-opensc:%lu:\n", GC_OPT_FLAG_NONE ); #endif printf ("allow-admin:%lu:\n", GC_OPT_FLAG_NONE ); @@ -612,7 +601,6 @@ main (int argc, char **argv ) if (pipe_server) { /* This is the simple pipe based server */ -#ifndef HAVE_OPENSC #ifdef USE_GNU_PTH pth_attr_t tattr; @@ -627,7 +615,6 @@ main (int argc, char **argv ) scd_exit (2); } #endif /*USE_GNU_PTH*/ -#endif /*!HAVE_OPENSC*/ scd_command_handler (-1); } else if (!is_daemon) @@ -859,7 +846,6 @@ scd_init_default_ctrl (CTRL ctrl) } -#ifndef HAVE_OPENSC #ifdef USE_GNU_PTH static void @@ -971,4 +957,3 @@ ticker_thread (void *dummy_arg) return NULL; } #endif /*USE_GNU_PTH*/ -#endif /*!HAVE_OPENSC*/ diff --git a/scd/scdaemon.h b/scd/scdaemon.h index c59879448..5e49f3ae5 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -42,7 +42,6 @@ /* A large struct name "opt" to keep global flags */ struct { unsigned int debug; /* debug flags (DBG_foo_VALUE) */ - int debug_sc; /* OpenSC debug level */ int verbose; /* verbosity level */ int quiet; /* be as quiet as possible */ int dry_run; /* don't change any persistent data */ @@ -51,7 +50,6 @@ struct { const char *ctapi_driver; /* Library to access the ctAPI. */ const char *pcsc_driver; /* Library to access the PC/SC system. */ const char *reader_port; /* NULL or reder port to use. */ - int disable_opensc; /* Disable the use of the OpenSC framework. */ int disable_ccid; /* Disable the use of the internal CCID driver. */ int allow_admin; /* Allow the use of admin commands for certain cards. */ @@ -96,7 +94,6 @@ struct server_control_s { typedef struct server_control_s *CTRL; typedef struct server_control_s *ctrl_t; -typedef struct card_ctx_s *CARD; typedef struct app_ctx_s *APP; typedef struct app_ctx_s *app_t; @@ -109,27 +106,5 @@ void scd_command_handler (int); void send_status_info (CTRL ctrl, const char *keyword, ...); void scd_update_reader_status_file (void); -/*-- card.c --*/ -int card_open (CARD *rcard); -void card_close (CARD card); -int card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp); -int card_enum_keypairs (CARD card, int idx, - unsigned char *keygrip, - char **keyid); -int card_enum_certs (CARD card, int idx, char **certid, int *certtype); -int card_read_cert (CARD card, const char *certidstr, - unsigned char **cert, size_t *ncert); -int card_sign (CARD card, - const char *keyidstr, int hashalgo, - int (pincb)(void*, const char *, char **), - void *pincb_arg, - const void *indata, size_t indatalen, - unsigned char **outdata, size_t *outdatalen ); -int card_decipher (CARD card, const char *keyidstr, - int (pincb)(void*, const char *, char **), - void *pincb_arg, - const void *indata, size_t indatalen, - unsigned char **outdata, size_t *outdatalen); - #endif /*SCDAEMON_H*/ -- cgit From 4237a9cc7fce3bad2a41b755fdf349a42ddd5ccf Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 18 May 2005 10:48:06 +0000 Subject: Changed the scdaemon to handle concurrent sessions. Adjusted gpg-agent accordingly. Code cleanups. --- ChangeLog | 5 + NEWS | 4 +- TODO | 14 +- agent/ChangeLog | 16 ++ agent/agent.h | 9 + agent/call-scd.c | 397 +++++++++++++++++++++++-------------- agent/command.c | 1 + agent/divert-scd.c | 7 - agent/gpg-agent.c | 96 ++------- configure.ac | 13 +- doc/ChangeLog | 4 + doc/gpg-agent.texi | 5 - doc/tools.texi | 10 + scd/ChangeLog | 36 ++++ scd/apdu.c | 4 + scd/app-common.h | 101 +++++----- scd/app-dinsig.c | 20 +- scd/app-nks.c | 24 +-- scd/app-openpgp.c | 58 +++--- scd/app-p15.c | 8 +- scd/app.c | 341 ++++++++++++++++++++++---------- scd/ccid-driver.c | 9 +- scd/command.c | 43 +++- scd/sc-copykeys.c | 2 +- scd/scdaemon.c | 491 ++++++++++++++++++++++++++++++---------------- scd/scdaemon.h | 3 +- tools/gpg-connect-agent.c | 20 +- 27 files changed, 1095 insertions(+), 646 deletions(-) (limited to 'scd/scdaemon.c') diff --git a/ChangeLog b/ChangeLog index 4f58b9198..154ae07e9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2005-05-15 Werner Koch + + * configure.ac: Remove option --disable-threads; require the use + of GNU Pth. + 2005-04-27 Werner Koch * configure.ac: Removed OpenSC detection and options. diff --git a/NEWS b/NEWS index 2d43d70ff..c25bbe08e 100644 --- a/NEWS +++ b/NEWS @@ -1,8 +1,10 @@ Noteworthy changes in version 1.9.17 ------------------------------------------------- + * GNU Pth is now a hard requirement. + * [scdaemon] Support for OpenSC has been removed. Instead a new and - starightforward pkcs#15 modules has been written. As of now it + straightforward pkcs#15 modules has been written. As of now it does allows only signing using TCOS cards but we are going to enhance it to match all the old capabilities. diff --git a/TODO b/TODO index 6a0e9b18e..6e8951f03 100644 --- a/TODO +++ b/TODO @@ -35,6 +35,9 @@ might want to have an agent context for each service request to do that while changing gpgsm to allow concurrent operations. ** support the anyPolicy semantic ** Check that we are really following the verification procedures in rfc3280. +** Implement a --card-status command. + This is useful to check whether a card is supported at all. + * sm/keydb.c ** Check file permissions @@ -54,10 +57,6 @@ might want to have an agent context for each service request ** Don't use stdio to return results. ** Support DSA -* agent/divert-scd.c - Remove the agent_reset_scd kludge. We will do this after Scdaemon - has been changed to allow multiple sessions. Currently in progress. - * Move pkcs-1 encoding into libgcrypt. * Use a MAC to protect sensitive files. @@ -90,10 +89,3 @@ might want to have an agent context for each service request This means we can't reread a configuration ** No card status notifications. - -* scd/ -** Release the card after use so that gpg 1.4 is able to access it - This won't be a sufficient change. we need to change gpg 1.4 to make - use of the agent. Work is underway. - - diff --git a/agent/ChangeLog b/agent/ChangeLog index f5dbeb9e3..00f019ddc 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,19 @@ +2005-05-18 Werner Koch + + * divert-scd.c (ask_for_card): Removed the card reset kludge. + +2005-05-17 Werner Koch + + * call-scd.c (unlock_scd): Add new arg CTRL. Changed all callers. + (start_scd): Reoworked to allow for additional connections. + * agent.h (ctrl_t): Add local data for the SCdaemon. + * command.c (start_command_handler): Release SERVER_LOCAL. + + * gpg-agent.c (create_server_socket): Use xmalloc. + (main): Removed option --disable-pth a dummy. Removed non-pth + code path. + (cleanup_sh): Removed. Not needed anymore. + 2005-05-05 Moritz Schulte * command-ssh.c (ssh_key_to_buffer): Rename to ... diff --git a/agent/agent.h b/agent/agent.h index 298b5b142..6ab65eeba 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -99,10 +99,19 @@ struct { #define DBG_ASSUAN (opt.debug & DBG_ASSUAN_VALUE) struct server_local_s; +struct scd_local_s; +/* Collection of data per session (aka connection). */ struct server_control_s { + + /* Private data of the server (command.c). */ struct server_local_s *server_local; + + /* Private data of the SCdaemon (call-scd.c). */ + struct scd_local_s *scd_local; + int connection_fd; /* -1 or an identifier for the current connection. */ + char *display; char *ttyname; char *ttytype; diff --git a/agent/call-scd.c b/agent/call-scd.c index 8373fd46d..58dd412f0 100644 --- a/agent/call-scd.c +++ b/agent/call-scd.c @@ -18,12 +18,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -/* Fixme: For now we have serialized all access to the scdaemon which - make sense becuase the scdaemon can't handle concurrent connections - right now. We should however keep a list of connections and lock - just that connection - it migth make sense to implemtn parts of - this in Assuan.*/ - #include #include #include @@ -37,9 +31,7 @@ #ifndef HAVE_W32_SYSTEM #include #endif -#ifdef USE_GNU_PTH -# include -#endif +#include #include "agent.h" #include @@ -50,24 +42,20 @@ #define MAX_OPEN_FDS 20 #endif -static ASSUAN_CONTEXT scd_ctx = NULL; -#ifdef USE_GNU_PTH -static pth_mutex_t scd_lock; -#endif -/* We need to keep track of the connection currently using the SCD. - For a pipe server this is all a NOP because the connection will - always have the connection indicator -1. agent_reset_scd releases - the active connection; i.e. sets it back to -1, so that a new - connection can start using the SCD. If we eventually allow - multiple SCD session we will either make scdaemon multi-threaded or - fork of a new scdaemon and let it see how it can get access to a - reader. -*/ -static int active_connection_fd = -1; -static int active_connection = 0; +/* Definition of module local data of the CTRL structure. */ +struct scd_local_s +{ + assuan_context_t ctx; /* NULL or session context for the SCdaemon + used with this connection. */ + int locked; /* This flag is used to assert proper use of + start_scd and unlock_scd. */ + +}; + /* Callback parameter for learn card */ -struct learn_parm_s { +struct learn_parm_s +{ void (*kpinfo_cb)(void*, const char *); void *kpinfo_cb_arg; void (*certinfo_cb)(void*, const char *); @@ -76,13 +64,39 @@ struct learn_parm_s { void *sinfo_cb_arg; }; -struct inq_needpin_s { - ASSUAN_CONTEXT ctx; +struct inq_needpin_s +{ + assuan_context_t ctx; int (*getpin_cb)(void *, const char *, char*, size_t); void *getpin_cb_arg; }; +/* A Mutex used inside the start_scd function. */ +static pth_mutex_t start_scd_lock; + +/* A malloced string with the name of the socket to be used for + additional connections. May be NULL if not provided by + SCdaemon. */ +static char *socket_name; + +/* The context of the primary connection. This is also used as a flag + to indicate whether the scdaemon has been started. */ +static assuan_context_t primary_scd_ctx; + +/* To allow reuse of the primary connection, the following flag is set + to true if the primary context has been reset and is not in use by + any connection. */ +static int primary_scd_ctx_reusable; + + + +/* Local prototypes. */ +static assuan_error_t membuf_data_cb (void *opaque, + const void *buffer, size_t length); + + + /* This function must be called once to initialize this module. This has to be done before a second thread is spawned. We can't do the @@ -91,27 +105,35 @@ struct inq_needpin_s { void initialize_module_call_scd (void) { -#ifdef USE_GNU_PTH static int initialized; if (!initialized) - if (pth_mutex_init (&scd_lock)) + { + if (!pth_mutex_init (&start_scd_lock)) + log_fatal ("error initializing mutex: %s\n", strerror (errno)); initialized = 1; -#endif /*USE_GNU_PTH*/ + } } +/* The unlock_scd function shall be called after having accessed the + SCD. It is currently not very useful but gives an opportunity to + keep track of connections currently calling SCD. Note that the + "lock" operation is done by the start_scd() function which must be + called and error checked before any SCD operation. CTRL is the + usual connection context and RC the error code to be passed trhough + the function. */ static int -unlock_scd (int rc) +unlock_scd (ctrl_t ctrl, int rc) { -#ifdef USE_GNU_PTH - if (!pth_mutex_release (&scd_lock)) + if (ctrl->scd_local->locked != 1) { - log_error ("failed to release the SCD lock\n"); + log_error ("unlock_scd: invalid lock count (%d)\n", + ctrl->scd_local->locked); if (!rc) rc = gpg_error (GPG_ERR_INTERNAL); } -#endif /*USE_GNU_PTH*/ + ctrl->scd_local->locked = 0; return rc; } @@ -125,68 +147,115 @@ atfork_cb (void *opaque, int where) } -/* Fork off the SCdaemon if this has not already been done. Note that - this fucntion alos locks the daemon. */ +/* Fork off the SCdaemon if this has not already been done. Lock the + daemon and make sure that a proper context has been setup in CTRL. + Thsi fucntion might also lock the daemon, which means that the + caller must call unlock_scd after this fucntion has returned + success and the actual Assuan transaction been done. */ static int start_scd (ctrl_t ctrl) { - int rc; + gpg_error_t err = 0; const char *pgmname; - ASSUAN_CONTEXT ctx; - const char *argv[3]; + assuan_context_t ctx; + const char *argv[4]; int no_close_list[3]; int i; + int rc; if (opt.disable_scdaemon) return gpg_error (GPG_ERR_NOT_SUPPORTED); -#ifdef USE_GNU_PTH - if (!pth_mutex_acquire (&scd_lock, 0, NULL)) + /* If this is the first call for this session, setup the local data + structure. */ + if (!ctrl->scd_local) + { + ctrl->scd_local = xtrycalloc (1, sizeof *ctrl->scd_local); + if (!ctrl->scd_local) + return gpg_error_from_errno (errno); + } + + + /* Assert that the lock count is as expected. */ + if (ctrl->scd_local->locked) { - log_error ("failed to acquire the SCD lock\n"); + log_error ("start_scd: invalid lock count (%d)\n", + ctrl->scd_local->locked); return gpg_error (GPG_ERR_INTERNAL); } -#endif + ctrl->scd_local->locked++; - if (scd_ctx) + /* If we already have a context, we better do a sanity check now to + see whether it has accidently died. This avoids annoying + timeouts and hung connections. */ + if (ctrl->scd_local->ctx) { pid_t pid; - - /* If we are not the connection currently using the SCD, return - an error. */ - if (!active_connection) - { - active_connection_fd = ctrl->connection_fd; - active_connection = 1; - } - else if (ctrl->connection_fd != active_connection_fd) - return unlock_scd (gpg_error (GPG_ERR_CONFLICT)); - - /* Okay, we already started the scdaemon and it is used by us.*/ - - /* We better do a sanity check now to see whether it has - accidently died. */ #ifndef HAVE_W32_SYSTEM - pid = assuan_get_pid (scd_ctx); + pid = assuan_get_pid (ctrl->scd_local->ctx); if (pid != (pid_t)(-1) && pid && ((rc=waitpid (pid, NULL, WNOHANG))==-1 || (rc == pid)) ) { - assuan_disconnect (scd_ctx); - scd_ctx = NULL; + assuan_disconnect (ctrl->scd_local->ctx); + ctrl->scd_local->ctx = NULL; } else #endif - return 0; + return 0; /* Okay, the context is fine. */ + } + + /* We need to protect the lowwing code. */ + if (!pth_mutex_acquire (&start_scd_lock, 0, NULL)) + { + log_error ("failed to acquire the start_scd lock: %s\n", + strerror (errno)); + return gpg_error (GPG_ERR_INTERNAL); + } + + /* Check whether the pipe server has already been started and in + this case either reuse a lingering pipe connection or establish a + new socket based one. */ + if (primary_scd_ctx && primary_scd_ctx_reusable) + { + ctx = primary_scd_ctx; + primary_scd_ctx_reusable = 0; + if (opt.verbose) + log_info ("new connection to SCdaemon established (reusing)\n"); + goto leave; } + if (socket_name) + { + rc = assuan_socket_connect (&ctx, socket_name, 0); + if (rc) + { + log_error ("can't connect to socket `%s': %s\n", + socket_name, assuan_strerror (rc)); + err = gpg_error (GPG_ERR_NO_SCDAEMON); + goto leave; + } + + if (opt.verbose) + log_info ("new connection to SCdaemon established\n"); + goto leave; + } + + if (primary_scd_ctx) + { + log_info ("SCdaemon is running but won't accept further connections\n"); + err = gpg_error (GPG_ERR_NO_SCDAEMON); + goto leave; + } + + /* Nope, it has not been started. Fire it up now. */ if (opt.verbose) log_info ("no running SCdaemon - starting it\n"); if (fflush (NULL)) { - gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); + err = gpg_error (gpg_err_code_from_errno (errno)); log_error ("error flushing pending output: %s\n", strerror (errno)); - return unlock_scd (tmperr); + goto leave; } if (!opt.scdaemon_program || !*opt.scdaemon_program) @@ -198,7 +267,8 @@ start_scd (ctrl_t ctrl) argv[0] = pgmname; argv[1] = "--server"; - argv[2] = NULL; + argv[2] = "--multi-server"; + argv[3] = NULL; i=0; if (!opt.running_detached) @@ -216,30 +286,68 @@ start_scd (ctrl_t ctrl) { log_error ("can't connect to the SCdaemon: %s\n", assuan_strerror (rc)); - return unlock_scd (gpg_error (GPG_ERR_NO_SCDAEMON)); + err = gpg_error (GPG_ERR_NO_SCDAEMON); + goto leave; } - scd_ctx = ctx; - active_connection_fd = ctrl->connection_fd; - active_connection = 1; - - if (DBG_ASSUAN) - log_debug ("connection to SCdaemon established\n"); - - /* Tell the scdaemon that we want him to send us an event signal. - But only do this if we are running as a regular sever and not - simply as a pipe server. */ - /* Fixme: gpg-agent does not use this signal yet. */ -/* if (ctrl->connection_fd != -1) */ -/* { */ -/* #ifndef HAVE_W32_SYSTEM */ -/* char buf[100]; */ - -/* sprintf (buf, "OPTION event-signal=%d", SIGUSR2); */ -/* assuan_transact (scd_ctx, buf, NULL, NULL, NULL, NULL, NULL, NULL); */ -/* #endif */ -/* } */ - return 0; + if (opt.verbose) + log_debug ("first connection to SCdaemon established\n"); + + /* Get the name of the additional socket opened by scdaemon. */ + { + membuf_t data; + unsigned char *databuf; + size_t datalen; + + xfree (socket_name); + socket_name = NULL; + init_membuf (&data, 256); + assuan_transact (ctx, "GETINFO socket_name", + membuf_data_cb, &data, NULL, NULL, NULL, NULL); + + databuf = get_membuf (&data, &datalen); + if (databuf && datalen) + { + socket_name = xtrymalloc (datalen + 1); + if (!socket_name) + log_error ("warning: can't store socket name: %s\n", + strerror (errno)); + else + { + memcpy (socket_name, databuf, datalen); + socket_name[datalen] = 0; + if (DBG_ASSUAN) + log_debug ("additional connections at `%s'\n", socket_name); + } + } + xfree (databuf); + } + + /* Tell the scdaemon we want him to send us an event signal. */ +#ifndef HAVE_W32_SYSTEM + { + char buf[100]; + + sprintf (buf, "OPTION event-signal=%d", SIGUSR2); + assuan_transact (ctx, buf, NULL, NULL, NULL, NULL, NULL, NULL); + } +#endif + + primary_scd_ctx = ctx; + primary_scd_ctx_reusable = 0; + + leave: + if (err) + { + unlock_scd (ctrl, err); + } + else + { + ctrl->scd_local->ctx = ctx; + } + if (!pth_mutex_release (&start_scd_lock)) + log_error ("failed to release the start_scd lock: %s\n", strerror (errno)); + return err; } @@ -248,25 +356,28 @@ start_scd (ctrl_t ctrl) int agent_reset_scd (ctrl_t ctrl) { - int rc = 0; - -#ifdef USE_GNU_PTH - if (!pth_mutex_acquire (&scd_lock, 0, NULL)) - { - log_error ("failed to acquire the SCD lock for reset\n"); - return gpg_error (GPG_ERR_INTERNAL); - } -#endif - if (active_connection && active_connection_fd == ctrl->connection_fd) + if (ctrl->scd_local) { - if (scd_ctx) - rc = assuan_transact (scd_ctx, "RESET", NULL, NULL, - NULL, NULL, NULL, NULL); - active_connection_fd = -1; - active_connection = 0; + if (ctrl->scd_local->ctx) + { + /* We can't disconnect the primary context becuase libassuan + does a waitpid on it and thus the system would hang. + Instead we send a reset and keep that connection for + reuse. */ + if (ctrl->scd_local->ctx == primary_scd_ctx) + { + if (!assuan_transact (primary_scd_ctx, "RESET", + NULL, NULL, NULL, NULL, NULL, NULL)) + primary_scd_ctx_reusable = 1; + } + else + assuan_disconnect (ctrl->scd_local->ctx); + } + xfree (ctrl->scd_local); + ctrl->scd_local = NULL; } - return unlock_scd (map_assuan_err (rc)); + return 0; } @@ -360,13 +471,13 @@ agent_card_learn (ctrl_t ctrl, parm.certinfo_cb_arg = certinfo_cb_arg; parm.sinfo_cb = sinfo_cb; parm.sinfo_cb_arg = sinfo_cb_arg; - rc = assuan_transact (scd_ctx, "LEARN --force", + rc = assuan_transact (ctrl->scd_local->ctx, "LEARN --force", NULL, NULL, NULL, NULL, learn_status_cb, &parm); if (rc) - return unlock_scd (map_assuan_err (rc)); + return unlock_scd (ctrl, map_assuan_err (rc)); - return unlock_scd (0); + return unlock_scd (ctrl, 0); } @@ -414,16 +525,16 @@ agent_card_serialno (ctrl_t ctrl, char **r_serialno) if (rc) return rc; - rc = assuan_transact (scd_ctx, "SERIALNO", + rc = assuan_transact (ctrl->scd_local->ctx, "SERIALNO", NULL, NULL, NULL, NULL, get_serialno_cb, &serialno); if (rc) { xfree (serialno); - return unlock_scd (map_assuan_err (rc)); + return unlock_scd (ctrl, map_assuan_err (rc)); } *r_serialno = serialno; - return unlock_scd (0); + return unlock_scd (ctrl, 0); } @@ -495,31 +606,32 @@ agent_card_pksign (ctrl_t ctrl, return rc; if (indatalen*2 + 50 > DIM(line)) - return unlock_scd (gpg_error (GPG_ERR_GENERAL)); + return unlock_scd (ctrl, gpg_error (GPG_ERR_GENERAL)); sprintf (line, "SETDATA "); p = line + strlen (line); for (i=0; i < indatalen ; i++, p += 2 ) sprintf (p, "%02X", indata[i]); - rc = assuan_transact (scd_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + rc = assuan_transact (ctrl->scd_local->ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL); if (rc) - return unlock_scd (map_assuan_err (rc)); + return unlock_scd (ctrl, map_assuan_err (rc)); init_membuf (&data, 1024); - inqparm.ctx = scd_ctx; + inqparm.ctx = ctrl->scd_local->ctx; inqparm.getpin_cb = getpin_cb; inqparm.getpin_cb_arg = getpin_cb_arg; snprintf (line, DIM(line)-1, ctrl->use_auth_call? "PKAUTH %s":"PKSIGN %s", keyid); line[DIM(line)-1] = 0; - rc = assuan_transact (scd_ctx, line, + rc = assuan_transact (ctrl->scd_local->ctx, line, membuf_data_cb, &data, inq_needpin, &inqparm, NULL, NULL); if (rc) { xfree (get_membuf (&data, &len)); - return unlock_scd (map_assuan_err (rc)); + return unlock_scd (ctrl, map_assuan_err (rc)); } sigbuf = get_membuf (&data, &sigbuflen); @@ -531,7 +643,7 @@ agent_card_pksign (ctrl_t ctrl, { gpg_error_t tmperr = out_of_core (); xfree (*r_buf); - return unlock_scd (tmperr); + return unlock_scd (ctrl, tmperr); } p = stpcpy (*r_buf, "(7:sig-val(3:rsa(1:s" ); sprintf (p, "%u:", (unsigned int)sigbuflen); @@ -542,7 +654,7 @@ agent_card_pksign (ctrl_t ctrl, xfree (sigbuf); assert (gcry_sexp_canon_len (*r_buf, *r_buflen, NULL, NULL)); - return unlock_scd (0); + return unlock_scd (ctrl, 0); } /* Decipher INDATA using the current card. Note that the returned value is */ @@ -567,36 +679,37 @@ agent_card_pkdecrypt (ctrl_t ctrl, /* FIXME: use secure memory where appropriate */ if (indatalen*2 + 50 > DIM(line)) - return unlock_scd (gpg_error (GPG_ERR_GENERAL)); + return unlock_scd (ctrl, gpg_error (GPG_ERR_GENERAL)); sprintf (line, "SETDATA "); p = line + strlen (line); for (i=0; i < indatalen ; i++, p += 2 ) sprintf (p, "%02X", indata[i]); - rc = assuan_transact (scd_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + rc = assuan_transact (ctrl->scd_local->ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL); if (rc) - return unlock_scd (map_assuan_err (rc)); + return unlock_scd (ctrl, map_assuan_err (rc)); init_membuf (&data, 1024); - inqparm.ctx = scd_ctx; + inqparm.ctx = ctrl->scd_local->ctx; inqparm.getpin_cb = getpin_cb; inqparm.getpin_cb_arg = getpin_cb_arg; snprintf (line, DIM(line)-1, "PKDECRYPT %s", keyid); line[DIM(line)-1] = 0; - rc = assuan_transact (scd_ctx, line, + rc = assuan_transact (ctrl->scd_local->ctx, line, membuf_data_cb, &data, inq_needpin, &inqparm, NULL, NULL); if (rc) { xfree (get_membuf (&data, &len)); - return unlock_scd (map_assuan_err (rc)); + return unlock_scd (ctrl, map_assuan_err (rc)); } *r_buf = get_membuf (&data, r_buflen); if (!*r_buf) - return unlock_scd (gpg_error (GPG_ERR_ENOMEM)); + return unlock_scd (ctrl, gpg_error (GPG_ERR_ENOMEM)); - return unlock_scd (0); + return unlock_scd (ctrl, 0); } @@ -619,20 +732,20 @@ agent_card_readcert (ctrl_t ctrl, init_membuf (&data, 1024); snprintf (line, DIM(line)-1, "READCERT %s", id); line[DIM(line)-1] = 0; - rc = assuan_transact (scd_ctx, line, + rc = assuan_transact (ctrl->scd_local->ctx, line, membuf_data_cb, &data, NULL, NULL, NULL, NULL); if (rc) { xfree (get_membuf (&data, &len)); - return unlock_scd (map_assuan_err (rc)); + return unlock_scd (ctrl, map_assuan_err (rc)); } *r_buf = get_membuf (&data, r_buflen); if (!*r_buf) - return unlock_scd (gpg_error (GPG_ERR_ENOMEM)); + return unlock_scd (ctrl, gpg_error (GPG_ERR_ENOMEM)); - return unlock_scd (0); + return unlock_scd (ctrl, 0); } @@ -655,26 +768,26 @@ agent_card_readkey (ctrl_t ctrl, const char *id, unsigned char **r_buf) init_membuf (&data, 1024); snprintf (line, DIM(line)-1, "READKEY %s", id); line[DIM(line)-1] = 0; - rc = assuan_transact (scd_ctx, line, + rc = assuan_transact (ctrl->scd_local->ctx, line, membuf_data_cb, &data, NULL, NULL, NULL, NULL); if (rc) { xfree (get_membuf (&data, &len)); - return unlock_scd (map_assuan_err (rc)); + return unlock_scd (ctrl, map_assuan_err (rc)); } *r_buf = get_membuf (&data, &buflen); if (!*r_buf) - return unlock_scd (gpg_error (GPG_ERR_ENOMEM)); + return unlock_scd (ctrl, gpg_error (GPG_ERR_ENOMEM)); if (!gcry_sexp_canon_len (*r_buf, buflen, NULL, NULL)) { xfree (*r_buf); *r_buf = NULL; - return unlock_scd (gpg_error (GPG_ERR_INV_VALUE)); + return unlock_scd (ctrl, gpg_error (GPG_ERR_INV_VALUE)); } - return unlock_scd (0); + return unlock_scd (ctrl, 0); } @@ -744,7 +857,7 @@ agent_card_getattr (ctrl_t ctrl, const char *name, char **result) if (err) return err; - err = map_assuan_err (assuan_transact (scd_ctx, line, + err = map_assuan_err (assuan_transact (ctrl->scd_local->ctx, line, NULL, NULL, NULL, NULL, card_getattr_cb, &parm)); if (!err && parm.error) @@ -758,7 +871,7 @@ agent_card_getattr (ctrl_t ctrl, const char *name, char **result) else xfree (parm.data); - return unlock_scd (err); + return unlock_scd (ctrl, err); } @@ -810,19 +923,19 @@ agent_card_scd (ctrl_t ctrl, const char *cmdline, if (rc) return rc; - inqparm.ctx = scd_ctx; + inqparm.ctx = ctrl->scd_local->ctx; inqparm.getpin_cb = getpin_cb; inqparm.getpin_cb_arg = getpin_cb_arg; - rc = assuan_transact (scd_ctx, cmdline, + rc = assuan_transact (ctrl->scd_local->ctx, cmdline, pass_data_thru, assuan_context, inq_needpin, &inqparm, pass_status_thru, assuan_context); if (rc) { - return unlock_scd (map_assuan_err (rc)); + return unlock_scd (ctrl, map_assuan_err (rc)); } - return unlock_scd (0); + return unlock_scd (ctrl, 0); } diff --git a/agent/command.c b/agent/command.c index 997140207..8af159f6d 100644 --- a/agent/command.c +++ b/agent/command.c @@ -1061,5 +1061,6 @@ start_command_handler (int listen_fd, int fd) free (ctrl.lc_ctype); if (ctrl.lc_messages) free (ctrl.lc_messages); + xfree (ctrl.server_local); } diff --git a/agent/divert-scd.c b/agent/divert-scd.c index f2ec2f051..f460ffe0c 100644 --- a/agent/divert-scd.c +++ b/agent/divert-scd.c @@ -108,13 +108,6 @@ ask_for_card (CTRL ctrl, const unsigned char *shadow_info, char **r_kid) if (!rc) { - /* We better reset the SCD now. This is kludge required - because the scdaemon is currently not always able to - detect the presence of a card. With a fully working - scdaemon this would not be required; i.e. the pkcs#15 - support does not require it because OpenSC correclty - detects a present card. */ - agent_reset_scd (ctrl); if (asprintf (&desc, "%s:%%0A%%0A" " \"%.*s\"", diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 7c682ada7..4ac995c26 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -37,9 +37,7 @@ #endif /*HAVE_W32_SYSTEM*/ #include #include -#ifdef USE_GNU_PTH -# include -#endif +#include #define JNLIB_NEED_LOG_LOGV #include "agent.h" @@ -83,7 +81,6 @@ enum cmd_and_opt_values oLCctype, oLCmessages, oScdaemonProgram, - oDisablePth, oDefCacheTTL, oMaxCacheTTL, oUseStandardSocket, @@ -120,7 +117,6 @@ static ARGPARSE_OPTS opts[] = { { oNoDetach, "no-detach" ,0, N_("do not detach from the console")}, { oNoGrab, "no-grab" ,0, N_("do not grab keyboard and mouse")}, { oLogFile, "log-file" ,2, N_("use a log file for the server")}, - { oDisablePth, "disable-pth", 0, N_("do not allow multiple connections")}, { oUseStandardSocket, "use-standard-socket", 0, N_("use a standard location for the socket")}, { oNoUseStandardSocket, "no-use-standard-socket", 0, "@"}, @@ -157,7 +153,6 @@ static ARGPARSE_OPTS opts[] = { #define DEFAULT_CACHE_TTL (10*60) /* 10 minutes */ #define MAX_CACHE_TTL (120*60) /* 2 hours */ -static volatile int caught_fatal_sig = 0; /* flag to indicate that a shutdown was requested */ static int shutdown_pending; @@ -190,10 +185,11 @@ static const char *debug_level; static char *current_logfile; /* The handle_tick() function may test whether a parent is still - runing. We record the PID of the parent here or -1 if it should be + running. We record the PID of the parent here or -1 if it should be watched. */ static pid_t parent_pid = (pid_t)(-1); + /* Local prototypes. */ @@ -203,17 +199,15 @@ static char *create_socket_name (int use_standard_socket, static int create_server_socket (int is_standard_name, const char *name); static void create_directories (void); -#ifdef USE_GNU_PTH static void handle_connections (int listen_fd, int listen_fd_ssh); -/* Pth wrapper function definitions. */ -GCRY_THREAD_OPTION_PTH_IMPL; -#endif /*USE_GNU_PTH*/ - static int check_for_running_agent (int); +/* Pth wrapper function definitions. */ +GCRY_THREAD_OPTION_PTH_IMPL; + /* Functions. */ @@ -351,28 +345,6 @@ cleanup (void) } -static RETSIGTYPE -cleanup_sh (int sig) -{ - if (caught_fatal_sig) - raise (sig); - caught_fatal_sig = 1; - - /* gcry_control( GCRYCTL_TERM_SECMEM );*/ - cleanup (); - -#ifndef HAVE_DOSISH_SYSTEM - { /* reset action to default action and raise signal again */ - struct sigaction nact; - nact.sa_handler = SIG_DFL; - sigemptyset( &nact.sa_mask ); - nact.sa_flags = 0; - sigaction( sig, &nact, NULL); - } -#endif - raise( sig ); -} - /* Handle options which are allowed to be reset after program start. Return true when the current option in PARGS could be handled and @@ -462,7 +434,6 @@ main (int argc, char **argv ) int csh_style = 0; char *logfile = NULL; int debug_wait = 0; - int disable_pth = 0; int gpgconf_list = 0; int standard_socket = 0; gpg_error_t err; @@ -481,14 +452,12 @@ main (int argc, char **argv ) /* Libgcrypt requires us to register the threading model first. Note that this will also do the pth_init. */ -#ifdef USE_GNU_PTH err = gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth); if (err) { log_fatal ("can't register GNU Pth with Libgcrypt: %s\n", gpg_strerror (err)); } -#endif /*USE_GNU_PTH*/ /* Check that the libraries are suitable. Do it here because @@ -634,7 +603,6 @@ main (int argc, char **argv ) case oSh: csh_style = 0; break; case oServer: pipe_server = 1; break; case oDaemon: is_daemon = 1; break; - case oDisablePth: disable_pth = 1; break; case oDisplay: default_display = xstrdup (pargs.r.ret_str); break; case oTTYname: default_ttyname = xstrdup (pargs.r.ret_str); break; @@ -983,45 +951,17 @@ main (int argc, char **argv ) exit (1); } + { + struct sigaction sa; + + sa.sa_handler = SIG_IGN; + sigemptyset (&sa.sa_mask); + sa.sa_flags = 0; + sigaction (SIGPIPE, &sa, NULL); + } #endif /*!HAVE_W32_SYSTEM*/ - -#ifdef USE_GNU_PTH - if (!disable_pth) - { -#ifndef HAVE_W32_SYSTEM /* FIXME */ - struct sigaction sa; - - sa.sa_handler = SIG_IGN; - sigemptyset (&sa.sa_mask); - sa.sa_flags = 0; - sigaction (SIGPIPE, &sa, NULL); -#endif - handle_connections (fd, opt.ssh_support ? fd_ssh : -1); - } - else -#endif /*!USE_GNU_PTH*/ - /* setup signals */ - { -#ifndef HAVE_W32_SYSTEM /* FIXME */ - struct sigaction oact, nact; - - nact.sa_handler = cleanup_sh; - sigemptyset (&nact.sa_mask); - nact.sa_flags = 0; - - sigaction (SIGHUP, NULL, &oact); - if (oact.sa_handler != SIG_IGN) - sigaction (SIGHUP, &nact, NULL); - sigaction( SIGTERM, NULL, &oact ); - if (oact.sa_handler != SIG_IGN) - sigaction (SIGTERM, &nact, NULL); - nact.sa_handler = SIG_IGN; - sigaction (SIGPIPE, &nact, NULL); - sigaction (SIGINT, &nact, NULL); -#endif - start_command_handler (fd, -1); - } + handle_connections (fd, opt.ssh_support ? fd_ssh : -1); close (fd); } @@ -1127,7 +1067,7 @@ reread_configuration (void) /* Create a name for the socket. With USE_STANDARD_SOCKET given as - true ising STANDARD_NAME in the home directory or if given has + true using STANDARD_NAME in the home directory or if given has false from the mkdir type name TEMPLATE. In the latter case a unique name in a unique new directory will be created. In both cases check for valid characters as well as against a maximum @@ -1195,7 +1135,7 @@ create_server_socket (int is_standard_name, const char *name) agent_exit (2); } - serv_addr = malloc (sizeof (*serv_addr)); /* FIXME. */ + serv_addr = xmalloc (sizeof (*serv_addr)); memset (serv_addr, 0, sizeof *serv_addr); serv_addr->sun_family = AF_UNIX; assert (strlen (name) + 1 < sizeof (serv_addr->sun_path)); @@ -1325,7 +1265,6 @@ create_directories (void) -#ifdef USE_GNU_PTH /* This is the worker for the ticker. It is called every few seconds and may only do fast operations. */ static void @@ -1581,7 +1520,6 @@ handle_connections (int listen_fd, int listen_fd_ssh) cleanup (); log_info (_("%s %s stopped\n"), strusage(11), strusage(13)); } -#endif /*USE_GNU_PTH*/ /* Figure out whether an agent is available and running. Prints an diff --git a/configure.ac b/configure.ac index dee0a9f09..369762b02 100644 --- a/configure.ac +++ b/configure.ac @@ -541,10 +541,6 @@ fi AC_SUBST(PTH_CFLAGS) AC_SUBST(PTH_LIBS) -AC_ARG_ENABLE(threads, - AC_HELP_STRING([--disable-threads],[allow building without Pth support]) - ) - dnl Must check for network library requirements before doing link tests dnl for ldap, for example. If ldap libs are static (or dynamic and without @@ -1106,17 +1102,14 @@ fi if test "$missing_pth" = "yes"; then AC_MSG_NOTICE([[ *** -*** It is strongly suggested to build with support for the +*** It is now required to build with support for the *** GNU Portable Threads Library (Pth). Please install this -*** library first or use --disable-threads to allow building -*** anyway. The library is for example available at +*** library first. The library is for example available at *** ftp://ftp.gnu.org/gnu/pth/ *** On a Debian GNU/Linux system you can install it using *** apt-get install libpth-dev ***]]) - if test "$enable_threads" != "no"; then - die=yes - fi + die=yes fi if test "$die" = "yes"; then diff --git a/doc/ChangeLog b/doc/ChangeLog index b1f5e8037..25840a5b1 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,7 @@ +2005-05-17 Werner Koch + + * gpg-agent.texi (Agent Options): Removed --disable-pth. + 2005-04-27 Werner Koch * tools.texi (symcryptrun): Added. diff --git a/doc/gpg-agent.texi b/doc/gpg-agent.texi index 33c8c148c..fa005c3b7 100644 --- a/doc/gpg-agent.texi +++ b/doc/gpg-agent.texi @@ -253,11 +253,6 @@ should in general not be used to avaoid X-sniffing attacks. Append all logging output to @var{file}. This is very helpful in seeing what the agent actually does. -@item --disable-pth -@opindex disable-pth -Don't allow multiple connections. This option is in general not very -useful. - @anchor{option --allow-mark-trusted} @item --allow-mark-trusted @opindex allow-mark-trusted diff --git a/doc/tools.texi b/doc/tools.texi index 805a17e6c..b2463c351 100644 --- a/doc/tools.texi +++ b/doc/tools.texi @@ -722,6 +722,16 @@ directory stated through the environment variable @env{GNUPGHOME} or (on W32 systems) by means on the Registry entry @var{HKCU\Software\GNU\GnuPG:HomeDir}. + +@item -S +@itemx --raw-socket @var{name} +@opindex S +@opindex raw-socket +Connect to socket @var{name} assuming this is an Assuan style server. +Do not run any special initializations or environment checks. This may +be used to directly connect to any Assuan style socket server. + + @end table @c diff --git a/scd/ChangeLog b/scd/ChangeLog index d82e92904..19bba2bf4 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,39 @@ +2005-05-17 Werner Koch + + * scdaemon.c: Removed non-pth code paths. + (create_socket_name, create_server_socket): New. Taken from + ../agent/gpg-agent. + (cleanup): Changed to adjust for SOCKET_NAME now being malloced. + (ticker_thread): Always use pth_event_occurred; it is again + defined for all decent PTH versions. + (handle_connections): New. Based on the gpg-agent code. + (start_connection_thread): Ditto. + (ticker_thread): Removed. + (cleanup_sh): Removed. + (main): Run the handler for the pipe server in a separate + thread. This replaces the old ticker thread. + (scd_get_socket_name): New. + * command.c (cmd_getinfo): New command GETINFO. + (scd_command_handler): Renamed argument and changed code to use an + already connected FD. + +2005-05-15 Werner Koch + + * app.c, app-common.h, app-nks.c, app-p15.c, app-dinsig.c + * app-openpgp.c: Change most function return types from int to + gpg_error_t. + * command.c (pin_cb): Ditto. + * sc-copykeys.c (pincb): Ditto. + + * app.c (lock_reader, unlock_reader): New. Changed call handler + wrappers to make use of these functions. + +2005-05-07 Werner Koch + + * ccid-driver.c (do_close_reader): Don't do a reset before close. + Some folks reported that it makes the SCR335 hang less often. + Look at the source on how to re-enable it. + 2005-04-27 Werner Koch * app-p15.c (micardo_mse): New. diff --git a/scd/apdu.c b/scd/apdu.c index d23a4adc9..212b9df24 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -20,6 +20,10 @@ * $Id$ */ +/* NOTE: This module is also used by other software, thus the use of + the macro USE_GNU_PTH is mandatory. For GnuPG this macro is + guaranteed to be defined true. */ + #include #include #include diff --git a/scd/app-common.h b/scd/app-common.h index 594f93850..517286c49 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -46,56 +46,56 @@ struct app_ctx_s { struct app_local_s *app_local; /* Local to the application. */ struct { void (*deinit) (app_t app); - int (*learn_status) (app_t app, ctrl_t ctrl); - int (*readcert) (app_t app, const char *certid, + gpg_error_t (*learn_status) (app_t app, ctrl_t ctrl); + gpg_error_t (*readcert) (app_t app, const char *certid, unsigned char **cert, size_t *certlen); - int (*readkey) (app_t app, const char *certid, + gpg_error_t (*readkey) (app_t app, const char *certid, unsigned char **pk, size_t *pklen); - int (*getattr) (app_t app, ctrl_t ctrl, const char *name); - int (*setattr) (app_t app, const char *name, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*getattr) (app_t app, ctrl_t ctrl, const char *name); + gpg_error_t (*setattr) (app_t app, const char *name, + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *value, size_t valuelen); - int (*sign) (app_t app, + gpg_error_t (*sign) (app_t app, const char *keyidstr, int hashalgo, - int (pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ); - int (*auth) (app_t app, const char *keyidstr, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*auth) (app_t app, const char *keyidstr, + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen); - int (*decipher) (app_t app, const char *keyidstr, - int (pincb)(void*, const char *, char **), + gpg_error_t (*decipher) (app_t app, const char *keyidstr, + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen); - int (*genkey) (app_t app, ctrl_t ctrl, + gpg_error_t (*genkey) (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); - int (*change_pin) (app_t app, ctrl_t ctrl, + gpg_error_t (*change_pin) (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); - int (*check_pin) (app_t app, const char *keyidstr, - int (pincb)(void*, const char *, char **), + gpg_error_t (*check_pin) (app_t app, const char *keyidstr, + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); } fnc; }; #if GNUPG_MAJOR_VERSION == 1 -int app_select_openpgp (app_t app); -int app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp); -int app_openpgp_storekey (app_t app, int keyno, +gpg_error_t app_select_openpgp (app_t app); +gpg_error_t app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp); +gpg_error_t app_openpgp_storekey (app_t app, int keyno, unsigned char *template, size_t template_len, time_t created_at, const unsigned char *m, size_t mlen, const unsigned char *e, size_t elen, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); #else /*-- app-help.c --*/ @@ -107,72 +107,73 @@ size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff); gpg_error_t select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app); void release_application (app_t app); -int app_munge_serialno (app_t app); -int app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp); -int app_write_learn_status (app_t app, ctrl_t ctrl); -int app_readcert (app_t app, const char *certid, +gpg_error_t app_munge_serialno (app_t app); +gpg_error_t app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp); +gpg_error_t app_write_learn_status (app_t app, ctrl_t ctrl); +gpg_error_t app_readcert (app_t app, const char *certid, unsigned char **cert, size_t *certlen); -int app_readkey (app_t app, const char *keyid, +gpg_error_t app_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen); -int app_getattr (app_t app, ctrl_t ctrl, const char *name); -int app_setattr (app_t app, const char *name, - int (*pincb)(void*, const char *, char **), +gpg_error_t app_getattr (app_t app, ctrl_t ctrl, const char *name); +gpg_error_t app_setattr (app_t app, const char *name, + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *value, size_t valuelen); -int app_sign (app_t app, const char *keyidstr, int hashalgo, - int (pincb)(void*, const char *, char **), +gpg_error_t app_sign (app_t app, const char *keyidstr, int hashalgo, + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ); -int app_auth (app_t app, const char *keyidstr, - int (*pincb)(void*, const char *, char **), +gpg_error_t app_auth (app_t app, const char *keyidstr, + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen); -int app_decipher (app_t app, const char *keyidstr, - int (pincb)(void*, const char *, char **), +gpg_error_t app_decipher (app_t app, const char *keyidstr, + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ); -int app_genkey (app_t app, ctrl_t ctrl, +gpg_error_t app_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); -int app_get_challenge (app_t app, size_t nbytes, unsigned char *buffer); -int app_change_pin (app_t app, ctrl_t ctrl, +gpg_error_t app_get_challenge (app_t app, size_t nbytes, + unsigned char *buffer); +gpg_error_t app_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); -int app_check_pin (app_t app, const char *keyidstr, - int (*pincb)(void*, const char *, char **), +gpg_error_t app_check_pin (app_t app, const char *keyidstr, + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); /*-- app-openpgp.c --*/ -int app_select_openpgp (app_t app); +gpg_error_t app_select_openpgp (app_t app); -int app_openpgp_cardinfo (app_t app, +gpg_error_t app_openpgp_cardinfo (app_t app, char **serialno, char **disp_name, char **pubkey_url, unsigned char **fpr1, unsigned char **fpr2, unsigned char **fpr3); -int app_openpgp_storekey (app_t app, int keyno, +gpg_error_t app_openpgp_storekey (app_t app, int keyno, unsigned char *template, size_t template_len, time_t created_at, const unsigned char *m, size_t mlen, const unsigned char *e, size_t elen, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); -int app_openpgp_readkey (app_t app, int keyno, +gpg_error_t app_openpgp_readkey (app_t app, int keyno, unsigned char **m, size_t *mlen, unsigned char **e, size_t *elen); /*-- app-nks.c --*/ -int app_select_nks (app_t app); +gpg_error_t app_select_nks (app_t app); /*-- app-dinsig.c --*/ -int app_select_dinsig (app_t app); +gpg_error_t app_select_dinsig (app_t app); /*-- app-p15.c --*/ gpg_error_t app_select_p15 (app_t app); diff --git a/scd/app-dinsig.c b/scd/app-dinsig.c index 38fbc79ee..28b70c466 100644 --- a/scd/app-dinsig.c +++ b/scd/app-dinsig.c @@ -85,7 +85,7 @@ #include "tlv.h" -static int +static gpg_error_t do_learn_status (app_t app, ctrl_t ctrl) { gpg_error_t err; @@ -162,7 +162,7 @@ do_learn_status (app_t app, ctrl_t ctrl) FIXME: This needs some cleanups and caching with do_learn_status. */ -static int +static gpg_error_t do_readcert (app_t app, const char *certid, unsigned char **cert, size_t *certlen) { @@ -273,9 +273,9 @@ do_readcert (app_t app, const char *certid, /* Verify the PIN if required. */ -static int +static gpg_error_t verify_pin (app_t app, - int (pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { if (!app->did_chv1 || app->force_chv1 ) @@ -326,12 +326,12 @@ verify_pin (app_t app, If a PIN is required the PINCB will be used to ask for the PIN; that callback should return the PIN in an allocated buffer and store that in the 3rd argument. */ -static int +static gpg_error_t do_sign (app_t app, const char *keyidstr, int hashalgo, - int (pincb)(void*, const char *, char **), - void *pincb_arg, - const void *indata, size_t indatalen, - unsigned char **outdata, size_t *outdatalen ) + gpg_error_t (*pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen ) { static unsigned char sha1_prefix[15] = /* Object ID is 1.3.14.3.2.26 */ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, @@ -397,7 +397,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, /* Select the DINSIG application on the card in SLOT. This function must be used before any other DINSIG application functions. */ -int +gpg_error_t app_select_dinsig (APP app) { static char const aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x66, 0x01 }; diff --git a/scd/app-nks.c b/scd/app-nks.c index f14b67972..b6a3037ed 100644 --- a/scd/app-nks.c +++ b/scd/app-nks.c @@ -117,7 +117,7 @@ keygripstr_from_pk_file (int slot, int fid, char *r_gripstr) -static int +static gpg_error_t do_learn_status (APP app, CTRL ctrl) { gpg_error_t err; @@ -175,7 +175,7 @@ do_learn_status (APP app, CTRL ctrl) the CERTINFO status lines) and return it in the freshly allocated buffer put into CERT and the length of the certificate put into CERTLEN. */ -static int +static gpg_error_t do_readcert (app_t app, const char *certid, unsigned char **cert, size_t *certlen) { @@ -299,9 +299,9 @@ do_readcert (app_t app, const char *certid, /* Verify the PIN if required. */ -static int +static gpg_error_t verify_pin (app_t app, - int (pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { /* Note that force_chv1 is never set but we do it here anyway so @@ -357,12 +357,12 @@ verify_pin (app_t app, If a PIN is required the PINCB will be used to ask for the PIN; that callback should return the PIN in an allocated buffer and store that in the 3rd argument. */ -static int +static gpg_error_t do_sign (app_t app, const char *keyidstr, int hashalgo, - int (pincb)(void*, const char *, char **), - void *pincb_arg, - const void *indata, size_t indatalen, - unsigned char **outdata, size_t *outdatalen ) + gpg_error_t (*pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen ) { static unsigned char sha1_prefix[15] = /* Object ID is 1.3.14.3.2.26 */ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, @@ -435,9 +435,9 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, /* Decrypt the data in INDATA and return the allocated result in OUTDATA. If a PIN is required the PINCB will be used to ask for the PIN; it should return the PIN in an allocated buffer and put it into PIN. */ -static int +static gpg_error_t do_decipher (app_t app, const char *keyidstr, - int (pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ) @@ -485,7 +485,7 @@ do_decipher (app_t app, const char *keyidstr, /* Select the NKS 2.0 application on the card in SLOT. */ -int +gpg_error_t app_select_nks (APP app) { static char const aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x03, 0x01, 0x02 }; diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index ca0e2501b..b8060df03 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -547,7 +547,7 @@ parse_login_data (app_t app) } /* Note, that FPR must be at least 20 bytes. */ -static int +static gpg_error_t store_fpr (int slot, int keynumber, u32 timestamp, const unsigned char *m, size_t mlen, const unsigned char *e, size_t elen, @@ -671,7 +671,7 @@ send_key_data (ctrl_t ctrl, const char *name, /* Implement the GETATTR command. This is similar to the LEARN command but returns just one value via the status interface. */ -static int +static gpg_error_t do_getattr (app_t app, ctrl_t ctrl, const char *name) { static struct { @@ -1168,7 +1168,7 @@ send_keypair_info (app_t app, ctrl_t ctrl, int keyno) /* Handle the LEARN command for OpenPGP. */ -static int +static gpg_error_t do_learn_status (app_t app, ctrl_t ctrl) { do_getattr (app, ctrl, "EXTCAP"); @@ -1204,7 +1204,7 @@ do_learn_status (app_t app, ctrl_t ctrl) its length (for assertions) at PKLEN; the caller must release that buffer. On error PK and PKLEN are not changed and an error code is returned. */ -static int +static gpg_error_t do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen) { gpg_error_t err; @@ -1236,9 +1236,9 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen) /* Verify CHV2 if required. Depending on the configuration of the card CHV1 will also be verified. */ -static int +static gpg_error_t verify_chv2 (app_t app, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { int rc = 0; @@ -1292,9 +1292,9 @@ verify_chv2 (app_t app, } /* Verify CHV3 if required. */ -static int +static gpg_error_t verify_chv3 (app_t app, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { int rc = 0; @@ -1366,9 +1366,9 @@ verify_chv3 (app_t app, /* Handle the SETATTR operation. All arguments are already basically checked. */ -static int +static gpg_error_t do_setattr (app_t app, const char *name, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *value, size_t valuelen) { @@ -1434,9 +1434,9 @@ do_setattr (app_t app, const char *name, /* Handle the PASSWD command. */ -static int +static gpg_error_t do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { int rc = 0; @@ -1525,9 +1525,9 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode, /* Handle the GENKEY command. */ -static int +static gpg_error_t do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { int rc; @@ -1691,7 +1691,7 @@ get_sig_counter (app_t app) return ul; } -static int +static gpg_error_t compare_fingerprint (app_t app, int keyno, unsigned char *sha1fpr) { const unsigned char *fpr; @@ -1731,7 +1731,7 @@ compare_fingerprint (app_t app, int keyno, unsigned char *sha1fpr) the key on the card has been replaced but the shadow information known to gpg was not updated. If there is no fingerprint we assume that this is okay. */ -static int +static gpg_error_t check_against_given_fingerprint (app_t app, const char *fpr, int keyno) { unsigned char tmp[20]; @@ -1762,9 +1762,9 @@ check_against_given_fingerprint (app_t app, const char *fpr, int keyno) GPG_ERR_WRONG_CARD to indicate that the card currently present does not match the one required for the requested action (e.g. the serial number does not match). */ -static int +static gpg_error_t do_sign (app_t app, const char *keyidstr, int hashalgo, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ) @@ -1911,9 +1911,9 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, GPG_ERR_WRONG_CARD to indicate that the card currently present does not match the one required for the requested action (e.g. the serial number does not match). */ -static int +static gpg_error_t do_auth (app_t app, const char *keyidstr, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ) @@ -1974,9 +1974,9 @@ do_auth (app_t app, const char *keyidstr, } -static int +static gpg_error_t do_decipher (app_t app, const char *keyidstr, - int (pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ) @@ -2040,9 +2040,9 @@ do_decipher (app_t app, const char *keyidstr, There is a special mode if the keyidstr is "[CHV3]" with the "[CHV3]" being a literal string: The Admin Pin is checked if and only if the retry counter is still at 3. */ -static int +static gpg_error_t do_check_pin (app_t app, const char *keyidstr, - int (pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { unsigned char tmp_sn[20]; @@ -2124,7 +2124,7 @@ do_check_pin (app_t app, const char *keyidstr, /* Select the OpenPGP application on the card in SLOT. This function must be used before any other OpenPGP application functions. */ -int +gpg_error_t app_select_openpgp (app_t app) { static char const aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 }; @@ -2237,7 +2237,7 @@ leave: LEARN command returns. All parameters return allocated strings or buffers or NULL if the data object is not available. All returned values are sanitized. */ -int +gpg_error_t app_openpgp_cardinfo (app_t app, char **serialno, char **disp_name, @@ -2327,13 +2327,13 @@ app_openpgp_cardinfo (app_t app, create the fingerprint. M, MLEN is the RSA modulus and E, ELEN the RSA public exponent. This function silently overwrites an existing key.*/ -int +gpg_error_t app_openpgp_storekey (app_t app, int keyno, unsigned char *template, size_t template_len, time_t created_at, const unsigned char *m, size_t mlen, const unsigned char *e, size_t elen, - int (*pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { int rc; @@ -2377,7 +2377,7 @@ app_openpgp_storekey (app_t app, int keyno, /* Utility function for external tools: Read the public RSA key at KEYNO and return modulus and exponent in (M,MLEN) and (E,ELEN). */ -int +gpg_error_t app_openpgp_readkey (app_t app, int keyno, unsigned char **m, size_t *mlen, unsigned char **e, size_t *elen) { diff --git a/scd/app-p15.c b/scd/app-p15.c index d2ed15a59..831f0d1f4 100644 --- a/scd/app-p15.c +++ b/scd/app-p15.c @@ -2366,7 +2366,7 @@ send_keypairinfo (app_t app, ctrl_t ctrl, prkdf_object_t keyinfo) /* This is the handler for the LEARN command. */ -static int /* FIXME: change this to gpg_error_t */ +static gpg_error_t do_learn_status (app_t app, ctrl_t ctrl) { gpg_error_t err; @@ -2513,7 +2513,7 @@ readcert_by_cdf (app_t app, cdf_object_t cdf, buffer to be stored at R_CERT and its length at R_CERTLEN. A error code will be returned on failure and R_CERT and R_CERTLEN will be set to NULL/0. */ -static int /* FIXME: change this to gpg_error_t */ +static gpg_error_t do_readcert (app_t app, const char *certid, unsigned char **r_cert, size_t *r_certlen) { @@ -2629,9 +2629,9 @@ micardo_mse (app_t app, unsigned short fid) If a PIN is required, the PINCB will be used to ask for the PIN; that callback should return the PIN in an allocated buffer and store that as the 3rd argument. */ -static int +static gpg_error_t do_sign (app_t app, const char *keyidstr, int hashalgo, - int (pincb)(void*, const char *, char **), + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ) diff --git a/scd/app.c b/scd/app.c index 8e95ef7ef..0a1960267 100644 --- a/scd/app.c +++ b/scd/app.c @@ -23,7 +23,7 @@ #include #include #include - +# include #include "scdaemon.h" #include "app-common.h" @@ -31,7 +31,72 @@ #include "iso7816.h" #include "tlv.h" +/* This table is used to keep track of locks on a per reader base. + The index into the table is the slot number of the reader. The + mutex will be initialized on demand (one of the advantages of a + userland threading system). */ +static struct +{ + int initialized; + pth_mutex_t lock; +} lock_table[10]; + + +/* Lock the reader associated with the APP context. This function + shall be used right before calling any of the actual application + functions to serialize access to the reader. We do this always + even if the reader is not actually used. This allows an actual + application to assume that it never shares a reader (while + performing one command). Returns 0 on success; only then the + unlock_reader function must be called after returning from the + handler. */ +static gpg_error_t +lock_reader (app_t app) +{ + gpg_error_t err; + int slot = app->slot; + + if (slot < 0 || slot >= DIM (lock_table)) + return gpg_error (app->slot<0? GPG_ERR_INV_VALUE : GPG_ERR_RESOURCE_LIMIT); + + if (!lock_table[slot].initialized) + { + if (!pth_mutex_init (&lock_table[slot].lock)) + { + err = gpg_error_from_errno (errno); + log_error ("error initializing mutex: %s\n", strerror (errno)); + return err; + } + lock_table[slot].initialized = 1; + } + + if (!pth_mutex_acquire (&lock_table[slot].lock, 0, NULL)) + { + err = gpg_error_from_errno (errno); + log_error ("failed to acquire APP lock for slot %d: %s\n", + slot, strerror (errno)); + return err; + } + + return 0; +} + +/* Release a lock on the reader. See lock_reader(). */ +static void +unlock_reader (app_t app) +{ + int slot = app->slot; + + if (slot < 0 || slot >= DIM (lock_table) + || !lock_table[slot].initialized) + log_bug ("unlock_reader called for invalid slot %d\n", slot); + if (!pth_mutex_release (&lock_table[slot].lock)) + log_error ("failed to release APP lock for slot %d: %s\n", + slot, strerror (errno)); + +} + /* Check wether the application NAME is allowed. This does not mean we have support for it though. */ static int @@ -54,7 +119,7 @@ is_app_allowed (const char *name) gpg_error_t select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app) { - int rc; + gpg_error_t err; app_t app; unsigned char *result = NULL; size_t resultlen; @@ -63,22 +128,26 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app) app = xtrycalloc (1, sizeof *app); if (!app) { - rc = gpg_error_from_errno (errno); - log_info ("error allocating context: %s\n", gpg_strerror (rc)); - return rc; + err = gpg_error_from_errno (errno); + log_info ("error allocating context: %s\n", gpg_strerror (err)); + return err; } app->slot = slot; + err = lock_reader (app); + if (err) + return err; + /* Fixme: We should now first check whether a card is at all present. */ /* Try to read the GDO file first to get a default serial number. */ - rc = iso7816_select_file (slot, 0x3F00, 1, NULL, NULL); - if (!rc) - rc = iso7816_select_file (slot, 0x2F02, 0, NULL, NULL); - if (!rc) - rc = iso7816_read_binary (slot, 0, 0, &result, &resultlen); - if (!rc) + err = iso7816_select_file (slot, 0x3F00, 1, NULL, NULL); + if (!err) + err = iso7816_select_file (slot, 0x2F02, 0, NULL, NULL); + if (!err) + err = iso7816_read_binary (slot, 0, 0, &result, &resultlen); + if (!err) { size_t n; const unsigned char *p; @@ -104,8 +173,8 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app) memmove (result, p, n); app->serialno = result; app->serialnolen = n; - rc = app_munge_serialno (app); - if (rc) + err = app_munge_serialno (app); + if (err) goto leave; } else @@ -114,38 +183,40 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app) } /* For certain error codes, there is no need to try more. */ - if (gpg_err_code (rc) == GPG_ERR_CARD_NOT_PRESENT) + if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT) goto leave; /* Figure out the application to use. */ - rc = gpg_error (GPG_ERR_NOT_FOUND); - - if (rc && is_app_allowed ("openpgp") && (!name || !strcmp (name, "openpgp"))) - rc = app_select_openpgp (app); - if (rc && is_app_allowed ("nks") && (!name || !strcmp (name, "nks"))) - rc = app_select_nks (app); - if (rc && is_app_allowed ("p15") && (!name || !strcmp (name, "p15"))) - rc = app_select_p15 (app); - if (rc && is_app_allowed ("dinsig") && (!name || !strcmp (name, "dinsig"))) - rc = app_select_dinsig (app); - if (rc && name) - rc = gpg_error (GPG_ERR_NOT_SUPPORTED); + err = gpg_error (GPG_ERR_NOT_FOUND); + + if (err && is_app_allowed ("openpgp") + && (!name || !strcmp (name, "openpgp"))) + err = app_select_openpgp (app); + if (err && is_app_allowed ("nks") && (!name || !strcmp (name, "nks"))) + err = app_select_nks (app); + if (err && is_app_allowed ("p15") && (!name || !strcmp (name, "p15"))) + err = app_select_p15 (app); + if (err && is_app_allowed ("dinsig") && (!name || !strcmp (name, "dinsig"))) + err = app_select_dinsig (app); + if (err && name) + err = gpg_error (GPG_ERR_NOT_SUPPORTED); leave: - if (rc) + if (err) { if (name) log_info ("can't select application `%s': %s\n", - name, gpg_strerror (rc)); + name, gpg_strerror (err)); else log_info ("no supported card application found: %s\n", - gpg_strerror (rc)); + gpg_strerror (err)); xfree (app); - return rc; + return err; } app->initialized = 1; + unlock_reader (app); *r_app = app; return 0; } @@ -181,7 +252,7 @@ release_application (app_t app) All other serial number not starting with FF are used as they are. */ -int +gpg_error_t app_munge_serialno (app_t app) { if (app->serialnolen && app->serialno[0] == 0xff) @@ -208,7 +279,7 @@ app_munge_serialno (app_t app) no update time is available the returned value is 0. Caller must free SERIAL unless the function returns an error. If STAMP is not of interest, NULL may be passed. */ -int +gpg_error_t app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp) { unsigned char *buf, *p; @@ -234,9 +305,11 @@ app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp) /* Write out the application specifig status lines for the LEARN command. */ -int -app_write_learn_status (APP app, CTRL ctrl) +gpg_error_t +app_write_learn_status (app_t app, CTRL ctrl) { + gpg_error_t err; + if (!app) return gpg_error (GPG_ERR_INV_VALUE); if (!app->initialized) @@ -247,8 +320,12 @@ app_write_learn_status (APP app, CTRL ctrl) if (app->apptype) send_status_info (ctrl, "APPTYPE", app->apptype, strlen (app->apptype), NULL, 0); - - return app->fnc.learn_status (app, ctrl); + err = lock_reader (app); + if (err) + return err; + err = app->fnc.learn_status (app, ctrl); + unlock_reader (app); + return err; } @@ -256,18 +333,24 @@ app_write_learn_status (APP app, CTRL ctrl) the CERTINFO status lines) and return it in the freshly allocated buffer put into CERT and the length of the certificate put into CERTLEN. */ -int +gpg_error_t app_readcert (app_t app, const char *certid, unsigned char **cert, size_t *certlen) { + gpg_error_t err; + if (!app) return gpg_error (GPG_ERR_INV_VALUE); if (!app->initialized) return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.readcert) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - - return app->fnc.readcert (app, certid, cert, certlen); + err = lock_reader (app); + if (err) + return err; + err = app->fnc.readcert (app, certid, cert, certlen); + unlock_reader (app); + return err; } @@ -278,9 +361,11 @@ app_readcert (app_t app, const char *certid, code returned. This function might not be supported by all applications. */ -int +gpg_error_t app_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen) { + gpg_error_t err; + if (pk) *pk = NULL; if (pklen) @@ -292,15 +377,21 @@ app_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen) return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.readkey) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - - return app->fnc.readkey (app, keyid, pk, pklen); + err = lock_reader (app); + if (err) + return err; + err= app->fnc.readkey (app, keyid, pk, pklen); + unlock_reader (app); + return err; } /* Perform a GETATTR operation. */ -int -app_getattr (APP app, CTRL ctrl, const char *name) +gpg_error_t +app_getattr (app_t app, CTRL ctrl, const char *name) { + gpg_error_t err; + if (!app || !name || !*name) return gpg_error (GPG_ERR_INV_VALUE); if (!app->initialized) @@ -328,36 +419,48 @@ app_getattr (APP app, CTRL ctrl, const char *name) if (!app->fnc.getattr) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - return app->fnc.getattr (app, ctrl, name); + err = lock_reader (app); + if (err) + return err; + err = app->fnc.getattr (app, ctrl, name); + unlock_reader (app); + return err; } /* Perform a SETATTR operation. */ -int -app_setattr (APP app, const char *name, - int (*pincb)(void*, const char *, char **), +gpg_error_t +app_setattr (app_t app, const char *name, + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *value, size_t valuelen) { + gpg_error_t err; + if (!app || !name || !*name || !value) return gpg_error (GPG_ERR_INV_VALUE); if (!app->initialized) return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.setattr) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - return app->fnc.setattr (app, name, pincb, pincb_arg, value, valuelen); + err = lock_reader (app); + if (err) + return err; + err = app->fnc.setattr (app, name, pincb, pincb_arg, value, valuelen); + unlock_reader (app); + return err; } /* Create the signature and return the allocated result in OUTDATA. If a PIN is required the PINCB will be used to ask for the PIN; it should return the PIN in an allocated buffer and put it into PIN. */ -int -app_sign (APP app, const char *keyidstr, int hashalgo, - int (pincb)(void*, const char *, char **), +gpg_error_t +app_sign (app_t app, const char *keyidstr, int hashalgo, + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ) { - int rc; + gpg_error_t err; if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb) return gpg_error (GPG_ERR_INV_VALUE); @@ -365,27 +468,31 @@ app_sign (APP app, const char *keyidstr, int hashalgo, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.sign) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - rc = app->fnc.sign (app, keyidstr, hashalgo, - pincb, pincb_arg, - indata, indatalen, - outdata, outdatalen); + err = lock_reader (app); + if (err) + return err; + err = app->fnc.sign (app, keyidstr, hashalgo, + pincb, pincb_arg, + indata, indatalen, + outdata, outdatalen); + unlock_reader (app); if (opt.verbose) - log_info ("operation sign result: %s\n", gpg_strerror (rc)); - return rc; + log_info ("operation sign result: %s\n", gpg_strerror (err)); + return err; } /* Create the signature using the INTERNAL AUTHENTICATE command and return the allocated result in OUTDATA. If a PIN is required the PINCB will be used to ask for the PIN; it should return the PIN in an allocated buffer and put it into PIN. */ -int -app_auth (APP app, const char *keyidstr, - int (pincb)(void*, const char *, char **), +gpg_error_t +app_auth (app_t app, const char *keyidstr, + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ) { - int rc; + gpg_error_t err; if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb) return gpg_error (GPG_ERR_INV_VALUE); @@ -393,27 +500,31 @@ app_auth (APP app, const char *keyidstr, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.auth) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - rc = app->fnc.auth (app, keyidstr, - pincb, pincb_arg, - indata, indatalen, - outdata, outdatalen); + err = lock_reader (app); + if (err) + return err; + err = app->fnc.auth (app, keyidstr, + pincb, pincb_arg, + indata, indatalen, + outdata, outdatalen); + unlock_reader (app); if (opt.verbose) - log_info ("operation auth result: %s\n", gpg_strerror (rc)); - return rc; + log_info ("operation auth result: %s\n", gpg_strerror (err)); + return err; } /* Decrypt the data in INDATA and return the allocated result in OUTDATA. If a PIN is required the PINCB will be used to ask for the PIN; it should return the PIN in an allocated buffer and put it into PIN. */ -int -app_decipher (APP app, const char *keyidstr, - int (pincb)(void*, const char *, char **), +gpg_error_t +app_decipher (app_t app, const char *keyidstr, + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ) { - int rc; + gpg_error_t err; if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb) return gpg_error (GPG_ERR_INV_VALUE); @@ -421,23 +532,27 @@ app_decipher (APP app, const char *keyidstr, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.decipher) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - rc = app->fnc.decipher (app, keyidstr, - pincb, pincb_arg, - indata, indatalen, - outdata, outdatalen); + err = lock_reader (app); + if (err) + return err; + err = app->fnc.decipher (app, keyidstr, + pincb, pincb_arg, + indata, indatalen, + outdata, outdatalen); + unlock_reader (app); if (opt.verbose) - log_info ("operation decipher result: %s\n", gpg_strerror (rc)); - return rc; + log_info ("operation decipher result: %s\n", gpg_strerror (err)); + return err; } /* Perform a SETATTR operation. */ -int -app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, - int (*pincb)(void*, const char *, char **), +gpg_error_t +app_genkey (app_t app, CTRL ctrl, const char *keynostr, unsigned int flags, + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { - int rc; + gpg_error_t err; if (!app || !keynostr || !*keynostr || !pincb) return gpg_error (GPG_ERR_INV_VALUE); @@ -445,35 +560,46 @@ app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.genkey) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - rc = app->fnc.genkey (app, ctrl, keynostr, flags, pincb, pincb_arg); + err = lock_reader (app); + if (err) + return err; + err = app->fnc.genkey (app, ctrl, keynostr, flags, pincb, pincb_arg); + unlock_reader (app); if (opt.verbose) - log_info ("operation genkey result: %s\n", gpg_strerror (rc)); - return rc; + log_info ("operation genkey result: %s\n", gpg_strerror (err)); + return err; } /* Perform a GET CHALLENGE operation. This fucntion is special as it directly accesses the card without any application specific wrapper. */ -int -app_get_challenge (APP app, size_t nbytes, unsigned char *buffer) +gpg_error_t +app_get_challenge (app_t app, size_t nbytes, unsigned char *buffer) { + gpg_error_t err; + if (!app || !nbytes || !buffer) return gpg_error (GPG_ERR_INV_VALUE); if (!app->initialized) return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - return iso7816_get_challenge (app->slot, nbytes, buffer); + err = lock_reader (app); + if (err) + return err; + err = iso7816_get_challenge (app->slot, nbytes, buffer); + unlock_reader (app); + return err; } /* Perform a CHANGE REFERENCE DATA or RESET RETRY COUNTER operation. */ -int -app_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode, - int (*pincb)(void*, const char *, char **), +gpg_error_t +app_change_pin (app_t app, CTRL ctrl, const char *chvnostr, int reset_mode, + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { - int rc; + gpg_error_t err; if (!app || !chvnostr || !*chvnostr || !pincb) return gpg_error (GPG_ERR_INV_VALUE); @@ -481,22 +607,27 @@ app_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.change_pin) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - rc = app->fnc.change_pin (app, ctrl, chvnostr, reset_mode, pincb, pincb_arg); + err = lock_reader (app); + if (err) + return err; + err = app->fnc.change_pin (app, ctrl, chvnostr, reset_mode, + pincb, pincb_arg); + unlock_reader (app); if (opt.verbose) - log_info ("operation change_pin result: %s\n", gpg_strerror (rc)); - return rc; + log_info ("operation change_pin result: %s\n", gpg_strerror (err)); + return err; } /* Perform a VERIFY operation without doing anything lese. This may be used to initialze a the PIN cache for long lasting other operations. Its use is highly application dependent. */ -int -app_check_pin (APP app, const char *keyidstr, - int (*pincb)(void*, const char *, char **), +gpg_error_t +app_check_pin (app_t app, const char *keyidstr, + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { - int rc; + gpg_error_t err; if (!app || !keyidstr || !*keyidstr || !pincb) return gpg_error (GPG_ERR_INV_VALUE); @@ -504,9 +635,13 @@ app_check_pin (APP app, const char *keyidstr, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.check_pin) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - rc = app->fnc.check_pin (app, keyidstr, pincb, pincb_arg); + err = lock_reader (app); + if (err) + return err; + err = app->fnc.check_pin (app, keyidstr, pincb, pincb_arg); + unlock_reader (app); if (opt.verbose) - log_info ("operation check_pin result: %s\n", gpg_strerror (rc)); - return rc; + log_info ("operation check_pin result: %s\n", gpg_strerror (err)); + return err; } diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c index e9666ee17..b817452b1 100644 --- a/scd/ccid-driver.c +++ b/scd/ccid-driver.c @@ -986,7 +986,8 @@ do_close_reader (ccid_driver_t handle) } if (handle->idev) { - usb_reset (handle->idev); + if (getenv ("GNUPG_CCID_DRIVER_RESET_BEFORE_CLOSE")) + usb_reset (handle->idev); usb_release_interface (handle->idev, handle->ifc_no); usb_close (handle->idev); handle->idev = NULL; @@ -1274,7 +1275,7 @@ ccid_poll (ccid_driver_t handle) } -/* Note that this function won't return the error codes NO_CARD or +/* 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) @@ -1303,12 +1304,12 @@ ccid_slot_status (ccid_driver_t handle, int *statusbits) { if (!retries) { - fprintf (stderr, "CALLING USB_CLEAR_HALT\n"); + DEBUGOUT ("USB: CALLING USB_CLEAR_HALT\n"); usb_clear_halt (handle->idev, handle->ep_bulk_in); usb_clear_halt (handle->idev, handle->ep_bulk_out); } else - fprintf (stderr, "RETRYING AGIAN\n"); + DEBUGOUT ("USB: RETRYING bulk_in AGAIN\n"); retries++; goto retry; } diff --git a/scd/command.c b/scd/command.c index c8eebaac0..5ea3e01db 100644 --- a/scd/command.c +++ b/scd/command.c @@ -122,7 +122,7 @@ has_option (const char *line, const char *name) /* Reset the card and free the application context. With DO_CLOSE set - to true and this is the last session with a reference to teh + to true and this is the last session with a reference to the reader, close the reader and don't do just a reset. */ static void do_reset (ctrl_t ctrl, int do_close) @@ -647,7 +647,7 @@ cmd_setdata (assuan_context_t ctx, char *line) -static int +static gpg_error_t pin_cb (void *opaque, const char *info, char **retstr) { assuan_context_t ctx = opaque; @@ -1171,6 +1171,34 @@ cmd_unlock (assuan_context_t ctx, char *line) } +/* GETINFO + + Multi purpose command to return certain information. + Supported values of WHAT are: + + socket_name - Return the name of the socket. + +*/ + +static int +cmd_getinfo (assuan_context_t ctx, char *line) +{ + int rc = 0; + + if (!strcmp (line, "socket_name")) + { + const char *s = scd_get_socket_name (); + + if (s) + rc = assuan_send_data (ctx, s, strlen (s)); + else + rc = gpg_error (GPG_ERR_NO_DATA); + } + else + rc = set_error (Parameter_Error, "unknown value for WHAT"); + return rc; +} + @@ -1200,6 +1228,7 @@ register_commands (assuan_context_t ctx) { "CHECKPIN", cmd_checkpin }, { "LOCK", cmd_lock }, { "UNLOCK", cmd_unlock }, + { "GETINFO", cmd_getinfo }, { NULL } }; int i, rc; @@ -1218,10 +1247,10 @@ register_commands (assuan_context_t ctx) } -/* Startup the server. If LISTEN_FD is given as -1, this is simple - piper server, otherwise it is a regular server */ +/* Startup the server. If FD is given as -1 this is simple pipe + server, otherwise it is a regular server. */ void -scd_command_handler (int listen_fd) +scd_command_handler (int fd) { int rc; assuan_context_t ctx; @@ -1230,7 +1259,7 @@ scd_command_handler (int listen_fd) memset (&ctrl, 0, sizeof ctrl); scd_init_default_ctrl (&ctrl); - if (listen_fd == -1) + if (fd == -1) { int filedes[2]; @@ -1240,7 +1269,7 @@ scd_command_handler (int listen_fd) } else { - rc = assuan_init_socket_server (&ctx, listen_fd); + rc = assuan_init_connected_socket_server (&ctx, fd); } if (rc) { diff --git a/scd/sc-copykeys.c b/scd/sc-copykeys.c index 78cb2acc8..66b6894e0 100644 --- a/scd/sc-copykeys.c +++ b/scd/sc-copykeys.c @@ -483,7 +483,7 @@ query_card (APP app) /* Callback function to ask for a PIN. */ -static int +static gpg_error_t pincb (void *arg, const char *prompt, char **pinvalue) { char *pin = xstrdup ("12345678"); diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 7b0f31cdb..9a8b31ac5 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -1,5 +1,5 @@ /* scdaemon.c - The GnuPG Smartcard Daemon - * Copyright (C) 2001, 2002, 2004 Free Software Foundation, Inc. + * Copyright (C) 2001, 2002, 2004, 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -35,9 +35,7 @@ #endif /*HAVE_W32_SYSTEM*/ #include #include -#ifdef USE_GNU_PTH -# include -#endif +#include #define JNLIB_NEED_LOG_LOGV #include "scdaemon.h" @@ -76,6 +74,7 @@ enum cmd_and_opt_values oNoGrab, oLogFile, oServer, + oMultiServer, oDaemon, oBatch, oReaderPort, @@ -110,6 +109,8 @@ static ARGPARSE_OPTS opts[] = { { oDebugWait,"debug-wait",1, "@"}, { oNoDetach, "no-detach" ,0, N_("do not detach from the console")}, { oLogFile, "log-file" ,2, N_("use a log file for the server")}, + { oMultiServer, "multi-server", 0, + N_("allow additional connections in server mode")}, { oReaderPort, "reader-port", 2, N_("|N|connect to reader at port N")}, { octapiDriver, "ctapi-driver", 2, N_("|NAME|use NAME as ct-API driver")}, { opcscDriver, "pcsc-driver", 2, N_("|NAME|use NAME as PC/SC driver")}, @@ -140,8 +141,6 @@ static ARGPARSE_OPTS opts[] = { #endif -static volatile int caught_fatal_sig = 0; - /* Flag to indicate that a shutdown was requested. */ static int shutdown_pending; @@ -149,16 +148,21 @@ static int shutdown_pending; static int maybe_setuid = 1; /* Name of the communication socket */ -static char socket_name[128]; +static char *socket_name; + + +static char *create_socket_name (int use_standard_socket, + char *standard_name, char *template); +static int create_server_socket (int is_standard_name, const char *name); +static void *start_connection_thread (void *arg); +static void handle_connections (int listen_fd); -#ifdef USE_GNU_PTH /* Pth wrapper function definitions. */ GCRY_THREAD_OPTION_PTH_IMPL; -static void *ticker_thread (void *arg); -#endif /*USE_GNU_PTH*/ + static const char * my_strusage (int level) { @@ -265,7 +269,7 @@ set_debug (const char *level) static void cleanup (void) { - if (*socket_name) + if (socket_name && *socket_name) { char *p; @@ -282,27 +286,6 @@ cleanup (void) } -static RETSIGTYPE -cleanup_sh (int sig) -{ - if (caught_fatal_sig) - raise (sig); - caught_fatal_sig = 1; - - /* gcry_control( GCRYCTL_TERM_SECMEM );*/ - cleanup (); - -#ifndef HAVE_DOSISH_SYSTEM - { /* reset action to default action and raise signal again */ - struct sigaction nact; - nact.sa_handler = SIG_DFL; - sigemptyset( &nact.sa_mask ); - nact.sa_flags = 0; - sigaction( sig, &nact, NULL); - } -#endif - raise( sig ); -} int main (int argc, char **argv ) @@ -322,6 +305,7 @@ main (int argc, char **argv ) int greeting = 0; int nogreeting = 0; int pipe_server = 0; + int multi_server = 0; int is_daemon = 0; int nodetach = 0; int csh_style = 0; @@ -343,14 +327,12 @@ main (int argc, char **argv ) /* Libgcrypt requires us to register the threading model first. Note that this will also do the pth_init. */ -#ifdef USE_GNU_PTH err = gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth); if (err) { log_fatal ("can't register GNU Pth with Libgcrypt: %s\n", gpg_strerror (err)); } -#endif /*USE_GNU_PTH*/ /* Check that the libraries are suitable. Do it here because the option parsing may need services of the library */ @@ -481,6 +463,7 @@ main (int argc, char **argv ) case oCsh: csh_style = 1; break; case oSh: csh_style = 0; break; case oServer: pipe_server = 1; break; + case oMultiServer: multi_server = 1; break; case oDaemon: is_daemon = 1; break; case oReaderPort: opt.reader_port = pargs.r.ret_str; break; @@ -598,24 +581,49 @@ main (int argc, char **argv ) log_set_prefix (NULL, 1|2|4); } - if (pipe_server) - { /* This is the simple pipe based server */ -#ifdef USE_GNU_PTH + { + /* This is the simple pipe based server */ pth_attr_t tattr; - + int fd = -1; + + { + struct sigaction sa; + + sa.sa_handler = SIG_IGN; + sigemptyset (&sa.sa_mask); + sa.sa_flags = 0; + sigaction (SIGPIPE, &sa, NULL); + } + + /* In multi server mode we need to listen on an additional + socket. Create that socket now before starting the handler + for the pipe connection. This allows that handler to send + back the name of that socket. */ + if (multi_server) + { + socket_name = create_socket_name (0, + "S.scdaemon", + "/tmp/gpg-XXXXXX/S.scdaemon"); + + fd = create_server_socket (0, socket_name); + } + tattr = pth_attr_new(); pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0); pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 512*1024); - pth_attr_set (tattr, PTH_ATTR_NAME, "ticker"); + pth_attr_set (tattr, PTH_ATTR_NAME, "pipe-connection"); - if (!pth_spawn (tattr, ticker_thread, NULL)) + if (!pth_spawn (tattr, start_connection_thread, (void*)(-1))) { - log_error ("error spawning ticker thread: %s\n", strerror (errno)); + log_error ("error spawning pipe connection handler: %s\n", + strerror (errno) ); scd_exit (2); } -#endif /*USE_GNU_PTH*/ - scd_command_handler (-1); + + handle_connections (fd); + if (fd != -1) + close (fd); } else if (!is_daemon) { @@ -623,87 +631,17 @@ main (int argc, char **argv ) " to run the program in the background\n")); } else - { /* regular server mode */ + { /* Regular server mode */ int fd; pid_t pid; int i; - int len; - struct sockaddr_un serv_addr; - char *p; - - /* fixme: if there is already a running gpg-agent we should - share the same directory - and vice versa */ - *socket_name = 0; - snprintf (socket_name, DIM(socket_name)-1, - "/tmp/gpg-XXXXXX/S.scdaemon"); - socket_name[DIM(socket_name)-1] = 0; - p = strrchr (socket_name, '/'); - if (!p) - BUG (); - *p = 0;; - -#ifndef HAVE_W32_SYSTEM - if (!mkdtemp(socket_name)) - { - log_error ("can't create directory `%s': %s\n", - socket_name, strerror(errno) ); - exit (1); - } -#endif - *p = '/'; - - if (strchr (socket_name, ':') ) - { - log_error ("colons are not allowed in the socket name\n"); - exit (1); - } - if (strlen (socket_name)+1 >= sizeof serv_addr.sun_path ) - { - log_error ("name of socket to long\n"); - exit (1); - } - - -#ifdef HAVE_W32_SYSTEM - fd = _w32_sock_new (AF_UNIX, SOCK_STREAM, 0); -#else - fd = socket (AF_UNIX, SOCK_STREAM, 0); -#endif - if (fd == -1) - { - log_error ("can't create socket: %s\n", strerror(errno) ); - exit (1); - } - - memset (&serv_addr, 0, sizeof serv_addr); - serv_addr.sun_family = AF_UNIX; - strcpy (serv_addr.sun_path, socket_name); - len = (offsetof (struct sockaddr_un, sun_path) - + strlen(serv_addr.sun_path) + 1); - if ( -#ifdef HAVE_W32_SYSTEM - _w32_sock_bind -#else - bind -#endif - (fd, (struct sockaddr*)&serv_addr, len) == -1) - { - log_error ("error binding socket to `%s': %s\n", - serv_addr.sun_path, strerror (errno) ); - close (fd); - exit (1); - } - - if (listen (fd, 5 ) == -1) - { - log_error ("listen() failed: %s\n", strerror (errno)); - close (fd); - exit (1); - } + /* Create the socket. */ + socket_name = create_socket_name (0, + "S.scdaemon", + "/tmp/gpg-XXXXXX/S.scdaemon"); - if (opt.verbose) - log_info ("listening on socket `%s'\n", socket_name ); + fd = create_server_socket (0, socket_name); fflush (NULL); @@ -746,7 +684,7 @@ main (int argc, char **argv ) } else { - /* print the environment string, so that the caller can use + /* Print the environment string, so that the caller can use shell's eval to set it */ if (csh_style) { @@ -763,14 +701,15 @@ main (int argc, char **argv ) /* NOTREACHED */ } /* end parent */ - /* this is the child */ + /* This is the child. */ - /* detach from tty and put process into a new session */ + /* Detach from tty and put process into a new session. */ if (!nodetach ) - { /* close stdin, stdout and stderr unless it is the log stream */ + { + /* Close stdin, stdout and stderr unless it is the log stream. */ for (i=0; i <= 2; i++) { - if ( log_get_fd () != i) + if ( log_test_fd (i) && i != fd) close (i); } if (setsid() == -1) @@ -781,23 +720,13 @@ main (int argc, char **argv ) } } - /* setup signals */ { - struct sigaction oact, nact; + struct sigaction sa; - nact.sa_handler = cleanup_sh; - sigemptyset (&nact.sa_mask); - nact.sa_flags = 0; - - sigaction (SIGHUP, NULL, &oact); - if (oact.sa_handler != SIG_IGN) - sigaction (SIGHUP, &nact, NULL); - sigaction( SIGTERM, NULL, &oact ); - if (oact.sa_handler != SIG_IGN) - sigaction (SIGTERM, &nact, NULL); - nact.sa_handler = SIG_IGN; - sigaction (SIGPIPE, &nact, NULL); - sigaction (SIGINT, &nact, NULL); + sa.sa_handler = SIG_IGN; + sigemptyset (&sa.sa_mask); + sa.sa_flags = 0; + sigaction (SIGPIPE, &sa, NULL); } if (chdir("/")) @@ -808,7 +737,7 @@ main (int argc, char **argv ) #endif /*!HAVE_W32_SYSTEM*/ - scd_command_handler (fd); + handle_connections (fd); close (fd); } @@ -840,13 +769,22 @@ scd_exit (int rc) void -scd_init_default_ctrl (CTRL ctrl) +scd_init_default_ctrl (ctrl_t ctrl) { ctrl->reader_slot = -1; } -#ifdef USE_GNU_PTH +/* Return the name of the socket to be used to connect to this + process. If no socket is available, return NULL. */ +const char * +scd_get_socket_name () +{ + if (socket_name && *socket_name) + return socket_name; + return NULL; +} + static void handle_signal (int signo) @@ -897,18 +835,175 @@ handle_signal (int signo) } } + static void handle_tick (void) { scd_update_reader_status_file (); } + +/* Create a name for the socket. With USE_STANDARD_SOCKET given as + true using STANDARD_NAME in the home directory or if given has + false from the mkdir type name TEMPLATE. In the latter case a + unique name in a unique new directory will be created. In both + cases check for valid characters as well as against a maximum + allowed length for a unix domain socket is done. The function + terminates the process in case of an error. Retunrs: Pointer to an + allcoated string with the absolute name of the socket used. */ +static char * +create_socket_name (int use_standard_socket, + char *standard_name, char *template) +{ + char *name, *p; + + if (use_standard_socket) + name = make_filename (opt.homedir, standard_name, NULL); + else + { + name = xstrdup (template); + p = strrchr (name, '/'); + if (!p) + BUG (); + *p = 0; + if (!mkdtemp (name)) + { + log_error (_("can't create directory `%s': %s\n"), + name, strerror (errno)); + scd_exit (2); + } + *p = '/'; + } + + if (strchr (name, PATHSEP_C)) + { + log_error (("`%s' are not allowed in the socket name\n"), PATHSEP_S); + scd_exit (2); + } + if (strlen (name) + 1 >= DIMof (struct sockaddr_un, sun_path) ) + { + log_error (_("name of socket too long\n")); + scd_exit (2); + } + return name; +} + + + +/* Create a Unix domain socket with NAME. IS_STANDARD_NAME indicates + whether a non-random socket is used. Returns the file descriptor + or terminates the process in case of an error. */ +static int +create_server_socket (int is_standard_name, const char *name) +{ + struct sockaddr_un *serv_addr; + socklen_t len; + int fd; + int rc; + +#ifdef HAVE_W32_SYSTEM + fd = _w32_sock_new (AF_UNIX, SOCK_STREAM, 0); +#else + fd = socket (AF_UNIX, SOCK_STREAM, 0); +#endif + if (fd == -1) + { + log_error (_("can't create socket: %s\n"), strerror (errno)); + scd_exit (2); + } + + serv_addr = xmalloc (sizeof (*serv_addr)); + memset (serv_addr, 0, sizeof *serv_addr); + serv_addr->sun_family = AF_UNIX; + assert (strlen (name) + 1 < sizeof (serv_addr->sun_path)); + strcpy (serv_addr->sun_path, name); + len = (offsetof (struct sockaddr_un, sun_path) + + strlen (serv_addr->sun_path) + 1); + +#ifdef HAVE_W32_SYSTEM + rc = _w32_sock_bind (fd, (struct sockaddr*) serv_addr, len); + if (is_standard_name && rc == -1 ) + { + remove (name); + rc = bind (fd, (struct sockaddr*) serv_addr, len); + } +#else + rc = bind (fd, (struct sockaddr*) serv_addr, len); + if (is_standard_name && rc == -1 && errno == EADDRINUSE) + { + remove (name); + rc = bind (fd, (struct sockaddr*) serv_addr, len); + } +#endif + if (rc == -1) + { + log_error (_("error binding socket to `%s': %s\n"), + serv_addr->sun_path, strerror (errno)); + close (fd); + scd_exit (2); + } + + if (listen (fd, 5 ) == -1) + { + log_error (_("listen() failed: %s\n"), strerror (errno)); + close (fd); + scd_exit (2); + } + + if (opt.verbose) + log_info (_("listening on socket `%s'\n"), serv_addr->sun_path); + + return fd; +} + + + +/* This is the standard connection thread's main function. */ static void * -ticker_thread (void *dummy_arg) +start_connection_thread (void *arg) { - pth_event_t sigs_ev, time_ev = NULL; + int fd = (int)arg; + + if (opt.verbose) + log_info (_("handler for fd %d started\n"), fd); + + scd_command_handler (fd); + + if (opt.verbose) + log_info (_("handler for fd %d terminated\n"), fd); + + /* If this thread is the pipe connection thread, flag that a + shutdown is required. With the next ticker event and given that + no other connections are running the shutdown will then + happen. */ + if (fd == -1) + shutdown_pending = 1; + + return NULL; +} + + +/* Connection handler loop. Wait for connection requests and spawn a + thread after accepting a connection. LISTEN_FD is allowed to be -1 + in which case this code will only do regular timeouts and handle + signals. */ +static void +handle_connections (int listen_fd) +{ + pth_attr_t tattr; + pth_event_t ev, time_ev; sigset_t sigs; int signo; + struct sockaddr_un paddr; + socklen_t plen; + fd_set fdset, read_fdset; + int ret; + int fd; + + tattr = pth_attr_new(); + pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0); + pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 512*1024); + pth_attr_set (tattr, PTH_ATTR_NAME, "scd-connections"); #ifndef HAVE_W32_SYSTEM /* fixme */ sigemptyset (&sigs ); @@ -917,43 +1012,101 @@ ticker_thread (void *dummy_arg) sigaddset (&sigs, SIGUSR2); sigaddset (&sigs, SIGINT); sigaddset (&sigs, SIGTERM); - sigs_ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo); + ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo); #else - sigs_ev = NULL; + ev = NULL; #endif - - while (!shutdown_pending) + time_ev = NULL; + + FD_ZERO (&fdset); + if (listen_fd != -1) + FD_SET (listen_fd, &fdset); + + for (;;) { - if (!time_ev) + if (shutdown_pending) { - time_ev = pth_event (PTH_EVENT_TIME, pth_timeout (2, 0)); - if (time_ev) - pth_event_concat (sigs_ev, time_ev, NULL); - } + if (pth_ctrl (PTH_CTRL_GETTHREADS) == 1) + break; /* ready */ + + /* Do not accept anymore connections but wait for existing + connections to terminate. We do this by clearing out all + file descriptors to wait for, so that the select will be + used to just wait on a signal or timeout event. */ + FD_ZERO (&fdset); + } - if (pth_wait (sigs_ev) < 1) - continue; + /* Create a timeout event if needed. */ + if (!time_ev) + time_ev = pth_event (PTH_EVENT_TIME, pth_timeout (2, 0)); + + /* POSIX says that fd_set should be implemented as a structure, + thus a simple assignment is fine to copy the entire set. */ + read_fdset = fdset; + + if (time_ev) + pth_event_concat (ev, time_ev, NULL); + ret = pth_select_ev (FD_SETSIZE, &read_fdset, NULL, NULL, NULL, ev); + if (time_ev) + pth_event_isolate (time_ev); + + if (ret == -1) + { + if (pth_event_occurred (ev) + || (time_ev && pth_event_occurred (time_ev))) + { + if (pth_event_occurred (ev)) + handle_signal (signo); + if (time_ev && pth_event_occurred (time_ev)) + { + pth_event_free (time_ev, PTH_FREE_ALL); + time_ev = NULL; + handle_tick (); + } + continue; + } + log_error (_("pth_select failed: %s - waiting 1s\n"), + strerror (errno)); + pth_sleep (1); + continue; + } - if ( -#ifdef PTH_STATUS_OCCURRED /* This is Pth 2 */ - pth_event_status (sigs_ev) == PTH_STATUS_OCCURRED -#else - pth_event_occurred (sigs_ev) -#endif - ) - handle_signal (signo); + if (pth_event_occurred (ev)) + { + handle_signal (signo); + } - /* Always run the ticker. */ - if (!shutdown_pending) + if (time_ev && pth_event_occurred (time_ev)) { - pth_event_isolate (sigs_ev); pth_event_free (time_ev, PTH_FREE_ALL); time_ev = NULL; handle_tick (); } + + if (listen_fd != -1 && FD_ISSET (listen_fd, &read_fdset)) + { + plen = sizeof paddr; + fd = pth_accept (listen_fd, (struct sockaddr *)&paddr, &plen); + if (fd == -1) + { + log_error ("accept failed: %s\n", strerror (errno)); + } + else if (!pth_spawn (tattr, start_connection_thread, (void*)fd)) + { + log_error ("error spawning connection handler: %s\n", + strerror (errno) ); + close (fd); + } + fd = -1; + } + } - pth_event_free (sigs_ev, PTH_FREE_ALL); - return NULL; + pth_event_free (ev, PTH_FREE_ALL); + if (time_ev) + pth_event_free (time_ev, PTH_FREE_ALL); + cleanup (); + log_info (_("%s %s stopped\n"), strusage(11), strusage(13)); } -#endif /*USE_GNU_PTH*/ + + diff --git a/scd/scdaemon.h b/scd/scdaemon.h index 5e49f3ae5..eaa9abd35 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -99,7 +99,8 @@ typedef struct app_ctx_s *app_t; /*-- scdaemon.c --*/ void scd_exit (int rc); -void scd_init_default_ctrl (CTRL ctrl); +void scd_init_default_ctrl (ctrl_t ctrl); +const char *scd_get_socket_name (void); /*-- command.c --*/ void scd_command_handler (int); diff --git a/tools/gpg-connect-agent.c b/tools/gpg-connect-agent.c index 6372954f6..403fa2c45 100644 --- a/tools/gpg-connect-agent.c +++ b/tools/gpg-connect-agent.c @@ -39,6 +39,7 @@ enum cmd_and_opt_values aNull = 0, oQuiet = 'q', oVerbose = 'v', + oRawSocket = 'S', oNoVerbose = 500, oHomedir, @@ -55,6 +56,7 @@ static ARGPARSE_OPTS opts[] = { oVerbose, "verbose", 0, N_("verbose") }, { oQuiet, "quiet", 0, N_("quiet") }, { oHex, "hex", 0, N_("print data out hex encoded") }, + { oRawSocket, "raw-socket", 2, N_("|NAME|connect to Assuan socket NAME")}, /* hidden options */ { oNoVerbose, "no-verbose", 0, "@"}, @@ -70,6 +72,7 @@ struct int quiet; /* Be extra quiet. */ const char *homedir; /* Configuration directory name */ int hex; /* Print data lines in hex format. */ + const char *raw_socket; /* Name of socket to connect in raw mode. */ } opt; @@ -159,6 +162,7 @@ main (int argc, char **argv) case oNoVerbose: opt.verbose = 0; break; case oHomedir: opt.homedir = pargs.r.ret_str; break; case oHex: opt.hex = 1; break; + case oRawSocket: opt.raw_socket = pargs.r.ret_str; break; default: pargs.err = 2; break; } @@ -169,7 +173,21 @@ main (int argc, char **argv) fname = argc ? *argv : NULL; - ctx = start_agent (); + if (opt.raw_socket) + { + rc = assuan_socket_connect (&ctx, opt.raw_socket, 0); + if (rc) + { + log_error ("can't connect to socket `%s': %s\n", + opt.raw_socket, assuan_strerror (rc)); + exit (1); + } + + if (opt.verbose) + log_info ("connection to socket `%s' established\n", opt.raw_socket); + } + else + ctx = start_agent (); line = NULL; linesize = 0; for (;;) -- cgit From 41862f5f13bef8113cf040ecaba34a35c370eeb3 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 20 May 2005 20:39:36 +0000 Subject: * protect-tool.c: New option --canonical. (show_file): Implement it. * keyformat.txt: Define the created-at attribute for keys. * ccid-driver.c: Replaced macro DEBUG_T1 by a new debug level. (parse_ccid_descriptor): Mark SCR335 firmware version 5.18 good. (ccid_transceive): Arghhh. The seqno is another bit in the R-block than in the I block, this was wrong at one place. * scdaemon.c: New options --debug-ccid-driver and --debug-disable-ticker. * app-openpgp.c (do_genkey, do_writekey): Factored code to check for existing key out into .. (does_key_exist): .. New function. * gpg-connect-agent.c (add_definq, show_definq, clear_definq) (handle_inquire): New. (read_and_print_response): Handle INQUIRE command. (main): Implement control commands. --- agent/ChangeLog | 7 + agent/call-scd.c | 7 +- agent/keyformat.txt | 9 +- agent/protect-tool.c | 23 ++- agent/protect.c | 2 +- doc/scdaemon.texi | 50 ++++++- scd/ChangeLog | 27 ++++ scd/app-common.h | 28 +++- scd/app-openpgp.c | 355 +++++++++++++++++++++++++++++++++++++++++----- scd/app.c | 29 ++++ scd/ccid-driver.c | 104 +++++++------- scd/command.c | 81 ++++++++++- scd/scdaemon.c | 33 +++-- scd/tlv.c | 73 ++++++++++ scd/tlv.h | 17 +++ tools/ChangeLog | 9 +- tools/gpg-connect-agent.c | 223 ++++++++++++++++++++++++++++- 17 files changed, 961 insertions(+), 116 deletions(-) (limited to 'scd/scdaemon.c') diff --git a/agent/ChangeLog b/agent/ChangeLog index 00f019ddc..015b0b6d8 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,10 @@ +2005-05-20 Werner Koch + + * protect-tool.c: New option --canonical. + (show_file): Implement it. + + * keyformat.txt: Define the created-at attribute for keys. + 2005-05-18 Werner Koch * divert-scd.c (ask_for_card): Removed the card reset kludge. diff --git a/agent/call-scd.c b/agent/call-scd.c index 58dd412f0..fc81e2fa3 100644 --- a/agent/call-scd.c +++ b/agent/call-scd.c @@ -158,7 +158,7 @@ start_scd (ctrl_t ctrl) gpg_error_t err = 0; const char *pgmname; assuan_context_t ctx; - const char *argv[4]; + const char *argv[3]; int no_close_list[3]; int i; int rc; @@ -266,9 +266,8 @@ start_scd (ctrl_t ctrl) pgmname++; argv[0] = pgmname; - argv[1] = "--server"; - argv[2] = "--multi-server"; - argv[3] = NULL; + argv[1] = "--multi-server"; + argv[2] = NULL; i=0; if (!opt.running_detached) diff --git a/agent/keyformat.txt b/agent/keyformat.txt index 7bdb94c0e..2fa53adba 100644 --- a/agent/keyformat.txt +++ b/agent/keyformat.txt @@ -30,12 +30,17 @@ Libgcrypt. Here is an example of an unprotected file: (q #00f7a7c..[some bytes not shown]..61#) (u #304559a..[some bytes not shown]..9b#) ) + (created-at timestamp) (uri http://foo.bar x-foo:whatever_you_want) (comment whatever) ) -"comment" and "uri" are optional. "comment" is currently used to keep -track of ssh key comments. +"comment", "created-at" and "uri" are optional. "comment" is +currently used to keep track of ssh key comments. "created-at" is used +to keep track of the creation time stamp used with OpenPGP keys; it is +optional but required for some operations to calculate the fingerprint +of the key. This timestamp should be a string with the number of +seconds since Epoch or an ISO time string (yyyymmddThhmmss). Actually this form should not be used for regular purposes and only accepted by gpg-agent with the configuration option: diff --git a/agent/protect-tool.c b/agent/protect-tool.c index c21aa0517..e8f1d2c10 100644 --- a/agent/protect-tool.c +++ b/agent/protect-tool.c @@ -60,6 +60,7 @@ enum cmd_and_opt_values oShadow, oShowShadowInfo, oShowKeygrip, + oCanonical, oP12Import, oP12Export, @@ -86,6 +87,7 @@ struct rsa_secret_key_s static const char *opt_homedir; static int opt_armor; +static int opt_canonical; static int opt_store; static int opt_force; static int opt_no_fail_on_exist; @@ -107,6 +109,7 @@ static ARGPARSE_OPTS opts[] = { { oVerbose, "verbose", 0, "verbose" }, { oArmor, "armor", 0, "write output in advanced format" }, + { oCanonical, "canonical", 0, "write output in canonical format" }, { oPassphrase, "passphrase", 2, "|STRING|use passphrase STRING" }, { oProtect, "protect", 256, "protect a private key"}, { oUnprotect, "unprotect", 256, "unprotect a private key"}, @@ -508,14 +511,21 @@ show_file (const char *fname) keylen = gcry_sexp_canon_len (key, 0, NULL,NULL); assert (keylen); - - p = make_advanced (key, keylen); - xfree (key); - if (p) + + if (opt_canonical) { - fwrite (p, strlen (p), 1, stdout); - xfree (p); + fwrite (key, keylen, 1, stdout); } + else + { + p = make_advanced (key, keylen); + if (p) + { + fwrite (p, strlen (p), 1, stdout); + xfree (p); + } + } + xfree (key); } static void @@ -1079,6 +1089,7 @@ main (int argc, char **argv ) { case oVerbose: opt.verbose++; break; case oArmor: opt_armor=1; break; + case oCanonical: opt_canonical=1; break; case oHomedir: opt_homedir = pargs.r.ret_str; break; case oProtect: cmd = oProtect; break; diff --git a/agent/protect.c b/agent/protect.c index ae3061c77..658c8c529 100644 --- a/agent/protect.c +++ b/agent/protect.c @@ -60,7 +60,7 @@ hash_passphrase (const char *passphrase, int hashalgo, -/* Calculate the MIC for a private key S-Exp. SHA1HASH should pint to +/* Calculate the MIC for a private key S-Exp. SHA1HASH should point to a 20 byte buffer. This function is suitable for any algorithms. */ static int calculate_mic (const unsigned char *plainkey, unsigned char *sha1hash) diff --git a/doc/scdaemon.texi b/doc/scdaemon.texi index 971234e52..f069a9fb8 100644 --- a/doc/scdaemon.texi +++ b/doc/scdaemon.texi @@ -54,6 +54,12 @@ abbreviate this command. Run in server mode and wait for commands on the @code{stdin}. This is default mode is to create a socket and listen for commands there. +@item --multi-server +@opindex multi-server +Run in server mode and wait for commands on the @code{stdin} as well as +on an additional Unix Domain socket. The server command @code{GETINFO} +may be used to get the name of that extra socket. + @item --daemon @opindex daemon Run the program in the background. This option is required to prevent @@ -120,6 +126,13 @@ How these messages are mapped to the actual debugging flags is not specified and may change with newer releaes of this program. They are however carefully selected to best aid in debugging. +@quotation Note +All debugging options are subject to change and thus should not be used +by any application program. As the name says, they are only used as +helpers to debug problems. +@end quotation + + @item --debug @var{flags} @opindex debug This option is only useful for debugging and the behaviour may change at @@ -128,7 +141,7 @@ usual C-Syntax. The currently defined bits are: @table @code @item 0 (1) - X.509 or OpenPGP protocol related data + command I/O @item 1 (2) values of big number integers @item 2 (4) @@ -143,8 +156,8 @@ usual C-Syntax. The currently defined bits are: write hashed data to files named @code{dbgmd-000*} @item 10 (1024) trace Assuan protocol - @item 12 (4096) - bypass all certificate validation + @item 11 (2048) + trace APDU I/O to the card. This may reveal sensitive data. @end table @item --debug-all @@ -157,6 +170,17 @@ When running in server mode, wait @var{n} seconds before entering the actual processing loop and print the pid. This gives time to attach a debugger. +@item --debug-ccid-driver +@opindex debug-wait +Enable debug output from the included CCID driver for smartcards. +Using this option twice will also enable some tracing of the T=1 +protocol. Note that this option may reveal sensitive data. + +@item --debug-disable-ticker +@opindex debug-disable-ticker +This option disables all ticker functions like checking for card +insertions. + @item --no-detach @opindex no-detach Don't detach the process from the console. This is manly usefule for @@ -286,6 +310,7 @@ syncronizing access to a token between sessions. * Scdaemon PKDECRYPT:: Decrypting data with a Smartcard. * Scdaemon GETATTR:: Read an attribute's value. * Scdaemon SETATTR:: Update an attribute's value. +* Scdaemon WRITEKEY:: Write a key to a card. * Scdaemon GENKEY:: Generate a new key on-card. * Scdaemon RANDOM:: Return random bytes generate on-card. * Scdaemon PASSWD:: Change PINs. @@ -420,6 +445,25 @@ TO BE WRITTEN. TO BE WRITTEN. +@node Scdaemon WRITEKEY +@subsection Write a key to a card. + +@example + WRITEKEY [--force] @var{keyid} +@end example + +This command is used to store a secret key on a a smartcard. The +allowed keyids depend on the currently selected smartcard +application. The actual keydata is requested using the inquiry +@code{KEYDATA} and need to be provided without any protection. With +@option{--force} set an existing key under this @var{keyid} will get +overwritten. The key data is expected to be the usual canonical encoded +S-expression. + +A PIN will be requested in most saes. This however depends on the +actual card application. + + @node Scdaemon GENKEY @subsection Generate a new key on-card. diff --git a/scd/ChangeLog b/scd/ChangeLog index 19bba2bf4..c64fbec7e 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,30 @@ +2005-05-20 Werner Koch + + * ccid-driver.c: Replaced macro DEBUG_T1 by a new debug level. + (parse_ccid_descriptor): Mark SCR335 firmware version 5.18 good. + (ccid_transceive): Arghhh. The seqno is another bit in the + R-block than in the I block, this was wrong at one place. + + * scdaemon.c: New options --debug-ccid-driver and + --debug-disable-ticker. + + * app-openpgp.c (do_genkey, do_writekey): Factored code to check + for existing key out into .. + (does_key_exist): .. New function. + +2005-05-19 Werner Koch + + * tlv.c (parse_sexp): New. + + * command.c (cmd_writekey): New. + * app.c (app_writekey): New. + * app-common.c (app_t): Add function ptr WRITEKEY. + * app-openpgp.c (do_writekey): New. + + * app-openpgp.c (do_readkey) [GNUPG_MAJOR_VERSION==1]: Return error. + * app-common.h (app_t) [GNUPG_MAJOR_VERSION==1]: Add a field to + store the Assuan context. + 2005-05-17 Werner Koch * scdaemon.c: Removed non-pth code paths. diff --git a/scd/app-common.h b/scd/app-common.h index 517286c49..c2c302395 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -23,10 +23,15 @@ #ifndef GNUPG_SCD_APP_COMMON_H #define GNUPG_SCD_APP_COMMON_H -#if GNUPG_MAJOR_VERSION != 1 -#include +#if GNUPG_MAJOR_VERSION == 1 +# ifdef ENABLE_AGENT_SUPPORT +# include "assuan.h" +# endif +#else +# include #endif + struct app_local_s; /* Defined by all app-*.c. */ struct app_ctx_s { @@ -35,6 +40,15 @@ struct app_ctx_s { unsupported operations the particular function pointer is set to NULL */ int slot; /* Used reader. */ + + /* If this is used by GnuPG 1.4 we need to know the assuan context + in case we need to divert the operation to an already running + agent. This if ASSUAN_CTX is not NULL we take this as indication + that all operations are diverted to gpg-agent. */ +#if GNUPG_MAJOR_VERSION == 1 && defined(ENABLE_AGENT_SUPPORT) + assuan_context_t assuan_ctx; +#endif /*GNUPG_MAJOR_VERSION == 1*/ + unsigned char *serialno; /* Serialnumber in raw form, allocated. */ size_t serialnolen; /* Length in octets of serialnumber. */ const char *apptype; @@ -72,6 +86,11 @@ struct app_ctx_s { void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen); + gpg_error_t (*writekey) (app_t app, ctrl_t ctrl, + const char *certid, unsigned int flags, + gpg_error_t (*pincb)(void*,const char *,char **), + void *pincb_arg, + const unsigned char *pk, size_t pklen); gpg_error_t (*genkey) (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), @@ -134,6 +153,11 @@ gpg_error_t app_decipher (app_t app, const char *keyidstr, void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ); +gpg_error_t app_writekey (app_t app, ctrl_t ctrl, + const char *keyidstr, unsigned int flags, + gpg_error_t (*pincb)(void*, const char *, char **), + void *pincb_arg, + const unsigned char *keydata, size_t keydatalen); gpg_error_t app_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index b8060df03..16ebd34c8 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -565,7 +565,7 @@ store_fpr (int slot, int keynumber, u32 timestamp, n = 6 + 2 + mlen + 2 + elen; p = buffer = xtrymalloc (3 + n); if (!buffer) - return gpg_error (gpg_err_code_from_errno (errno)); + return gpg_error_from_errno (errno); *p++ = 0x99; /* ctb */ *p++ = n >> 8; /* 2 byte length header */ @@ -1207,6 +1207,7 @@ do_learn_status (app_t app, ctrl_t ctrl) static gpg_error_t do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen) { +#if GNUPG_MAJOR_VERSION > 1 gpg_error_t err; int keyno; unsigned char *buf; @@ -1230,6 +1231,9 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen) *pk = buf; *pklen = app->app_local->pk[keyno-1].keylen;; return 0; +#else + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +#endif } @@ -1523,6 +1527,318 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode, } +/* Check whether a key already exists. KEYIDX is the index of the key + (0..2). If FORCE is TRUE a diagnositivc will be printed but no + error returned if the key already exists. */ +static gpg_error_t +does_key_exist (app_t app, int keyidx, int force) +{ + const unsigned char *fpr; + unsigned char *buffer; + size_t buflen, n; + int i; + + assert (keyidx >=0 && keyidx <= 2); + + if (iso7816_get_data (app->slot, 0x006E, &buffer, &buflen)) + { + log_error (_("error reading application data\n")); + return gpg_error (GPG_ERR_GENERAL); + } + fpr = find_tlv (buffer, buflen, 0x00C5, &n); + if (!fpr || n < 60) + { + log_error (_("error reading fingerprint DO\n")); + xfree (buffer); + return gpg_error (GPG_ERR_GENERAL); + } + fpr += 20*keyidx; + for (i=0; i < 20 && !fpr[i]; i++) + ; + xfree (buffer); + if (i!=20 && !force) + { + log_error (_("key already exists\n")); + return gpg_error (GPG_ERR_EEXIST); + } + else if (i!=20) + log_info (_("existing key will be replaced\n")); + else + log_info (_("generating new key\n")); + return 0; +} + + + +/* Handle the WRITEKEY command for OpenPGP. This function expects a + canonical encoded S-expression with the secret key in KEYDATA and + its length (for assertions) in KEYDATALEN. KEYID needs to be the + usual keyid which for OpenPGP is the string "OPENPGP.n" with + n=1,2,3. Bit 0 of FLAGS indicates whether an existing key shall + get overwritten. PINCB and PINCB_ARG are the usual arguments for + the pinentry callback. */ +static gpg_error_t +do_writekey (app_t app, ctrl_t ctrl, + const char *keyid, unsigned int flags, + gpg_error_t (*pincb)(void*, const char *, char **), + void *pincb_arg, + const unsigned char *keydata, size_t keydatalen) +{ + gpg_error_t err; + int force = (flags & 1); + int keyno; + const unsigned char *buf, *tok; + size_t buflen, toklen; + int depth, last_depth1, last_depth2; + const unsigned char *rsa_n = NULL; + const unsigned char *rsa_e = NULL; + const unsigned char *rsa_p = NULL; + const unsigned char *rsa_q = NULL; + size_t rsa_n_len, rsa_e_len, rsa_p_len, rsa_q_len; + unsigned int nbits; + unsigned char *template = NULL; + unsigned char *tp; + size_t template_len; + unsigned char fprbuf[20]; + u32 created_at = 0; + + if (!strcmp (keyid, "OPENPGP.1")) + keyno = 0; + else if (!strcmp (keyid, "OPENPGP.2")) + keyno = 1; + else if (!strcmp (keyid, "OPENPGP.3")) + keyno = 2; + else + return gpg_error (GPG_ERR_INV_ID); + + err = does_key_exist (app, keyno, force); + if (err) + return err; + + + /* + Parse the S-expression + */ + buf = keydata; + buflen = keydatalen; + depth = 0; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + goto leave; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + goto leave; + if (!tok || toklen != 11 || memcmp ("private-key", tok, toklen)) + { + if (!tok) + ; + else if (toklen == 21 && !memcmp ("protected-private-key", tok, toklen)) + log_info ("protected-private-key passed to writekey\n"); + else if (toklen == 20 && !memcmp ("shadowed-private-key", tok, toklen)) + log_info ("shadowed-private-key passed to writekey\n"); + err = gpg_error (GPG_ERR_BAD_KEY); + goto leave; + } + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + goto leave; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + goto leave; + if (!tok || toklen != 3 || memcmp ("rsa", tok, toklen)) + { + err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); + goto leave; + } + last_depth1 = depth; + while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) + && depth && depth >= last_depth1) + { + if (tok) + { + err = gpg_error (GPG_ERR_UNKNOWN_SEXP); + goto leave; + } + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + goto leave; + if (tok && toklen == 1) + { + const unsigned char **mpi; + size_t *mpi_len; + + switch (*tok) + { + case 'n': mpi = &rsa_n; mpi_len = &rsa_n_len; break; + case 'e': mpi = &rsa_e; mpi_len = &rsa_e_len; break; + case 'p': mpi = &rsa_p; mpi_len = &rsa_p_len; break; + case 'q': mpi = &rsa_q; mpi_len = &rsa_q_len;break; + default: mpi = NULL; mpi_len = NULL; break; + } + if (mpi && *mpi) + { + err = gpg_error (GPG_ERR_DUP_VALUE); + goto leave; + } + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + goto leave; + if (tok && mpi) + { + /* Strip off leading zero bytes and save. */ + for (;toklen && !*tok; toklen--, tok++) + ; + *mpi = tok; + *mpi_len = toklen; + } + } + /* Skip until end of list. */ + last_depth2 = depth; + while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) + && depth && depth >= last_depth2) + ; + if (err) + goto leave; + } + /* Parse other attributes. */ + last_depth1 = depth; + while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) + && depth && depth >= last_depth1) + { + if (tok) + { + err = gpg_error (GPG_ERR_UNKNOWN_SEXP); + goto leave; + } + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + goto leave; + if (tok && toklen == 10 && !memcmp ("created-at", tok, toklen)) + { + if ((err = parse_sexp (&buf,&buflen,&depth,&tok,&toklen))) + goto leave; + if (tok) + { + for (created_at=0; toklen && *tok && *tok >= '0' && *tok <= '9'; + tok++, toklen--) + created_at = created_at*10 + (*tok - '0'); + } + } + /* Skip until end of list. */ + last_depth2 = depth; + while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) + && depth && depth >= last_depth2) + ; + if (err) + goto leave; + } + + + /* Check that we have all parameters and that they match the card + description. */ + if (!created_at) + { + log_error (_("creation timestamp missing\n")); + err = gpg_error (GPG_ERR_INV_VALUE); + goto leave; + } + nbits = rsa_n? count_bits (rsa_n, rsa_n_len) : 0; + if (nbits != 1024) + { + log_error (_("RSA modulus missing or not of size %d bits\n"), 1024); + err = gpg_error (GPG_ERR_BAD_SECKEY); + goto leave; + } + nbits = rsa_e? count_bits (rsa_e, rsa_e_len) : 0; + if (nbits < 2 || nbits > 32) + { + log_error (_("RSA public exponent missing or largerr than %d bits\n"), + 32); + err = gpg_error (GPG_ERR_BAD_SECKEY); + goto leave; + } + nbits = rsa_p? count_bits (rsa_p, rsa_p_len) : 0; + if (nbits != 512) + { + log_error (_("RSA prime %s missing or not of size %d bits\n"), "P", 512); + err = gpg_error (GPG_ERR_BAD_SECKEY); + goto leave; + } + nbits = rsa_q? count_bits (rsa_q, rsa_q_len) : 0; + if (nbits != 512) + { + log_error (_("RSA prime %s missing or not of size %d bits\n"), "Q", 512); + err = gpg_error (GPG_ERR_BAD_SECKEY); + goto leave; + } + + + /* Build the private key template as described in section 4.3.3.6 of + the OpenPGP card specs: + 0xC0 public exponent + 0xC1 prime p + 0xC2 prime q + */ + assert (rsa_e_len <= 4); + template_len = (1 + 1 + 4 + + 1 + 1 + rsa_p_len + + 1 + 1 + rsa_q_len); + template = tp = xtrymalloc_secure (template_len); + if (!template) + { + err = gpg_error_from_errno (errno); + goto leave; + } + *tp++ = 0xC0; + *tp++ = 4; + memcpy (tp, rsa_e, rsa_e_len); + if (rsa_e_len < 4) + { + /* Right justify E. */ + memmove (tp+4-rsa_e_len, tp, 4-rsa_e_len); + memset (tp, 0, 4-rsa_e_len); + } + tp += 4; + + *tp++ = 0xC1; + *tp++ = rsa_p_len; + memcpy (tp, rsa_p, rsa_p_len); + tp += rsa_p_len; + + *tp++ = 0xC2; + *tp++ = rsa_q_len; + memcpy (tp, rsa_q, rsa_q_len); + tp += rsa_q_len; + + assert (tp - template == template_len); + + + /* Obviously we need to remove the cached public key. */ + xfree (app->app_local->pk[keyno].key); + app->app_local->pk[keyno].key = NULL; + app->app_local->pk[keyno].keylen = 0; + app->app_local->pk[keyno].read_done = 0; + + /* Prepare for storing the key. */ + err = verify_chv3 (app, pincb, pincb_arg); + if (err) + goto leave; + + /* Store the key. */ + err = iso7816_put_data (app->slot, + (app->card_version > 0x0007? 0xE0 : 0xE9) + keyno, + template, template_len); + if (err) + { + log_error (_("failed to store the key: %s\n"), gpg_strerror (err)); + goto leave; + } + + err = store_fpr (app->slot, keyno, created_at, + rsa_n, rsa_n_len, rsa_e, rsa_e_len, + fprbuf, app->card_version); + if (err) + goto leave; + + + leave: + xfree (template); + return err; +} + /* Handle the GENKEY command. */ static gpg_error_t @@ -1531,13 +1847,11 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags, void *pincb_arg) { int rc; - int i; char numbuf[30]; unsigned char fprbuf[20]; - const unsigned char *fpr; const unsigned char *keydata, *m, *e; - unsigned char *buffer; - size_t buflen, keydatalen, n, mlen, elen; + unsigned char *buffer = NULL; + size_t buflen, keydatalen, mlen, elen; time_t created_at; int keyno = atoi (keynostr); int force = (flags & 1); @@ -1558,41 +1872,15 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags, app->app_local->pk[keyno].read_done = 0; /* Check whether a key already exists. */ - rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen); + rc = does_key_exist (app, keyno, force); if (rc) - { - log_error (_("error reading application data\n")); - return gpg_error (GPG_ERR_GENERAL); - } - fpr = find_tlv (buffer, buflen, 0x00C5, &n); - if (!fpr || n != 60) - { - rc = gpg_error (GPG_ERR_GENERAL); - log_error (_("error reading fingerprint DO\n")); - goto leave; - } - fpr += 20*keyno; - for (i=0; i < 20 && !fpr[i]; i++) - ; - if (i!=20 && !force) - { - rc = gpg_error (GPG_ERR_EEXIST); - log_error (_("key already exists\n")); - goto leave; - } - else if (i!=20) - log_info (_("existing key will be replaced\n")); - else - log_info (_("generating new key\n")); + return rc; - /* Prepare for key generation by verifying the ADmin PIN. */ rc = verify_chv3 (app, pincb, pincb_arg); if (rc) goto leave; - xfree (buffer); buffer = NULL; - #if 1 log_info (_("please wait while key is being generated ...\n")); start_at = time (NULL); @@ -2216,6 +2504,7 @@ app_select_openpgp (app_t app) app->fnc.readkey = do_readkey; app->fnc.getattr = do_getattr; app->fnc.setattr = do_setattr; + app->fnc.writekey = do_writekey; app->fnc.genkey = do_genkey; app->fnc.sign = do_sign; app->fnc.auth = do_auth; diff --git a/scd/app.c b/scd/app.c index 0a1960267..f2c427f5b 100644 --- a/scd/app.c +++ b/scd/app.c @@ -546,6 +546,35 @@ app_decipher (app_t app, const char *keyidstr, } +/* Perform the WRITEKEY operation. */ +gpg_error_t +app_writekey (app_t app, ctrl_t ctrl, + const char *keyidstr, unsigned int flags, + gpg_error_t (*pincb)(void*, const char *, char **), + void *pincb_arg, + const unsigned char *keydata, size_t keydatalen) +{ + gpg_error_t err; + + if (!app || !keyidstr || !*keyidstr || !pincb) + return gpg_error (GPG_ERR_INV_VALUE); + if (!app->initialized) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (!app->fnc.writekey) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + err = lock_reader (app); + if (err) + return err; + err = app->fnc.writekey (app, ctrl, keyidstr, flags, + pincb, pincb_arg, keydata, keydatalen); + unlock_reader (app); + if (opt.verbose) + log_info ("operation writekey result: %s\n", gpg_strerror (err)); + return err; + +} + + /* Perform a SETATTR operation. */ gpg_error_t app_genkey (app_t app, CTRL ctrl, const char *keynostr, unsigned int flags, diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c index b817452b1..387108559 100644 --- a/scd/ccid-driver.c +++ b/scd/ccid-driver.c @@ -108,9 +108,6 @@ # include "scdaemon.h" #endif -/* Define to print information pertaining the T=1 protocol. */ -#undef DEBUG_T1 - # define DEBUGOUT(t) do { if (debug_level) \ log_debug (DRVNAME t); } while (0) @@ -120,6 +117,8 @@ log_debug (DRVNAME t,(a),(b)); } while (0) # define DEBUGOUT_3(t,a,b,c) do { if (debug_level) \ log_debug (DRVNAME t,(a),(b),(c));} while (0) +# define DEBUGOUT_4(t,a,b,c,d) do { if (debug_level) \ + log_debug (DRVNAME t,(a),(b),(c),(d));} while (0) # define DEBUGOUT_CONT(t) do { if (debug_level) \ log_printf (t); } while (0) # define DEBUGOUT_CONT_1(t,a) do { if (debug_level) \ @@ -141,6 +140,8 @@ fprintf (stderr, DRVNAME t, (a), (b)); } while (0) # define DEBUGOUT_3(t,a,b,c) do { if (debug_level) \ fprintf (stderr, DRVNAME t, (a), (b), (c)); } while (0) +# define DEBUGOUT_4(t,a,b,c,d) do { if (debug_level) \ + fprintf (stderr, DRVNAME t, (a), (b), (c), (d));} while(0) # define DEBUGOUT_CONT(t) do { if (debug_level) \ fprintf (stderr, t); } while (0) # define DEBUGOUT_CONT_1(t,a) do { if (debug_level) \ @@ -216,7 +217,11 @@ struct ccid_driver_s static int initialized_usb; /* Tracks whether USB has been initialized. */ -static int debug_level; /* Flag to control the debug output. */ +static int debug_level; /* Flag to control the debug output. + 0 = No debugging + 1 = USB I/O info + 2 = T=1 protocol tracing + */ static unsigned int compute_edc (const unsigned char *data, size_t datalen, @@ -457,7 +462,7 @@ parse_ccid_descriptor (ccid_driver_t handle, && handle->max_ifsd > 48 && ( (handle->id_product == 0xe001 && handle->bcd_device < 0x0516) ||(handle->id_product == 0x5111 && handle->bcd_device < 0x0620) - ||(handle->id_product == 0x5115 && handle->bcd_device < 0x0519) + ||(handle->id_product == 0x5115 && handle->bcd_device < 0x0518) ||(handle->id_product == 0xe003 && handle->bcd_device < 0x0504) )) { @@ -827,7 +832,8 @@ scan_or_find_devices (int readerno, const char *readerid, /* Set the level of debugging to to usea dn return the old level. -1 just returns the old level. A level of 0 disables debugging, 1 - enables debugging, other values are not yet defined. */ + enables debugging, 2 enables additional tracing of the T=1 + protocol, other values are not yet defined. */ int ccid_set_debug_level (int level) { @@ -1437,12 +1443,13 @@ ccid_get_atr (ccid_driver_t handle, DEBUGOUT_CONT_1 (" %02X", msg[i]); DEBUGOUT_LF (); -#ifdef DEBUG_T1 - fprintf (stderr, "T1: put %c-block seq=%d\n", - ((msg[11] & 0xc0) == 0x80)? 'R' : - (msg[11] & 0x80)? 'S' : 'I', - ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40))); -#endif + if (debug_level > 1) + DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n", + ((msg[11] & 0xc0) == 0x80)? 'R' : + (msg[11] & 0x80)? 'S' : 'I', + ((msg[11] & 0x80)? !!(msg[11]& 0x10) + : !!(msg[11] & 0x40)), + (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); rc = bulk_out (handle, msg, msglen); if (rc) @@ -1460,14 +1467,15 @@ ccid_get_atr (ccid_driver_t handle, if (tpdulen < 4) return CCID_DRIVER_ERR_ABORTED; -#ifdef DEBUG_T1 - fprintf (stderr, "T1: got %c-block seq=%d err=%d\n", - ((msg[11] & 0xc0) == 0x80)? 'R' : - (msg[11] & 0x80)? 'S' : 'I', - ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)), - ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0 - ); -#endif + if (debug_level > 1) + DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n", + ((msg[11] & 0xc0) == 0x80)? 'R' : + (msg[11] & 0x80)? 'S' : 'I', + ((msg[11] & 0x80)? !!(msg[11]& 0x10) + : !!(msg[11] & 0x40)), + ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0, + (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); + if ((tpdu[1] & 0xe0) != 0xe0 || tpdu[2] != 1) { DEBUGOUT ("invalid response for S-block (Change-IFSD)\n"); @@ -1706,12 +1714,13 @@ ccid_transceive (ccid_driver_t handle, DEBUGOUT_CONT_1 (" %02X", msg[i]); DEBUGOUT_LF (); -#ifdef DEBUG_T1 - fprintf (stderr, "T1: put %c-block seq=%d\n", - ((msg[11] & 0xc0) == 0x80)? 'R' : - (msg[11] & 0x80)? 'S' : 'I', - ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40))); -#endif + if (debug_level > 1) + DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n", + ((msg[11] & 0xc0) == 0x80)? 'R' : + (msg[11] & 0x80)? 'S' : 'I', + ((msg[11] & 0x80)? !!(msg[11]& 0x10) + : !!(msg[11] & 0x40)), + (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); rc = bulk_out (handle, msg, msglen); if (rc) @@ -1731,14 +1740,14 @@ ccid_transceive (ccid_driver_t handle, usb_clear_halt (handle->idev, handle->ep_bulk_in); return CCID_DRIVER_ERR_ABORTED; } -#ifdef DEBUG_T1 - fprintf (stderr, "T1: got %c-block seq=%d err=%d\n", - ((msg[11] & 0xc0) == 0x80)? 'R' : - (msg[11] & 0x80)? 'S' : 'I', - ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)), - ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0 - ); -#endif + + if (debug_level > 1) + DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n", + ((msg[11] & 0xc0) == 0x80)? 'R' : + (msg[11] & 0x80)? 'S' : 'I', + ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)), + ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0, + (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); if (!(tpdu[1] & 0x80)) { /* This is an I-block. */ @@ -1814,8 +1823,8 @@ ccid_transceive (ccid_driver_t handle, msg = send_buffer; tpdulen = last_tpdulen; } - else if (sending && !!(tpdu[1] & 0x40) == handle->t1_ns) - { /* Reponse does not match our sequence number. */ + else if (sending && !!(tpdu[1] & 0x10) == handle->t1_ns) + { /* Response does not match our sequence number. */ DEBUGOUT ("R-block with wrong seqno received on more bit\n"); return CCID_DRIVER_ERR_CARD_IO_ERROR; } @@ -1835,7 +1844,7 @@ ccid_transceive (ccid_driver_t handle, else { /* This is a S-block. */ retries = 0; - DEBUGOUT_2 ("T1 S-block %s received cmd=%d\n", + DEBUGOUT_2 ("T=1 S-block %s received cmd=%d\n", (tpdu[1] & 0x20)? "response": "request", (tpdu[1] & 0x1f)); if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 3 && tpdu[2]) @@ -1853,7 +1862,7 @@ ccid_transceive (ccid_driver_t handle, if (use_crc) tpdu[tpdulen++] = (edc >> 8); tpdu[tpdulen++] = edc; - DEBUGOUT_1 ("T1 waittime extension of bwi=%d\n", bwi); + DEBUGOUT_1 ("T=1 waittime extension of bwi=%d\n", bwi); } else return CCID_DRIVER_ERR_CARD_IO_ERROR; @@ -2008,14 +2017,13 @@ ccid_transceive_secure (ccid_driver_t handle, usb_clear_halt (handle->idev, handle->ep_bulk_in); return CCID_DRIVER_ERR_ABORTED; } -#ifdef DEBUG_T1 - fprintf (stderr, "T1: got %c-block seq=%d err=%d\n", - ((msg[11] & 0xc0) == 0x80)? 'R' : - (msg[11] & 0x80)? 'S' : 'I', - ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)), - ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0 - ); -#endif + if (debug_level > 1) + DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n", + ((msg[11] & 0xc0) == 0x80)? 'R' : + (msg[11] & 0x80)? 'S' : 'I', + ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)), + ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0, + (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); if (!(tpdu[1] & 0x80)) { /* This is an I-block. */ @@ -2062,7 +2070,7 @@ ccid_transceive_secure (ccid_driver_t handle, DEBUGOUT ("No retries supported for Secure operation\n"); return CCID_DRIVER_ERR_CARD_IO_ERROR; } - else if (!!(tpdu[1] & 0x40) == handle->t1_ns) + else if (!!(tpdu[1] & 0x10) == handle->t1_ns) { /* Reponse does not match our sequence number. */ DEBUGOUT ("R-block with wrong seqno received on more bit\n"); return CCID_DRIVER_ERR_CARD_IO_ERROR; @@ -2075,7 +2083,7 @@ ccid_transceive_secure (ccid_driver_t handle, } else { /* This is a S-block. */ - DEBUGOUT_2 ("T1 S-block %s received cmd=%d for Secure operation\n", + DEBUGOUT_2 ("T=1 S-block %s received cmd=%d for Secure operation\n", (tpdu[1] & 0x20)? "response": "request", (tpdu[1] & 0x1f)); return CCID_DRIVER_ERR_CARD_IO_ERROR; diff --git a/scd/command.c b/scd/command.c index 5ea3e01db..c68d0e925 100644 --- a/scd/command.c +++ b/scd/command.c @@ -40,6 +40,9 @@ /* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN */ #define MAXLEN_PIN 100 +/* Maximum allowed size of key data as used in inquiries. */ +#define MAXLEN_KEYDATA 4096 + #define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t)) @@ -523,7 +526,7 @@ cmd_readcert (assuan_context_t ctx, char *line) } -/* READKEY +/* READKEY Return the public key for the given cert or key ID as an standard S-Expression. @@ -913,6 +916,79 @@ cmd_setattr (assuan_context_t ctx, char *orig_line) return map_to_assuan_status (rc); } + + +/* WRITEKEY [--force] + + This command is used to store a secret key on a a smartcard. The + allowed keyids depend on the currently selected smartcard + application. The actual keydata is requested using the inquiry + "KETDATA" and need to be provided without any protection. With + --force set an existing key under this KEYID will get overwritten. + The keydata is expected to be the usual canonical encoded + S-expression. + + A PIN will be requested for most NAMEs. See the corresponding + writekey function of the actually used application (app-*.c) for + details. */ +static int +cmd_writekey (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + int rc; + char *keyid; + int force = has_option (line, "--force"); + unsigned char *keydata; + size_t keydatalen; + + if ( IS_LOCKED (ctrl) ) + return gpg_error (GPG_ERR_LOCKED); + + /* Skip over options. */ + while ( *line == '-' && line[1] == '-' ) + { + while (*line && !spacep (line)) + line++; + while (spacep (line)) + line++; + } + if (!*line) + return set_error (Parameter_Error, "no keyid given"); + keyid = line; + while (*line && !spacep (line)) + line++; + *line = 0; + + if ((rc = open_card (ctrl, NULL))) + return rc; + + if (!ctrl->app_ctx) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + + keyid = xtrystrdup (keyid); + if (!keyid) + return ASSUAN_Out_Of_Core; + + /* Now get the actual keydata. */ + rc = assuan_inquire (ctx, "KEYDATA", &keydata, &keydatalen, MAXLEN_KEYDATA); + if (rc) + { + xfree (keyid); + return rc; + } + + /* Write the key to the card. */ + rc = app_writekey (ctrl->app_ctx, ctrl, keyid, force? 1:0, + pin_cb, ctx, keydata, keydatalen); + xfree (keyid); + xfree (keydata); + + TEST_CARD_REMOVAL (ctrl, rc); + return map_to_assuan_status (rc); +} + + + /* GENKEY [--force] Generate a key on-card identified by NO, which is application @@ -924,7 +1000,7 @@ cmd_setattr (assuan_context_t ctx, char *orig_line) S KEY-DATA [p|n] - --force is required to overwriet an already existing key. The + --force is required to overwrite an already existing key. The KEY-CREATED-AT is required for further processing because it is part of the hashed key material for the fingerprint. @@ -1222,6 +1298,7 @@ register_commands (assuan_context_t ctx) { "OUTPUT", NULL }, { "GETATTR", cmd_getattr }, { "SETATTR", cmd_setattr }, + { "WRITEKEY", cmd_writekey }, { "GENKEY", cmd_genkey }, { "RANDOM", cmd_random }, { "PASSWD", cmd_passwd }, diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 9a8b31ac5..1110d9d76 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -50,6 +50,7 @@ #ifdef HAVE_W32_SYSTEM #include "../jnlib/w32-afunix.h" #endif +#include "ccid-driver.h" enum cmd_and_opt_values @@ -66,7 +67,7 @@ enum cmd_and_opt_values oDebugAll, oDebugLevel, oDebugWait, - oDebugSC, + oDebugCCIDDriver, oNoGreeting, oNoOptions, oHomedir, @@ -85,8 +86,8 @@ enum cmd_and_opt_values oAllowAdmin, oDenyAdmin, oDisableApplication, - -aTest }; + oDebugDisableTicker +}; @@ -97,6 +98,8 @@ static ARGPARSE_OPTS opts[] = { { 301, NULL, 0, N_("@Options:\n ") }, { oServer, "server", 0, N_("run in server mode (foreground)") }, + { oMultiServer, "multi-server", 0, + N_("run in multi server mode (foreground)") }, { oDaemon, "daemon", 0, N_("run in daemon mode (background)") }, { oVerbose, "verbose", 0, N_("verbose") }, { oQuiet, "quiet", 0, N_("be somewhat more quiet") }, @@ -107,10 +110,10 @@ static ARGPARSE_OPTS opts[] = { { oDebugAll, "debug-all" ,0, "@"}, { oDebugLevel, "debug-level" ,2, "@"}, { oDebugWait,"debug-wait",1, "@"}, + { oDebugCCIDDriver, "debug-ccid-driver", 0, "@"}, + { oDebugDisableTicker, "debug-disable-ticker", 0, "@"}, { oNoDetach, "no-detach" ,0, N_("do not detach from the console")}, { oLogFile, "log-file" ,2, N_("use a log file for the server")}, - { oMultiServer, "multi-server", 0, - N_("allow additional connections in server mode")}, { oReaderPort, "reader-port", 2, N_("|N|connect to reader at port N")}, { octapiDriver, "ctapi-driver", 2, N_("|NAME|use NAME as ct-API driver")}, { opcscDriver, "pcsc-driver", 2, N_("|NAME|use NAME as PC/SC driver")}, @@ -125,10 +128,6 @@ static ARGPARSE_OPTS opts[] = { { oDenyAdmin, "deny-admin", 0, "@" }, { oDisableApplication, "disable-application", 2, "@"}, - /* Dummy options to be removed at some point. */ - { oDebugSC, "debug-sc", 1, "@" }, - { oDisableOpenSC, "disable-opensc", 0, "@" }, - {0} }; @@ -150,6 +149,12 @@ static int maybe_setuid = 1; /* Name of the communication socket */ static char *socket_name; + +/* Debug flag to disable the ticker. The ticker is in fact not + disabled but it won't perform any ticker specific actions. */ +static int ticker_disabled; + + static char *create_socket_name (int use_standard_socket, char *standard_name, char *template); @@ -443,7 +448,10 @@ main (int argc, char **argv ) case oDebugAll: opt.debug = ~0; break; case oDebugLevel: debug_level = pargs.r.ret_str; break; case oDebugWait: debug_wait = pargs.r.ret_int; break; - case oDebugSC: break; + case oDebugCCIDDriver: + ccid_set_debug_level (ccid_set_debug_level (-1)+1); + break; + case oDebugDisableTicker: ticker_disabled = 1; break; case oOptions: /* config files may not be nested (silently ignore them) */ @@ -463,7 +471,7 @@ main (int argc, char **argv ) case oCsh: csh_style = 1; break; case oSh: csh_style = 0; break; case oServer: pipe_server = 1; break; - case oMultiServer: multi_server = 1; break; + case oMultiServer: pipe_server = 1; multi_server = 1; break; case oDaemon: is_daemon = 1; break; case oReaderPort: opt.reader_port = pargs.r.ret_str; break; @@ -839,7 +847,8 @@ handle_signal (int signo) static void handle_tick (void) { - scd_update_reader_status_file (); + if (!ticker_disabled) + scd_update_reader_status_file (); } diff --git a/scd/tlv.c b/scd/tlv.c index 3a81ea6d9..b5dcd4021 100644 --- a/scd/tlv.c +++ b/scd/tlv.c @@ -221,3 +221,76 @@ parse_ber_header (unsigned char const **buffer, size_t *size, *size = length; return 0; } + + +/* FIXME: The following function should not go into this file but for + now it is easier to keep it here. */ + +/* Return the next token of an canconical encoded S-expression. BUF + is the pointer to the S-expression and BUFLEN is a pointer to the + length of this S-expression (used to validate the syntax). Both + are updated to reflect the new position. The token itself is + returned as a pointer into the orginal buffer at TOK and TOKLEN. + If a parentheses is the next token, TOK will be set to NULL. + TOKLEN is checked to be within the bounds. On error a error code + is returned and all pointers should are not guaranteed to point to + a meanigful value. DEPTH should be initialized to 0 and will + reflect on return the actual depth of the tree. To detect the end + of the S-expression it is advisable to check DEPTH after a + successful return: + + depth = 0; + while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) + && depth) + process_token (tok, toklen); + if (err) + handle_error (); + */ +gpg_error_t +parse_sexp (unsigned char const **buf, size_t *buflen, + int *depth, unsigned char const **tok, size_t *toklen) +{ + const unsigned char *s; + size_t n, vlen; + + s = *buf; + n = *buflen; + *tok = NULL; + *toklen = 0; + if (!n) + return *depth ? gpg_error (GPG_ERR_INV_SEXP) : 0; + if (*s == '(') + { + s++; n--; + (*depth)++; + *buf = s; + *buflen = n; + return 0; + } + if (*s == ')') + { + if (!*depth) + return gpg_error (GPG_ERR_INV_SEXP); + *toklen = 1; + s++; n--; + (*depth)--; + *buf = s; + *buflen = n; + return 0; + } + for (vlen=0; n && *s && *s != ':' && (*s >= '0' && *s <= '9'); s++, n--) + vlen = vlen*10 + (*s - '0'); + if (!n || *s != ':') + return gpg_error (GPG_ERR_INV_SEXP); + s++; n--; + if (vlen > n) + return gpg_error (GPG_ERR_INV_SEXP); + *tok = s; + *toklen = vlen; + s += vlen; + n -= vlen; + *buf = s; + *buflen = n; + return 0; +} + diff --git a/scd/tlv.h b/scd/tlv.h index 628580431..f587dd9df 100644 --- a/scd/tlv.h +++ b/scd/tlv.h @@ -88,4 +88,21 @@ gpg_error_t parse_ber_header (unsigned char const **buffer, size_t *size, +/* Return the next token of an canconical encoded S-expression. BUF + is the pointer to the S-expression and BUFLEN is a pointer to the + length of this S-expression (used to validate the syntax). Both + are updated to reflect the new position. The token itself is + returned as a pointer into the orginal buffer at TOK and TOKLEN. + If a parentheses is the next token, TOK will be set to NULL. + TOKLEN is checked to be within the bounds. On error a error code + is returned and all pointers should are not guaranteed to point to + a meanigful value. DEPTH should be initialized to 0 and will + reflect on return the actual depth of the tree. To detect the end + of the S-expression it is advisable to check DEPTH after a + successful return. */ +gpg_error_t parse_sexp (unsigned char const **buf, size_t *buflen, + int *depth, unsigned char const **tok, size_t *toklen); + + + #endif /* SCD_TLV_H */ diff --git a/tools/ChangeLog b/tools/ChangeLog index 68b62dd30..76505a6bf 100644 --- a/tools/ChangeLog +++ b/tools/ChangeLog @@ -1,3 +1,10 @@ +2005-05-20 Werner Koch + + * gpg-connect-agent.c (add_definq, show_definq, clear_definq) + (handle_inquire): New. + (read_and_print_response): Handle INQUIRE command. + (main): Implement control commands. + 2005-04-21 Werner Koch * symcryptrun.c (main): Optionally allow the input file as command @@ -368,7 +375,7 @@ * watchgnupg.c: New. - Copyright 2003, 2004 Free Software Foundation, Inc. + Copyright 2003, 2004, 2005 Free Software Foundation, Inc. This file is free software; as a special exception the author gives unlimited permission to copy and/or distribute it, with or without diff --git a/tools/gpg-connect-agent.c b/tools/gpg-connect-agent.c index 403fa2c45..bb05030ee 100644 --- a/tools/gpg-connect-agent.c +++ b/tools/gpg-connect-agent.c @@ -76,6 +76,23 @@ struct } opt; + +/* Definitions for /definq commands and a global linked list with all + the definitions. */ +struct definq_s +{ + struct definq_s *next; + char *name; /* Name of inquiry or NULL for any name. */ + int is_prog; /* True if this is a program to run. */ + char file[1]; /* Name of file or program. */ +}; +typedef struct definq_s *definq_t; + +static definq_t definq_list; +static definq_t *definq_list_tail = &definq_list; + + + /*-- local prototypes --*/ static int read_and_print_response (assuan_context_t ctx); static assuan_context_t start_agent (void); @@ -129,6 +146,68 @@ i18n_init(void) #endif } +/* Store an inquire response pattern. Note, that this function may + change the content of LINE. We assume that leading white spaces + are already removed. */ +static void +add_definq (char *line, int is_prog) +{ + definq_t d; + char *name, *p; + + /* Get name. */ + name = line; + for (p=name; *p && !spacep (p); p++) + ; + if (*p) + *p++ = 0; + while (spacep (p)) + p++; + + d = xmalloc (sizeof *d + strlen (p) ); + strcpy (d->file, p); + d->is_prog = is_prog; + if ( !strcmp (name, "*")) + d->name = NULL; + else + d->name = xstrdup (name); + + d->next = NULL; + *definq_list_tail = d; + definq_list_tail = &d->next; +} + + +/* Show all inquiry defintions. */ +static void +show_definq (void) +{ + definq_t d; + + for (d=definq_list; d; d = d->next) + if (d->name) + printf ("%-20s %c %s\n", d->name, d->is_prog? 'p':'f', d->file); + for (d=definq_list; d; d = d->next) + if (!d->name) + printf ("%-20s %c %s\n", "*", d->is_prog? 'p':'f', d->file); +} + + +/* Clear all inquiry definitions. */ +static void +clear_definq (void) +{ + while (definq_list) + { + definq_t tmp = definq_list->next; + xfree (definq_list->name); + xfree (definq_list); + definq_list = tmp; + } + definq_list_tail = &definq_list; +} + + /* gpg-connect-agent's entry point. */ int @@ -138,7 +217,7 @@ main (int argc, char **argv) const char *fname; int no_more_options = 0; assuan_context_t ctx; - char *line; + char *line, *p; size_t linesize; int rc; @@ -213,6 +292,57 @@ main (int argc, char **argv) log_info (_("line shortened due to embedded Nul character\n")); if (line[n-1] == '\n') line[n-1] = 0; + if (*line == '/') + { + /* Handle control commands. */ + char *cmd = line+1; + + for (p=cmd; *p && !spacep (p); p++) + ; + if (*p) + *p++ = 0; + while (spacep (p)) + p++; + if (!strcmp (cmd, "definqfile")) + { + add_definq (p, 0); + } + else if (!strcmp (cmd, "definqprog")) + { + add_definq (p, 1); + } + else if (!strcmp (cmd, "showdef")) + { + show_definq (); + } + else if (!strcmp (cmd, "cleardef")) + { + clear_definq (); + } + else if (!strcmp (cmd, "echo")) + { + puts (p); + } + else if (!strcmp (cmd, "help")) + { + puts ("Available commands:\n" + "/echo ARGS Echo ARGS.\n" + "/definqfile NAME FILE\n" + " Use content of FILE for inquiries with NAME.\n" + " NAME may be \"*\" to match any inquiry.\n" + "/definqprog NAME PGM\n" + " Run PGM for inquiries matching NAME and pass the\n" + " entire line to it as arguments.\n" + "/showdef Print all definitions.\n" + "/cleardef Delete all definitions.\n" + "/help Print this help."); + } + else + log_error (_("unknown command `%s'\n"), cmd ); + + continue; + } + rc = assuan_write_line (ctx, line); if (rc) { @@ -234,6 +364,94 @@ main (int argc, char **argv) } +/* Handle an Inquire from the server. Return False if it could not be + handled; in this case the caller shll complete the operation. LINE + is the complete line as received from the server. This function + may change the content of LINE. */ +static int +handle_inquire (assuan_context_t ctx, char *line) +{ + const char *name; + definq_t d; + FILE *fp; + char buffer[1024]; + int rc, n; + + /* Skip the command and trailing spaces. */ + for (; *line && !spacep (line); line++) + ; + while (spacep (line)) + line++; + /* Get the name. */ + name = line; + for (; *line && !spacep (line); line++) + ; + if (*line) + *line++ = 0; + + /* Now match it against our list. he second loop is todetect the + match all entry. **/ + for (d=definq_list; d; d = d->next) + if (d->name && !strcmp (d->name, name)) + break; + if (!d) + for (d=definq_list; d; d = d->next) + if (!d->name) + break; + if (!d) + { + if (opt.verbose) + log_info ("no handler for inquiry `%s' found\n", name); + return 0; + } + + if (d->is_prog) + { + fp = popen (d->file, "r"); + if (!fp) + log_error ("error executing `%s': %s\n", d->file, strerror (errno)); + else if (opt.verbose) + log_error ("handling inquiry `%s' by running `%s'\n", name, d->file); + } + else + { + fp = fopen (d->file, "rb"); + if (!fp) + log_error ("error opening `%s': %s\n", d->file, strerror (errno)); + else if (opt.verbose) + log_error ("handling inquiry `%s' by returning content of `%s'\n", + name, d->file); + } + if (!fp) + return 0; + + while ( (n = fread (buffer, 1, sizeof buffer, fp)) ) + { + rc = assuan_send_data (ctx, buffer, n); + if (rc) + { + log_error ("sending data back failed: %s\n", assuan_strerror (rc) ); + break; + } + } + if (ferror (fp)) + log_error ("error reading from `%s': %s\n", d->file, strerror (errno)); + + rc = assuan_send_data (ctx, NULL, 0); + if (rc) + log_error ("sending data back failed: %s\n", assuan_strerror (rc) ); + + if (d->is_prog) + { + if (pclose (fp)) + log_error ("error running `%s': %s\n", d->file, strerror (errno)); + } + else + fclose (fp); + return 1; +} + + /* Read all response lines from server and print them. Returns 0 on success or an assuan error code. */ static int @@ -325,7 +543,8 @@ read_and_print_response (assuan_context_t ctx) { fwrite (line, linelen, 1, stdout); putchar ('\n'); - return 0; + if (!handle_inquire (ctx, line)) + assuan_write_line (ctx, "CANCEL"); } else if (linelen >= 3 && line[0] == 'E' && line[1] == 'N' && line[2] == 'D' -- cgit From 05e1dc22f0feef8d5af7c8bbb0c0a5129f2c0b05 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Sat, 21 May 2005 18:49:00 +0000 Subject: * call-scd.c (start_scd): Don't test for an alive scdaemon here. (agent_scd_check_aliveness): New. * gpg-agent.c (handle_tick): Test for an alive scdaemon. (handle_signal): Print thread info on SIGUSR1. * scdaemon.c (handle_signal): Print thread info on SIGUSR1. --- NEWS | 5 ++++ TODO | 15 +++++++++++ agent/ChangeLog | 7 +++++ agent/agent.h | 1 + agent/call-scd.c | 81 ++++++++++++++++++++++++++++++++++++++++--------------- agent/gpg-agent.c | 8 +++++- scd/ChangeLog | 4 +++ scd/app-openpgp.c | 2 +- scd/scdaemon.c | 3 ++- 9 files changed, 101 insertions(+), 25 deletions(-) (limited to 'scd/scdaemon.c') diff --git a/NEWS b/NEWS index c25bbe08e..79a74cbe4 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,11 @@ Noteworthy changes in version 1.9.17 ------------------------------------------------- + * gpg-connect-agent has now features to handle Assuan INQUIRE + commands. + + * Internal changes for OpenPGP cards. New Assuan command WRITEKEY. + * GNU Pth is now a hard requirement. * [scdaemon] Support for OpenSC has been removed. Instead a new and diff --git a/TODO b/TODO index 6e8951f03..fe10e9f77 100644 --- a/TODO +++ b/TODO @@ -67,6 +67,11 @@ might want to have an agent context for each service request * sm/export.c ** Return an error code or a status info per user ID. +* scd/tlv.c + The parse_sexp fucntion should not go into this file. Check whether + we can change all S-expression handling code to make use of this + function. + * tests ** Makefile.am We use printf(1) to setup the library path, this is not portable. @@ -89,3 +94,13 @@ might want to have an agent context for each service request This means we can't reread a configuration ** No card status notifications. + + +* IMPORTANT: + Check that the PIN cache is cleared after failed card operations. + After receiving a HUP gpg-agent should set a flag to kill scdaemon + as soon as possible, w/o that scdaemon will continue running as a + zombie and gpg-agent won't be able to fire up a new one. + Implement an scd/agent option to wait for a card. + + diff --git a/agent/ChangeLog b/agent/ChangeLog index 015b0b6d8..6c271c8e2 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,10 @@ +2005-05-21 Werner Koch + + * call-scd.c (start_scd): Don't test for an alive scdaemon here. + (agent_scd_check_aliveness): New. + * gpg-agent.c (handle_tick): Test for an alive scdaemon. + (handle_signal): Print thread info on SIGUSR1. + 2005-05-20 Werner Koch * protect-tool.c: New option --canonical. diff --git a/agent/agent.h b/agent/agent.h index 6ab65eeba..7e4f555e8 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -247,6 +247,7 @@ int divert_generic_cmd (ctrl_t ctrl, /*-- call-scd.c --*/ void initialize_module_call_scd (void); +void agent_scd_check_aliveness (void); int agent_reset_scd (ctrl_t ctrl); int agent_card_learn (ctrl_t ctrl, void (*kpinfo_cb)(void*, const char *), diff --git a/agent/call-scd.c b/agent/call-scd.c index fc81e2fa3..617ef0d48 100644 --- a/agent/call-scd.c +++ b/agent/call-scd.c @@ -185,26 +185,15 @@ start_scd (ctrl_t ctrl) } ctrl->scd_local->locked++; - /* If we already have a context, we better do a sanity check now to - see whether it has accidently died. This avoids annoying - timeouts and hung connections. */ if (ctrl->scd_local->ctx) - { - pid_t pid; -#ifndef HAVE_W32_SYSTEM - pid = assuan_get_pid (ctrl->scd_local->ctx); - if (pid != (pid_t)(-1) && pid - && ((rc=waitpid (pid, NULL, WNOHANG))==-1 || (rc == pid)) ) - { - assuan_disconnect (ctrl->scd_local->ctx); - ctrl->scd_local->ctx = NULL; - } - else -#endif - return 0; /* Okay, the context is fine. */ - } + return 0; /* Okay, the context is fine. We used to test for an + alive context here and do an disconnect. How that we + have a ticker function to check for it, it is easier + not to check here but to let the connection run on an + error instead. */ - /* We need to protect the lowwing code. */ + + /* We need to protect the following code. */ if (!pth_mutex_acquire (&start_scd_lock, 0, NULL)) { log_error ("failed to acquire the start_scd lock: %s\n", @@ -350,6 +339,50 @@ start_scd (ctrl_t ctrl) } +/* Check whether the Scdaemon is still alive and clean it up if not. */ +void +agent_scd_check_aliveness (void) +{ + pid_t pid; + int rc; + + /* We can do so only if there is no more active primary connection. + With an active primary connection, this is all no problem because + with the end of gpg-agent's session a disconnect is send and the + this function will be used at a later time. */ + if (!primary_scd_ctx || !primary_scd_ctx_reusable) + return; + + if (!pth_mutex_acquire (&start_scd_lock, 0, NULL)) + { + log_error ("failed to acquire the start_scd lock while" + " doing an aliveness check: %s\n", + strerror (errno)); + return; + } + + if (primary_scd_ctx && primary_scd_ctx_reusable) + { + pid = assuan_get_pid (primary_scd_ctx); + if (pid != (pid_t)(-1) && pid + && ((rc=waitpid (pid, NULL, WNOHANG))==-1 || (rc == pid)) ) + { + /* Okay, scdaemon died. Disconnect the primary connection now + but take care that it won't do another wait. */ + assuan_set_flag (primary_scd_ctx, ASSUAN_NO_WAITPID, 1); + assuan_disconnect (primary_scd_ctx); + primary_scd_ctx = NULL; + primary_scd_ctx_reusable = 0; + xfree (socket_name); + socket_name = NULL; + } + } + + if (!pth_mutex_release (&start_scd_lock)) + log_error ("failed to release the start_scd lock while" + " doing the aliveness check: %s\n", strerror (errno)); +} + /* Reset the SCD if it has been used. */ int @@ -359,15 +392,19 @@ agent_reset_scd (ctrl_t ctrl) { if (ctrl->scd_local->ctx) { - /* We can't disconnect the primary context becuase libassuan + /* We can't disconnect the primary context because libassuan does a waitpid on it and thus the system would hang. Instead we send a reset and keep that connection for reuse. */ if (ctrl->scd_local->ctx == primary_scd_ctx) { - if (!assuan_transact (primary_scd_ctx, "RESET", - NULL, NULL, NULL, NULL, NULL, NULL)) - primary_scd_ctx_reusable = 1; + /* The RESET may fail for example if the scdaemon has + already been terminated. We need to set the reusable + flag anyway to make sure that the aliveness check can + clean it up. */ + assuan_transact (primary_scd_ctx, "RESET", + NULL, NULL, NULL, NULL, NULL, NULL); + primary_scd_ctx_reusable = 1; } else assuan_disconnect (ctrl->scd_local->ctx); diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 4ac995c26..e3e952906 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -1270,6 +1270,11 @@ create_directories (void) static void handle_tick (void) { + /* Check whether the scdaemon has dies and cleanup in this case. */ + agent_scd_check_aliveness (); + + /* If we are running as a child of another process, check whether + the parent is still alive and shutdwon if now. */ #ifndef HAVE_W32_SYSTEM if (parent_pid != (pid_t)(-1)) { @@ -1301,7 +1306,8 @@ handle_signal (int signo) break; case SIGUSR1: - log_info ("SIGUSR1 received - no action defined\n"); + log_info ("SIGUSR1 received - printing internal information:\n"); + pth_ctrl (PTH_CTRL_DUMPSTATE, log_get_stream ()); break; case SIGUSR2: diff --git a/scd/ChangeLog b/scd/ChangeLog index c64fbec7e..feeaabfce 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,7 @@ +2005-05-21 Werner Koch + + * scdaemon.c (handle_signal): Print thread info on SIGUSR1. + 2005-05-20 Werner Koch * ccid-driver.c: Replaced macro DEBUG_T1 by a new debug level. diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 16ebd34c8..1165ec683 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -1634,7 +1634,7 @@ do_writekey (app_t app, ctrl_t ctrl, log_info ("protected-private-key passed to writekey\n"); else if (toklen == 20 && !memcmp ("shadowed-private-key", tok, toklen)) log_info ("shadowed-private-key passed to writekey\n"); - err = gpg_error (GPG_ERR_BAD_KEY); + err = gpg_error (GPG_ERR_BAD_SECKEY); goto leave; } if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 1110d9d76..488a4853b 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -807,7 +807,8 @@ handle_signal (int signo) break; case SIGUSR1: - log_info ("SIGUSR1 received - no action defined\n"); + log_info ("SIGUSR1 received - printing internal information:\n"); + pth_ctrl (PTH_CTRL_DUMPSTATE, log_get_stream ()); break; case SIGUSR2: -- cgit From cad9562436e61cdaf1a2f03ba96bcc82077205e6 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 1 Jun 2005 15:46:01 +0000 Subject: * configure.ac (gl_INIT): Add gnulib stuff. (fseeko, ftello, ttyname, isascii): Replaced the AC_REPLACE_FUNCS by a simple check. (putc_unlocked): Removed check. Not used. (strsep, mkdtemp, asprintf): Replaced checks by gnulib checks. (xsize): Added will probably come handy soon. (CFLAGS): Use -Wformat-security instead of -Wformat-nonliteral. Add --Wno-format-y2k. * gl/, gl/m4/: New. * gpg-agent.c: Include setenv.h. * Makefile.am (AM_CPPFLAGS): Added. * util.h: Add some includes for gnulib. (ttyname, isascii): Define them inline. * fseeko.c, ftello.c: Removed. * strsep.c, mkdtemp.c: Removed. * ttyname.c, isascii.c: Removed. * mkdtemp.c: Removed. * exec.c: Include mkdtemp.h * keybox-file.c (ftello) [!HAVE_FSEEKO]: New replacement function. Copied from ../common/ftello.c. * keybox-update.c (fseeko) [!HAVE_FSEEKO]: New replacement function. Copied from ../common/iobuf.c. * scdaemon.c: Include mkdtemp.h. * misc.c: Include setenv.h. * symcryptrun.c: Include mkdtemp.h. --- ChangeLog | 12 +++++++ Makefile.am | 4 +-- agent/ChangeLog | 4 +++ agent/Makefile.am | 4 +-- agent/gpg-agent.c | 1 + autogen.sh | 4 +-- common/ChangeLog | 10 ++++++ common/Makefile.am | 16 ++------- common/fseeko.c | 41 --------------------- common/ftello.c | 46 ------------------------ common/mkdtemp.c | 97 ------------------------------------------------- common/putc_unlocked.c | 31 ---------------- common/strsep.c | 76 --------------------------------------- common/ttyname.c | 32 ----------------- common/util.h | 40 ++++++++++++--------- common/xasprintf.c | 2 +- configure.ac | 20 +++++------ g10/ChangeLog | 5 +++ g10/Makefile.am | 6 ++-- g10/exec.c | 5 +-- g10/mkdtemp.c | 98 -------------------------------------------------- kbx/ChangeLog | 7 ++++ kbx/Makefile.am | 5 +-- kbx/keybox-file.c | 16 +++++++++ kbx/keybox-update.c | 32 +++++++++++++++++ scd/ChangeLog | 4 +++ scd/Makefile.am | 4 +-- scd/scdaemon.c | 2 +- sm/ChangeLog | 4 +++ sm/Makefile.am | 4 +-- sm/misc.c | 1 + tools/ChangeLog | 4 +++ tools/Makefile.am | 14 +++++--- tools/symcryptrun.c | 1 + 34 files changed, 166 insertions(+), 486 deletions(-) delete mode 100644 common/fseeko.c delete mode 100644 common/ftello.c delete mode 100644 common/mkdtemp.c delete mode 100644 common/putc_unlocked.c delete mode 100644 common/strsep.c delete mode 100644 common/ttyname.c delete mode 100644 g10/mkdtemp.c (limited to 'scd/scdaemon.c') diff --git a/ChangeLog b/ChangeLog index 154ae07e9..fbd9ad79d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2005-06-01 Werner Koch + + * configure.ac (gl_INIT): Add gnulib stuff. + (fseeko, ftello, ttyname, isascii): Replaced the AC_REPLACE_FUNCS + by a simple check. + (putc_unlocked): Removed check. Not used. + (strsep, mkdtemp, asprintf): Replaced checks by gnulib checks. + (xsize): Added will probably come handy soon. + (CFLAGS): Use -Wformat-security instead of + -Wformat-nonliteral. Add --Wno-format-y2k. + * gl/, gl/m4/: New. + 2005-05-15 Werner Koch * configure.ac: Remove option --disable-threads; require the use diff --git a/Makefile.am b/Makefile.am index e6cbde893..9fafb1102 100644 --- a/Makefile.am +++ b/Makefile.am @@ -19,7 +19,7 @@ ## Process this file with automake to produce Makefile.in -ACLOCAL_AMFLAGS = -I m4 +ACLOCAL_AMFLAGS = -I m4 -I gl/m4 AUTOMAKE_OPTIONS = dist-bzip2 EXTRA_DIST = scripts/config.rpath autogen.sh README.CVS @@ -59,7 +59,7 @@ else tests = tests endif -SUBDIRS = m4 intl jnlib common ${kbx} \ +SUBDIRS = m4 intl gl jnlib common ${kbx} \ ${gpg} ${sm} ${agent} ${scd} tools po doc ${tests} dist-hook: diff --git a/agent/ChangeLog b/agent/ChangeLog index 86f62be48..9c57ad43e 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,7 @@ +2005-06-01 Werner Koch + + * gpg-agent.c: Include setenv.h. + 2005-05-31 Werner Koch * agent.h (out_of_core): s/__inline__/inine. Noted by Ray Link. diff --git a/agent/Makefile.am b/agent/Makefile.am index df8ec322c..017b84795 100644 --- a/agent/Makefile.am +++ b/agent/Makefile.am @@ -21,7 +21,7 @@ bin_PROGRAMS = gpg-agent libexec_PROGRAMS = gpg-protect-tool gpg-preset-passphrase -AM_CPPFLAGS = -I$(top_srcdir)/common -I$(top_srcdir)/intl +AM_CPPFLAGS = -I$(top_srcdir)/gl -I$(top_srcdir)/common -I$(top_srcdir)/intl include $(top_srcdir)/am/cmacros.am @@ -44,7 +44,7 @@ gpg_agent_SOURCES = \ learncard.c -gpg_agent_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \ +gpg_agent_LDADD = ../gl/libgnu.a ../jnlib/libjnlib.a ../common/libcommon.a \ $(LIBGCRYPT_LIBS) $(PTH_LIBS) $(LIBASSUAN_LIBS) \ -lgpg-error @LIBINTL@ diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index c793e7eab..3537b07f0 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -48,6 +48,7 @@ #ifdef HAVE_W32_SYSTEM #include "../jnlib/w32-afunix.h" #endif +#include "setenv.h" enum cmd_and_opt_values diff --git a/autogen.sh b/autogen.sh index 67d28ed38..aaf0d0ea4 100755 --- a/autogen.sh +++ b/autogen.sh @@ -152,8 +152,8 @@ EOF fi -echo "Running aclocal -I m4 ${ACLOCAL_FLAGS:+$ACLOCAL_FLAGS }..." -$ACLOCAL -I m4 $ACLOCAL_FLAGS +echo "Running aclocal -I m4 -I gl/m4 ${ACLOCAL_FLAGS:+$ACLOCAL_FLAGS }..." +$ACLOCAL -I m4 -I gl/m4 $ACLOCAL_FLAGS echo "Running autoheader..." $AUTOHEADER echo "Running automake --gnu ..." diff --git a/common/ChangeLog b/common/ChangeLog index abb3c6427..fccc71d49 100644 --- a/common/ChangeLog +++ b/common/ChangeLog @@ -1,3 +1,13 @@ +2005-06-01 Werner Koch + + * Makefile.am (AM_CPPFLAGS): Added. + + * util.h: Add some includes for gnulib. + (ttyname, isascii): Define them inline. + * fseeko.c, ftello.c: Removed. + * strsep.c, mkdtemp.c: Removed. + * ttyname.c, isascii.c: Removed. + 2005-05-31 Werner Koch * dynload.h: s/__inline__/inline/. diff --git a/common/Makefile.am b/common/Makefile.am index a039be184..0f9b4324d 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -21,7 +21,9 @@ noinst_LIBRARIES = libcommon.a libsimple-pwquery.a -AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) $(PTH_CFLAGS) +AM_CPPFLAGS = -I$(top_srcdir)/gl + +AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) $(PTH_CFLAGS) libcommon_a_SOURCES = \ util.h i18n.h \ @@ -49,18 +51,6 @@ libcommon_a_SOURCES = \ estream.c estream.h -libcommon_a_LIBADD = @LIBOBJS@ - libsimple_pwquery_a_SOURCES = \ simple-pwquery.c simple-pwquery.h asshelp.c asshelp.h -libsimple_pwquery_a_LIBADD = @LIBOBJS@ - - - - - - - - - diff --git a/common/fseeko.c b/common/fseeko.c deleted file mode 100644 index 06838e4c4..000000000 --- a/common/fseeko.c +++ /dev/null @@ -1,41 +0,0 @@ -/* fseeko.c - libc replacement function - * Copyright (C) 2001 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 - */ - -#ifdef HAVE_CONFIG_H -#include -#endif -#include -#include /* Defines off_t under W32. */ - -int -fseeko (FILE *stream, off_t off, int whence) -{ - return fseek (stream, off, whence); -} - - - - - - - - - - diff --git a/common/ftello.c b/common/ftello.c deleted file mode 100644 index 6837be959..000000000 --- a/common/ftello.c +++ /dev/null @@ -1,46 +0,0 @@ -/* ftello.c - libc replacement function - * Copyright (C) 2001 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 - */ - -#ifdef HAVE_CONFIG_H -#include -#endif -#include -#include /* Defines off_t under W32. */ - -off_t -ftello (FILE *stream) -{ - long int off; - - off = ftell (stream); - if (off == -1) - return (off_t)-1; - return off; -} - - - - - - - - - - diff --git a/common/mkdtemp.c b/common/mkdtemp.c deleted file mode 100644 index a85b89eb4..000000000 --- a/common/mkdtemp.c +++ /dev/null @@ -1,97 +0,0 @@ -/* mkdtemp.c - libc replacement function - * Copyright (C) 2001 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 - */ - -/* This is a replacement function for mkdtemp in case the platform - we're building on (like mine!) doesn't have it. */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef MKDIR_TAKES_ONE_ARG -# undef mkdir -# define mkdir(a,b) mkdir(a) -#endif - -char * -mkdtemp (char *template) -{ - int attempts,idx,count=0; - unsigned char *ch; - - idx=strlen(template); - - /* Walk backwards to count all the Xes */ - while(idx>0 && template[idx-1]=='X') - { - count++; - idx--; - } - - if(count==0) - { - errno=EINVAL; - return NULL; - } - - ch=&template[idx]; - - /* Try 4 times to make the temp directory */ - for(attempts=0;attempts<4;attempts++) - { - int remaining=count; - char *marker=ch; - unsigned char *randombits; - - idx=0; - - randombits = gcry_xmalloc (4*remaining); - gcry_create_nonce (randombits, 4*remaining); - - while(remaining>1) - { - sprintf(marker,"%02X",randombits[idx++]); - marker+=2; - remaining-=2; - } - - /* Any leftover Xes? get_random_bits rounds up to full bytes, - so this is safe. */ - if(remaining>0) - sprintf(marker,"%X",randombits[idx]&0xF); - - gcry_free (randombits); - - if(mkdir(template,0700)==0) - break; - } - - if(attempts==4) - return NULL; /* keeps the errno from mkdir, whatever it is */ - - return template; -} - - diff --git a/common/putc_unlocked.c b/common/putc_unlocked.c deleted file mode 100644 index 02c646130..000000000 --- a/common/putc_unlocked.c +++ /dev/null @@ -1,31 +0,0 @@ -/* putc_unlocked.c - Replacement for putc_unlocked. - * Copyright (C) 2002 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 - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -int -putc_unlocked (int c, FILE *stream) -{ - return putc (c, stream); -} diff --git a/common/strsep.c b/common/strsep.c deleted file mode 100644 index dd01a826f..000000000 --- a/common/strsep.c +++ /dev/null @@ -1,76 +0,0 @@ -/* strsep.c - Replacement for strsep(). - * Copyright (C) 1992, 1993, 1996, 1997, 1998, 1999, - * 2004 Free Software Foundation, Inc. - * - * This file is part of the GNU C Library. - * - * The GNU C Library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * The GNU C Library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with the GNU C Library; if not, write to the Free - * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307 USA. - */ - -/* Code taken from glibc-2.3.2/sysdeps/generic/strsep.c and slightly - modified for use with GnuPG. */ - -#ifdef HAVE_CONFIG_H -#include -#endif -#include -#include - -char * -strsep (char **stringp, const char *delim) -{ - char *begin, *end; - - begin = *stringp; - if (begin == NULL) - return NULL; - - /* A frequent case is when the delimiter string contains only one - character. Here we don't need to call the expensive `strpbrk' - function and instead work using `strchr'. */ - if (delim[0] == '\0' || delim[1] == '\0') - { - char ch = delim[0]; - - if (ch == '\0') - end = NULL; - else - { - if (*begin == ch) - end = begin; - else if (*begin == '\0') - end = NULL; - else - end = strchr (begin + 1, ch); - } - } - else - /* Find the end of the token. */ - end = strpbrk (begin, delim); - - if (end) - { - /* Terminate the token and set *STRINGP past NUL character. */ - *end++ = '\0'; - *stringp = end; - } - else - /* No more delimiters; this is the last token. */ - *stringp = NULL; - - return begin; -} - diff --git a/common/ttyname.c b/common/ttyname.c deleted file mode 100644 index 822beef99..000000000 --- a/common/ttyname.c +++ /dev/null @@ -1,32 +0,0 @@ -/* ttyname.c - Replacement for ttyname. - * Copyright (C) 2004 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 - */ - -/* This one is a simple dummy and suitable for Dosish systems. */ - -#ifdef HAVE_CONFIG_H -#include -#endif -#include - -char * -ttyname (int fd) -{ - return NULL; -} diff --git a/common/util.h b/common/util.h index 6a9b54ef5..d233dbf5e 100644 --- a/common/util.h +++ b/common/util.h @@ -25,7 +25,13 @@ #include /* We need time_t. */ #include /* we need gpg-error_t. */ -/* to pass hash functions to libksba we need to cast it */ +/* Common GNUlib includes (-I ../gl/). */ +#include "strpbrk.h" +#include "strsep.h" +#include "vasprintf.h" + + +/* Hash function used with libksba. */ #define HASH_FNC ((void (*)(void *, const void*,size_t))gcry_md_write) /* get all the stuff from jnlib */ @@ -152,24 +158,26 @@ char *make_printable_string (const byte *p, size_t n, int delim); int is_file_compressed (const char *s, int *ret_rc); -/*-- replacement functions from funcname.c --*/ -#if !HAVE_VASPRINTF -#include -int vasprintf (char **result, const char *format, va_list args); -int asprintf (char **result, const char *format, ...) JNLIB_GCC_A_PRINTF(2,3); -#endif -#ifndef HAVE_STRSEP -char *strsep (char **stringp, const char *delim); -#endif + +/*-- Simple replacement functions. */ #ifndef HAVE_TTYNAME -char *ttyname (int fd); -#endif -#ifndef HAVE_MKDTEMP -char *mkdtemp (char *template); -#endif +/* Systems without ttyname (W32) will merely return NULL. */ +static inline char * +ttyname (int fd) +{ + return NULL +}; +#endif /* !HAVE_TTYNAME */ +#ifndef HAVE_ISASCII +static inline int +isascii (int c) +{ + return (((c) & ~0x7f) == 0); +} +#endif /* !HAVE_ISASCII */ -/*-- some macros to replace ctype ones and avoid locale problems --*/ +/*-- Macros to replace ctype ones to avoid locale problems. --*/ #define spacep(p) (*(p) == ' ' || *(p) == '\t') #define digitp(p) (*(p) >= '0' && *(p) <= '9') #define hexdigitp(a) (digitp (a) \ diff --git a/common/xasprintf.c b/common/xasprintf.c index a3b5e27ac..46740a2e6 100644 --- a/common/xasprintf.c +++ b/common/xasprintf.c @@ -43,7 +43,7 @@ xasprintf (const char *fmt, ...) return p; } -/* Same as above bit return NULL on memory failure. */ +/* Same as above but return NULL on memory failure. */ char * xtryasprintf (const char *fmt, ...) { diff --git a/configure.ac b/configure.ac index 369762b02..c1a3d77ea 100644 --- a/configure.ac +++ b/configure.ac @@ -337,6 +337,7 @@ AC_PROG_RANLIB AC_CHECK_TOOL(AR, ar, :) AC_PATH_PROG(PERL,"perl") AC_ISC_POSIX +gl_EARLY AC_SYS_LARGEFILE AC_CHECK_PROG(DOCBOOK_TO_MAN, docbook-to-man, yes, no) AM_CONDITIONAL(HAVE_DOCBOOK_TO_MAN, test "$ac_cv_prog_DOCBOOK_TO_MAN" = yes) @@ -794,23 +795,21 @@ AC_CHECK_FUNCS(strcasecmp strncasecmp ctermid times gmtime_r) AC_CHECK_FUNCS(memmove gettimeofday getrusage setrlimit clock_gettime) AC_CHECK_FUNCS(atexit raise getpagesize strftime nl_langinfo setlocale) AC_CHECK_FUNCS(waitpid wait4 sigaction sigprocmask rand pipe stat getaddrinfo) +AC_CHECK_FUNCS(fseeko ftello ttyname isascii) AC_CHECK_TYPES([struct sigaction, sigset_t],,,[#include ]) +# gnulib checks +gl_SOURCE_BASE(gl) +gl_M4_BASE(gl/m4) +gl_MODULES(setenv strsep mkdtemp vasprintf xsize) +gl_INIT + # These are needed by libjnlib - fixme: we should have macros for them AC_CHECK_FUNCS(memicmp stpcpy strlwr strtoul memmove stricmp strtol) AC_CHECK_FUNCS(getrusage setrlimit stat setlocale) AC_CHECK_FUNCS(flockfile funlockfile fopencookie funopen) -AC_REPLACE_FUNCS(vasprintf) -AC_REPLACE_FUNCS(mkdtemp) -AC_REPLACE_FUNCS(fseeko ftello) -AC_REPLACE_FUNCS(isascii) -AC_REPLACE_FUNCS(putc_unlocked) -AC_REPLACE_FUNCS(strsep) -AC_REPLACE_FUNCS(ttyname) - - # # check for gethrtime and run a testprogram to see whether @@ -989,7 +988,7 @@ fi if test "$GCC" = yes; then if test "$USE_MAINTAINER_MODE" = "yes"; then CFLAGS="$CFLAGS -Wall -Wcast-align -Wshadow -Wstrict-prototypes" - CFLAGS="$CFLAGS -Wformat-nonliteral" + CFLAGS="$CFLAGS -Wno-format-y2k -Wformat-security" else CFLAGS="$CFLAGS -Wall" fi @@ -1126,6 +1125,7 @@ AC_CONFIG_FILES([ m4/Makefile Makefile po/Makefile.in intl/Makefile +gl/Makefile jnlib/Makefile common/Makefile kbx/Makefile diff --git a/g10/ChangeLog b/g10/ChangeLog index bd4b54894..b33735e1f 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,8 @@ +2005-06-01 Werner Koch + + * mkdtemp.c: Removed. + * exec.c: Include mkdtemp.h + 2004-12-21 Werner Koch * gpgv.c, g10.c (main): Use default_hoemdir (). diff --git a/g10/Makefile.am b/g10/Makefile.am index 8e63e9335..f371dab4a 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -21,14 +21,14 @@ EXTRA_DIST = options.skel -AM_CPPFLAGS = -I$(top_srcdir)/common -I$(top_srcdir)/include \ - -I$(top_srcdir)/intl +AM_CPPFLAGS = -I$(top_srcdir)/gl -I$(top_srcdir)/common \ + -I$(top_srcdir)/include -I$(top_srcdir)/intl include $(top_srcdir)/am/cmacros.am AM_CFLAGS = $(LIBGCRYPT_CFLAGS) -needed_libs = ../common/libcommon.a ../jnlib/libjnlib.a +needed_libs = ../gl/libgnu.a ../common/libcommon.a ../jnlib/libjnlib.a bin_PROGRAMS = gpg2 gpgv2 diff --git a/g10/exec.c b/g10/exec.c index a49fe15d2..b1fc2c70f 100644 --- a/g10/exec.c +++ b/g10/exec.c @@ -39,6 +39,7 @@ #include "i18n.h" #include "iobuf.h" #include "util.h" +#include "mkdtemp.h" #include "exec.h" #ifdef NO_EXEC @@ -55,10 +56,6 @@ int set_exec_path(const char *path,int method) { return GPG_ERR_GENERAL; } #else /* ! NO_EXEC */ -#ifndef HAVE_MKDTEMP -char *mkdtemp(char *template); -#endif - #if defined (_WIN32) /* This is a nicer system() for windows that waits for programs to return before returning control to the caller. I hate helpful diff --git a/g10/mkdtemp.c b/g10/mkdtemp.c deleted file mode 100644 index 55e5b189f..000000000 --- a/g10/mkdtemp.c +++ /dev/null @@ -1,98 +0,0 @@ -/* mkdtemp.c - libc replacement function - * Copyright (C) 2001 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 - */ - -/* This is a replacement function for mkdtemp in case the platform - we're building on (like mine!) doesn't have it. */ - -#include -#include -#include -#include -#include -#include -#include -#include "types.h" -#include "cipher.h" - -#ifdef MKDIR_TAKES_ONE_ARG -# undef mkdir -# define mkdir(a,b) mkdir(a) -#endif - -char *mkdtemp(char *template) -{ - unsigned int attempts,idx,count=0; - byte *ch; - - idx=strlen(template); - - /* Walk backwards to count all the Xes */ - while(idx>0 && template[idx-1]=='X') - { - count++; - idx--; - } - - if(count==0) - { - errno=EINVAL; - return NULL; - } - - ch=&template[idx]; - - /* Try 4 times to make the temp directory */ - for(attempts=0;attempts<4;attempts++) - { - unsigned int remaining=count; - char *marker=ch; - byte *randombits; - - idx=0; - - /* Using really random bits is probably overkill here. The - worst thing that can happen with a directory name collision - is that the function will return an error. */ - - randombits=get_random_bits(4*remaining,0,0); - - while(remaining>1) - { - sprintf(marker,"%02X",randombits[idx++]); - marker+=2; - remaining-=2; - } - - /* Any leftover Xes? get_random_bits rounds up to full bytes, - so this is safe. */ - if(remaining>0) - sprintf(marker,"%X",randombits[idx]&0xF); - - xfree (randombits); - - if(mkdir(template,0700)==0) - break; - } - - if(attempts==4) - return NULL; /* keeps the errno from mkdir, whatever it is */ - - return template; -} diff --git a/kbx/ChangeLog b/kbx/ChangeLog index 3688174bd..7c112085c 100644 --- a/kbx/ChangeLog +++ b/kbx/ChangeLog @@ -1,3 +1,10 @@ +2005-06-01 Werner Koch + + * keybox-file.c (ftello) [!HAVE_FSEEKO]: New replacement + function. Copied from ../common/ftello.c. + * keybox-update.c (fseeko) [!HAVE_FSEEKO]: New replacement + function. Copied from ../common/iobuf.c. + 2004-12-18 Werner Koch * keybox-defs.h (map_assuan_err): Define in terms of diff --git a/kbx/Makefile.am b/kbx/Makefile.am index ea8436d72..85704eb66 100644 --- a/kbx/Makefile.am +++ b/kbx/Makefile.am @@ -23,7 +23,7 @@ localedir = $(datadir)/locale INCLUDES = -I../intl -DLOCALEDIR=\"$(localedir)\" EXTRA_DIST = mkerrors -AM_CPPFLAGS = -I$(top_srcdir)/common -I$(top_srcdir)/intl \ +AM_CPPFLAGS = -I$(top_srcdir)/gl -I$(top_srcdir)/common -I$(top_srcdir)/intl \ $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) noinst_LIBRARIES = libkeybox.a @@ -45,6 +45,7 @@ libkeybox_a_SOURCES = $(common_sources) # Note that libcommon is only required to resolve the LIBOBJS. kbxutil_SOURCES = kbxutil.c $(common_sources) -kbxutil_LDADD = ../jnlib/libjnlib.a $(KSBA_LIBS) $(LIBGCRYPT_LIBS) \ +kbxutil_LDADD = ../gl/libgnu.a ../jnlib/libjnlib.a \ + $(KSBA_LIBS) $(LIBGCRYPT_LIBS) \ -lgpg-error $(LIBINTL) ../common/libcommon.a diff --git a/kbx/keybox-file.c b/kbx/keybox-file.c index db3164fef..fe02c1f9f 100644 --- a/kbx/keybox-file.c +++ b/kbx/keybox-file.c @@ -27,6 +27,22 @@ #include "keybox-defs.h" + +#if !defined(HAVE_FTELLO) && !defined(ftello) +static off_t +ftello (FILE *stream) +{ + long int off; + + off = ftell (stream); + if (off == -1) + return (off_t)-1; + return off; +} +#endif /* !defined(HAVE_FTELLO) && !defined(ftello) */ + + + /* Read a block at the current postion and return it in r_blob. r_blob may be NULL to simply skip the current block */ int diff --git a/kbx/keybox-update.c b/kbx/keybox-update.c index eabaa1db2..a16c18e23 100644 --- a/kbx/keybox-update.c +++ b/kbx/keybox-update.c @@ -31,6 +31,38 @@ #define EXTSEP_S "." +#if !defined(HAVE_FSEEKO) && !defined(fseeko) + +#ifdef HAVE_LIMITS_H +# include +#endif +#ifndef LONG_MAX +# define LONG_MAX ((long) ((unsigned long) -1 >> 1)) +#endif +#ifndef LONG_MIN +# define LONG_MIN (-1 - LONG_MAX) +#endif + +/**************** + * A substitute for fseeko, for hosts that don't have it. + */ +static int +fseeko (FILE * stream, off_t newpos, int whence) +{ + while (newpos != (long) newpos) + { + long pos = newpos < 0 ? LONG_MIN : LONG_MAX; + if (fseek (stream, pos, whence) != 0) + return -1; + newpos -= pos; + whence = SEEK_CUR; + } + return fseek (stream, (long) newpos, whence); +} +#endif /* !defined(HAVE_FSEEKO) && !defined(fseeko) */ + + + static int create_tmp_file (const char *template, char **r_bakfname, char **r_tmpfname, FILE **r_fp) diff --git a/scd/ChangeLog b/scd/ChangeLog index ea1b0d287..136ed5618 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,7 @@ +2005-06-01 Werner Koch + + * scdaemon.c: Include mkdtemp.h. + 2005-05-31 Werner Koch * tlv.c [GNUPG_MAJOR_VERSION==1]: Define constants instead of diff --git a/scd/Makefile.am b/scd/Makefile.am index e76f83ea4..dadab4f67 100644 --- a/scd/Makefile.am +++ b/scd/Makefile.am @@ -23,7 +23,7 @@ if ! HAVE_W32_SYSTEM pkglib_PROGRAMS = pcsc-wrapper endif -AM_CPPFLAGS = -I$(top_srcdir)/intl -I$(top_srcdir)/common +AM_CPPFLAGS = -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/common include $(top_srcdir)/am/cmacros.am @@ -43,7 +43,7 @@ scdaemon_SOURCES = \ app.c app-common.h app-help.c $(card_apps) -scdaemon_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \ +scdaemon_LDADD = ../gl/libgnu.a ../jnlib/libjnlib.a ../common/libcommon.a \ $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(PTH_LIBS) $(LIBASSUAN_LIBS) \ $(LIBUSB_LIBS) -lgpg-error $(LIBINTL) $(DL_LIBS) diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 488a4853b..341719b1e 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -51,7 +51,7 @@ #include "../jnlib/w32-afunix.h" #endif #include "ccid-driver.h" - +#include "mkdtemp.h" enum cmd_and_opt_values { aNull = 0, diff --git a/sm/ChangeLog b/sm/ChangeLog index aa8e8671f..ffb61a294 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,3 +1,7 @@ +2005-06-01 Werner Koch + + * misc.c: Include setenv.h. + 2005-04-21 Werner Koch * gpgsm.c: New options --{enable,disable}-trusted-cert-crl-check. diff --git a/sm/Makefile.am b/sm/Makefile.am index f1a116ab5..e5311e967 100644 --- a/sm/Makefile.am +++ b/sm/Makefile.am @@ -24,7 +24,7 @@ bin_PROGRAMS = gpgsm AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(LIBASSUAN_CFLAGS) $(KSBA_CFLAGS) \ $(PTH_CFLAGS) -AM_CPPFLAGS = -I$(top_srcdir)/common -I$(top_srcdir)/intl +AM_CPPFLAGS = -I$(top_srcdir)/gl -I$(top_srcdir)/common -I$(top_srcdir)/intl include $(top_srcdir)/am/cmacros.am @@ -52,7 +52,7 @@ gpgsm_SOURCES = \ certreqgen.c -gpgsm_LDADD = ../jnlib/libjnlib.a ../kbx/libkeybox.a \ +gpgsm_LDADD = ../gl/libgnu.a ../jnlib/libjnlib.a ../kbx/libkeybox.a \ ../common/libcommon.a \ $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) -lgpg-error \ $(LIBINTL) $(PTH_LIBS) diff --git a/sm/misc.c b/sm/misc.c index 36c4dda70..cd072ce6b 100644 --- a/sm/misc.c +++ b/sm/misc.c @@ -31,6 +31,7 @@ #include "gpgsm.h" #include "i18n.h" +#include "setenv.h" /* Setup the environment so that the pinentry is able to get all required information. This is used prior to an exec of the diff --git a/tools/ChangeLog b/tools/ChangeLog index 103be28e5..de7e5b89b 100644 --- a/tools/ChangeLog +++ b/tools/ChangeLog @@ -1,3 +1,7 @@ +2005-06-01 Werner Koch + + * symcryptrun.c: Include mkdtemp.h. + 2005-05-31 Werner Koch * watchgnupg.c: Make sure that PF_LCOAL and AF_LOCAL are defines. diff --git a/tools/Makefile.am b/tools/Makefile.am index 6d2d2e741..bc66118bf 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -21,7 +21,7 @@ EXTRA_DIST = Manifest watchgnupg.c \ rfc822parse.c rfc822parse.h gpgparsemail.c \ addgnupghome gpgsm-gencert.sh -AM_CPPFLAGS = -I$(top_srcdir)/intl -I$(top_srcdir)/common +AM_CPPFLAGS = -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/common include $(top_srcdir)/am/cmacros.am AM_CFLAGS = $(GPG_ERROR_CFLAGS) $(LIBASSUAN_CFLAGS) @@ -43,19 +43,23 @@ endif gpgconf_SOURCES = gpgconf.c gpgconf.h gpgconf-comp.c no-libgcrypt.c -gpgconf_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a @LIBINTL@ +gpgconf_LDADD = ../gl/libgnu.a ../jnlib/libjnlib.a \ + ../common/libcommon.a @LIBINTL@ symcryptrun_SOURCES = symcryptrun.c -symcryptrun_LDADD = $(LIBUTIL_LIBS) ../jnlib/libjnlib.a ../common/libcommon.a \ +symcryptrun_LDADD = $(LIBUTIL_LIBS) ../gl/libgnu.a ../jnlib/libjnlib.a \ + ../common/libcommon.a \ ../common/libsimple-pwquery.a $(LIBGCRYPT_LIBS) \ $(GPG_ERROR_LIBS) $(LIBINTL) watchgnupg_SOURCES = watchgnupg.c gpg_connect_agent_SOURCES = gpg-connect-agent.c no-libgcrypt.c -gpg_connect_agent_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \ +gpg_connect_agent_LDADD = ../gl/libgnu.a ../jnlib/libjnlib.a \ + ../common/libcommon.a \ $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) $(LIBINTL) gpgkey2ssh_SOURCES = gpgkey2ssh.c gpgkey2ssh_CFLAGS = $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS) -gpgkey2ssh_LDADD = ../common/libcommon.a $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) +gpgkey2ssh_LDADD = ../gl/libgnu.a ../common/libcommon.a \ + $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) diff --git a/tools/symcryptrun.c b/tools/symcryptrun.c index 6771ab953..09fc8582f 100644 --- a/tools/symcryptrun.c +++ b/tools/symcryptrun.c @@ -82,6 +82,7 @@ #define JNLIB_NEED_LOG_LOGV #include "i18n.h" #include "../common/util.h" +#include "mkdtemp.h" /* FIXME: Bah. For spwq_secure_free. */ #define SIMPLE_PWQUERY_IMPLEMENTATION 1 -- cgit From f1dac8851d02a0cb63fc7379ee74692856d0cf39 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 3 Jun 2005 13:57:24 +0000 Subject: * command.c (cmd_updatestartuptty): New. * gpg-agent.c: New option --write-env-file. * gpg-agent.c (handle_connections): Make sure that the signals we are handling are not blocked.Block signals while creating new threads. * estream.c: Use HAVE_CONFIG_H and not USE_CONFIG_H! (es_func_fd_read, es_func_fd_write): Protect against EINTR. * gpg-agent.texi (Agent UPDATESTARTUPTTY): New. * scdaemon.c (handle_connections): Make sure that the signals we are handling are not blocked.Block signals while creating new threads. (handle_connections): Include the file descriptor into the name of the thread. --- ChangeLog | 5 ++ NEWS | 3 + TODO | 7 +++ agent/ChangeLog | 17 ++++++ agent/agent.h | 14 +++-- agent/call-scd.c | 30 ++++++++++ agent/command.c | 34 +++++++++++ agent/gpg-agent.c | 104 ++++++++++++++++++++++++++------- common/ChangeLog | 6 ++ common/estream.c | 10 +++- configure.ac | 3 + doc/ChangeLog | 4 ++ doc/gpg-agent.texi | 60 +++++++++++++++---- scd/ChangeLog | 29 ++++++++++ scd/app-common.h | 6 ++ scd/app-openpgp.c | 2 +- scd/app.c | 167 ++++++++++++++++++++++++++++++++++++++--------------- scd/command.c | 6 +- scd/scdaemon.c | 33 ++++++++--- scd/scdaemon.h | 23 +++++--- 20 files changed, 459 insertions(+), 104 deletions(-) (limited to 'scd/scdaemon.c') diff --git a/ChangeLog b/ChangeLog index fbd9ad79d..f7efcee89 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2005-06-02 Werner Koch + + * configure.ac (HAVE_PTH): Define as alias for USE_GNU_PTH. It is + used by common/estream.c. + 2005-06-01 Werner Koch * configure.ac (gl_INIT): Add gnulib stuff. diff --git a/NEWS b/NEWS index 79a74cbe4..e28f1284a 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,9 @@ Noteworthy changes in version 1.9.17 does allows only signing using TCOS cards but we are going to enhance it to match all the old capabilities. + * [gpg-agent] New option --rite-env-file and Assuan command + UPDATESTARTUPTTY. + Noteworthy changes in version 1.9.16 (2005-04-21) ------------------------------------------------- diff --git a/TODO b/TODO index 74763a71f..5f1b57a0f 100644 --- a/TODO +++ b/TODO @@ -72,6 +72,13 @@ might want to have an agent context for each service request we can change all S-expression handling code to make use of this function. +* scd +** Application context vs. reader slot + We have 2 concurrent method of tracking whether a read is in use: + Using the session_list in command.c and the lock_table in app.c. IT + would be better to do this just at one place. First we need to see + how we can support cards with multiple applications. + * tests ** Makefile.am We use printf(1) to setup the library path, this is not portable. diff --git a/agent/ChangeLog b/agent/ChangeLog index 9c57ad43e..9621e5de0 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,20 @@ +2005-06-03 Werner Koch + + * command.c (cmd_updatestartuptty): New. + + * gpg-agent.c: New option --write-env-file. + + * gpg-agent.c (handle_connections): Make sure that the signals we + are handling are not blocked.Block signals while creating new + threads. + +2005-06-02 Werner Koch + + * call-scd.c (agent_scd_dump_state, dump_mutex_state): New. + * gpg-agent.c (handle_signal): Print it on SIGUSR1. + (handle_connections): Include the file descriptor into the + threadnames. + 2005-06-01 Werner Koch * gpg-agent.c: Include setenv.h. diff --git a/agent/agent.h b/agent/agent.h index a667c0d46..51e66abee 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -54,12 +54,13 @@ struct { int batch; /* Batch mode */ const char *homedir; /* Configuration directory name */ - /* Environment setting gathred at program start. */ - const char *startup_display; - const char *startup_ttyname; - const char *startup_ttytype; - const char *startup_lc_ctype; - const char *startup_lc_messages; + /* Environment setting gathered at program start or hanged using the + Assuan command UPDATESTARTUPTTY. */ + char *startup_display; + char *startup_ttyname; + char *startup_ttytype; + char *startup_lc_ctype; + char *startup_lc_messages; const char *pinentry_program; /* Filename of the program to start as @@ -248,6 +249,7 @@ int divert_generic_cmd (ctrl_t ctrl, /*-- call-scd.c --*/ void initialize_module_call_scd (void); +void agent_scd_dump_state (void); void agent_scd_check_aliveness (void); int agent_reset_scd (ctrl_t ctrl); int agent_card_learn (ctrl_t ctrl, diff --git a/agent/call-scd.c b/agent/call-scd.c index 78e28fe97..00c9df2a7 100644 --- a/agent/call-scd.c +++ b/agent/call-scd.c @@ -116,6 +116,35 @@ initialize_module_call_scd (void) } +static void +dump_mutex_state (pth_mutex_t *m) +{ + if (!(m->mx_state & PTH_MUTEX_INITIALIZED)) + log_printf ("not_initialized"); + else if (!(m->mx_state & PTH_MUTEX_LOCKED)) + log_printf ("not_locked"); + else + log_printf ("locked tid=0x%lx count=%lu", (long)m->mx_owner, m->mx_count); +} + + +/* This function may be called to print infromation pertaining to the + current state of this module to the log. */ +void +agent_scd_dump_state (void) +{ + log_info ("agent_scd_dump_state: scd_lock="); + dump_mutex_state (&start_scd_lock); + log_printf ("\n"); + log_info ("agent_scd_dump_state: primary_scd_ctx=%p pid=%ld reusable=%d\n", + primary_scd_ctx, + (long)assuan_get_pid (primary_scd_ctx), + primary_scd_ctx_reusable); + if (socket_name) + log_info ("agent_scd_dump_state: socket=`%s'\n", socket_name); +} + + /* The unlock_scd function shall be called after having accessed the SCD. It is currently not very useful but gives an opportunity to keep track of connections currently calling SCD. Note that the @@ -384,6 +413,7 @@ agent_scd_check_aliveness (void) } + /* Reset the SCD if it has been used. */ int agent_reset_scd (ctrl_t ctrl) diff --git a/agent/command.c b/agent/command.c index 8af159f6d..56167118d 100644 --- a/agent/command.c +++ b/agent/command.c @@ -867,6 +867,39 @@ cmd_scd (ASSUAN_CONTEXT ctx, char *line) } + +/* UPDATESTARTUPTTY + + Set startup TTY and X DISPLAY variables to the values of this + session. This command is useful to pull future pinentries to + another screen. It is only required because there is no way in the + ssh-agent protocol to convey this information. */ +static int +cmd_updatestartuptty (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + + xfree (opt.startup_display); opt.startup_display = NULL; + xfree (opt.startup_ttyname); opt.startup_ttyname = NULL; + xfree (opt.startup_ttytype); opt.startup_ttytype = NULL; + xfree (opt.startup_lc_ctype); opt.startup_lc_ctype = NULL; + xfree (opt.startup_lc_messages); opt.startup_lc_messages = NULL; + + if (ctrl->display) + opt.startup_display = xtrystrdup (ctrl->display); + if (ctrl->ttyname) + opt.startup_ttyname = xtrystrdup (ctrl->ttyname); + if (ctrl->ttytype) + opt.startup_ttytype = xtrystrdup (ctrl->ttytype); + if (ctrl->lc_ctype) + opt.startup_lc_ctype = xtrystrdup (ctrl->lc_ctype); + if (ctrl->lc_messages) + opt.startup_lc_messages = xtrystrdup (ctrl->lc_messages); + + return 0; +} + + static int option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value) @@ -957,6 +990,7 @@ register_commands (ASSUAN_CONTEXT ctx) { "INPUT", NULL }, { "OUTPUT", NULL }, { "SCD", cmd_scd }, + { "UPDATESTARTUPTTY", cmd_updatestartuptty }, { NULL } }; int i, rc; diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 3537b07f0..90b071d5e 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -93,7 +93,8 @@ enum cmd_and_opt_values oKeepTTY, oKeepDISPLAY, oSSHSupport, - oDisableScdaemon + oDisableScdaemon, + oWriteEnvFile }; @@ -147,6 +148,8 @@ static ARGPARSE_OPTS opts[] = { { oAllowPresetPassphrase, "allow-preset-passphrase", 0, N_("allow presetting passphrase")}, { oSSHSupport, "enable-ssh-support", 0, N_("enable ssh-agent emulation") }, + { oWriteEnvFile, "write-env-file", 2, + N_("|FILE|write environment settings also to FILE")}, {0} }; @@ -438,6 +441,7 @@ main (int argc, char **argv ) int gpgconf_list = 0; int standard_socket = 0; gpg_error_t err; + const char *env_file_name = NULL; set_strusage (my_strusage); gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); @@ -501,7 +505,7 @@ main (int argc, char **argv ) opt.startup_ttytype = getenv ("TERM"); if (opt.startup_ttytype) opt.startup_ttytype = xstrdup (opt.startup_ttytype); - /* Fixme: Neen to use the locale fucntion here. */ + /* Fixme: Better use the locale function here. */ opt.startup_lc_ctype = getenv ("LC_CTYPE"); if (opt.startup_lc_ctype) opt.startup_lc_ctype = xstrdup (opt.startup_lc_ctype); @@ -619,6 +623,7 @@ main (int argc, char **argv ) case oKeepDISPLAY: opt.keep_display = 1; break; case oSSHSupport: opt.ssh_support = 1; break; + case oWriteEnvFile: env_file_name = pargs.r.ret_str; break; default : pargs.err = configfp? 1:2; break; } @@ -855,6 +860,29 @@ main (int argc, char **argv ) if (opt.ssh_support) *socket_name_ssh = 0; + if (env_file_name) + { + FILE *fp; + + fp = fopen (env_file_name, "w"); + if (!fp) + log_error (_("error creating `%s': %s\n"), + env_file_name, strerror (errno)); + else + { + fputs (infostr, fp); + putc ('\n', fp); + if (opt.ssh_support) + { + fputs (infostr_ssh_sock, fp); + putc ('\n', fp); + fputs (infostr_ssh_pid, fp); + putc ('\n', fp); + } + fclose (fp); + } + } + if (argc) { /* Run the program given on the commandline. */ @@ -1273,7 +1301,7 @@ create_directories (void) static void handle_tick (void) { - /* Check whether the scdaemon has dies and cleanup in this case. */ + /* Check whether the scdaemon has died and cleanup in this case. */ agent_scd_check_aliveness (); /* If we are running as a child of another process, check whether @@ -1311,6 +1339,7 @@ handle_signal (int signo) case SIGUSR1: log_info ("SIGUSR1 received - printing internal information:\n"); pth_ctrl (PTH_CTRL_DUMPSTATE, log_get_stream ()); + agent_scd_dump_state (); break; case SIGUSR2: @@ -1353,7 +1382,8 @@ start_connection_thread (void *arg) int fd = (int)arg; if (opt.verbose) - log_info (_("handler for fd %d started\n"), fd); + log_info (_("handler 0x%lx for fd %d started\n"), + (long)pth_self (), fd); /* FIXME: Move this housekeeping into a ticker function. Calling it for each connection should work but won't work anymore if our @@ -1362,7 +1392,8 @@ start_connection_thread (void *arg) start_command_handler (-1, fd); if (opt.verbose) - log_info (_("handler for fd %d terminated\n"), fd); + log_info (_("handler 0x%lx for fd %d terminated\n"), + (long)pth_self (), fd); return NULL; } @@ -1375,13 +1406,15 @@ start_connection_thread_ssh (void *arg) int fd = (int)arg; if (opt.verbose) - log_info (_("ssh handler for fd %d started\n"), fd); + log_info (_("ssh handler 0x%lx for fd %d started\n"), + (long)pth_self (), fd); agent_trustlist_housekeeping (); start_command_handler_ssh (fd); if (opt.verbose) - log_info (_("ssh handler for fd %d terminated\n"), fd); + log_info (_("ssh handler 0x%lx for fd %d terminated\n"), + (long)pth_self (), fd); return NULL; } @@ -1405,15 +1438,17 @@ handle_connections (int listen_fd, int listen_fd_ssh) tattr = pth_attr_new(); pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0); pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 256*1024); - pth_attr_set (tattr, PTH_ATTR_NAME, "gpg-agent"); #ifndef HAVE_W32_SYSTEM /* fixme */ + /* Make sure that the signals we are going to handle are not blocked + and create an event object for them. */ sigemptyset (&sigs ); sigaddset (&sigs, SIGHUP); sigaddset (&sigs, SIGUSR1); sigaddset (&sigs, SIGUSR2); sigaddset (&sigs, SIGINT); sigaddset (&sigs, SIGTERM); + pth_sigmask (SIG_UNBLOCK, &sigs, NULL); ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo); #else ev = NULL; @@ -1427,6 +1462,8 @@ handle_connections (int listen_fd, int listen_fd_ssh) for (;;) { + sigset_t oldsigs; + if (shutdown_pending) { if (pth_ctrl (PTH_CTRL_GETTHREADS) == 1) @@ -1488,6 +1525,12 @@ handle_connections (int listen_fd, int listen_fd_ssh) handle_tick (); } + + /* We now might create new threads and because we don't want any + signals - we are handling here - to be delivered to a new + thread. Thus we need to block those signals. */ + pth_sigmask (SIG_BLOCK, &sigs, &oldsigs); + if (FD_ISSET (listen_fd, &read_fdset)) { plen = sizeof paddr; @@ -1496,12 +1539,20 @@ handle_connections (int listen_fd, int listen_fd_ssh) { log_error ("accept failed: %s\n", strerror (errno)); } - else if (!pth_spawn (tattr, start_connection_thread, (void*)fd)) - { - log_error ("error spawning connection handler: %s\n", - strerror (errno) ); - close (fd); - } + else + { + char threadname[50]; + snprintf (threadname, sizeof threadname-1, + "conn fd=%d (gpg)", fd); + threadname[sizeof threadname -1] = 0; + pth_attr_set (tattr, PTH_ATTR_NAME, threadname); + if (!pth_spawn (tattr, start_connection_thread, (void*)fd)) + { + log_error ("error spawning connection handler: %s\n", + strerror (errno) ); + close (fd); + } + } fd = -1; } @@ -1513,14 +1564,27 @@ handle_connections (int listen_fd, int listen_fd_ssh) { log_error ("accept failed for ssh: %s\n", strerror (errno)); } - else if (!pth_spawn (tattr, start_connection_thread_ssh, (void*)fd)) - { - log_error ("error spawning ssh connection handler: %s\n", - strerror (errno) ); - close (fd); - } + else + { + char threadname[50]; + snprintf (threadname, sizeof threadname-1, + "conn fd=%d (ssh)", fd); + threadname[sizeof threadname -1] = 0; + pth_attr_set (tattr, PTH_ATTR_NAME, threadname); + + if (!pth_spawn (tattr, start_connection_thread_ssh, (void*)fd)) + { + log_error ("error spawning ssh connection handler: %s\n", + strerror (errno) ); + close (fd); + } + } fd = -1; } + + /* Restore the signal mask. */ + pth_sigmask (SIG_SETMASK, &oldsigs, NULL); + } pth_event_free (ev, PTH_FREE_ALL); diff --git a/common/ChangeLog b/common/ChangeLog index fccc71d49..08fb06775 100644 --- a/common/ChangeLog +++ b/common/ChangeLog @@ -1,3 +1,9 @@ +2005-06-03 Werner Koch + + * estream.c: Use HAVE_CONFIG_H and not USE_CONFIG_H! + (es_func_fd_read, es_func_fd_write): Protect against EINTR. + + 2005-06-01 Werner Koch * Makefile.am (AM_CPPFLAGS): Added. diff --git a/common/estream.c b/common/estream.c index 00cb749e8..bf5b02001 100644 --- a/common/estream.c +++ b/common/estream.c @@ -22,7 +22,7 @@ # include #endif -#ifdef USE_CONFIG_H +#ifdef HAVE_CONFIG_H # include #endif @@ -597,7 +597,9 @@ es_func_fd_read (void *cookie, char *buffer, size_t size) estream_cookie_fd_t file_cookie = cookie; ssize_t bytes_read; - bytes_read = ESTREAM_SYS_READ (file_cookie->fd, buffer, size); + do + bytes_read = ESTREAM_SYS_READ (file_cookie->fd, buffer, size); + while (bytes_read == -1 && errno == EINTR); return bytes_read; } @@ -610,7 +612,9 @@ es_func_fd_write (void *cookie, const char *buffer, size_t size) estream_cookie_fd_t file_cookie = cookie; ssize_t bytes_written; - bytes_written = ESTREAM_SYS_WRITE (file_cookie->fd, buffer, size); + do + bytes_written = ESTREAM_SYS_WRITE (file_cookie->fd, buffer, size); + while (bytes_written == -1 && errno == EINTR); return bytes_written; } diff --git a/configure.ac b/configure.ac index c1a3d77ea..17465c520 100644 --- a/configure.ac +++ b/configure.ac @@ -531,6 +531,8 @@ if test "$have_w32_system" = no; then PTH_LIBS="$PTH_LIBS `$PTH_CONFIG --libs`" AC_DEFINE(USE_GNU_PTH, 1, [Defined if the GNU Portable Thread Library should be used]) + AC_DEFINE(HAVE_PTH, 1, + [Defined if the GNU Pth is available]) fi fi else @@ -538,6 +540,7 @@ else PTH_CFLAGS="" PTH_LIBS="" AC_DEFINE(USE_GNU_PTH, 1) + AC_DEFINE(HAVE_PTH, 1) fi AC_SUBST(PTH_CFLAGS) AC_SUBST(PTH_LIBS) diff --git a/doc/ChangeLog b/doc/ChangeLog index 25840a5b1..f353bdf03 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,7 @@ +2005-06-03 Werner Koch + + * gpg-agent.texi (Agent UPDATESTARTUPTTY): New. + 2005-05-17 Werner Koch * gpg-agent.texi (Agent Options): Removed --disable-pth. diff --git a/doc/gpg-agent.texi b/doc/gpg-agent.texi index fa005c3b7..5e8c19468 100644 --- a/doc/gpg-agent.texi +++ b/doc/gpg-agent.texi @@ -25,11 +25,11 @@ eval `gpg-agent --daemon` @noindent If you don't use an X server, you can also put this into your regular startup file @code{~/.profile} or @code{.bash_profile}. It is best not -to run multiple instance of the @command{gpg-agent}, so you should make sure that -only is running: @command{gpg-agent} uses an environment variable to inform -clients about the communication parameters. You can write the -content of this environment variable to a file so that you can test for -a running agent. This short script may do the job: +to run multiple instance of the @command{gpg-agent}, so you should make +sure that only one is running: @command{gpg-agent} uses an environment +variable to inform clients about the communication parameters. You can +write the content of this environment variable to a file so that you can +test for a running agent. This short script may do the job: @smallexample if test -f $HOME/.gpg-agent-info && \ @@ -42,6 +42,9 @@ else fi @end smallexample +The new option @option{--write-env-file} may be used instead. + + @noindent You should always add the following lines to your @code{.bashrc} or whatever initialization file is used for all shell invocations: @@ -243,6 +246,21 @@ shell respective the C-shell . The default ist to guess it based on the environment variable @code{SHELL} which is in almost all cases sufficient. +@item --write-env-file @var{file} +@opindex write-env-file +Often it is required to connect to the agent from a process not being an +inferior of @command{gpg-agent} and thus the environment variable with +the socket name is not available. To help setting up those variables in +other sessions, this option may be used to write the information into +@var{file}. The format is suitable to be evaluated by a Bourne shell +like in this simple example: + +@example +eval `cat @var{file}` +eval `cut -d= -f 1 < @var{file} | xargs echo export` +@end example + + @item --no-grab @opindex no-grab Tell the pinentryo not to grab the keyboard and mouse. This option @@ -353,12 +371,19 @@ directory. Once, a key has been added to the gpg-agent this way, the gpg-agent will be ready to use the key. -Note: in case the gpg-agent receives a signature request, the user -might need to be prompted for a passphrase, which is necessary for -decrypting the stored key. Since the ssh-agent protocol does not -contain a mechanism for telling the agent on which display/terminal it -is running, gpg-agent's ssh-support will use the TTY or X display where -gpg-agent has been started. +Note: in case the gpg-agent receives a signature request, the user might +need to be prompted for a passphrase, which is necessary for decrypting +the stored key. Since the ssh-agent protocol does not contain a +mechanism for telling the agent on which display/terminal it is running, +gpg-agent's ssh-support will use the TTY or X display where gpg-agent +has been started. To switch this display to the current one, the +follwing command may be used: + +@smallexample +echo UPDATESTARTUPTTY | gpg-connect-agent +@end smallexample + + @end table @@ -544,6 +569,7 @@ secret keys. * Agent HAVEKEY:: Check whether a key is available * Agent LEARN:: Register a smartcard * Agent PASSWD:: Change a Passphrase +* Agent UPDATESTARTUPTTY:: Change the Standard Display @end menu @node Agent PKDECRYPT @@ -944,4 +970,16 @@ This command is used to interactively change the passphrase of the key indentified by the hex string @var{keygrip}. +@node Agent UPDATESTARTUPTTY +@subsection Change the standard display + +@example + UPDATESTARTUPTTY +@end example + +Set the startup TTY and X-DISPLAY variables to the values of this +session. This command is useful to direct future pinentry invocations +to another screen. It is only required because there is no way in the +ssh-agent protocol to convey this information. + diff --git a/scd/ChangeLog b/scd/ChangeLog index 136ed5618..da433e2f8 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,32 @@ +2005-06-03 Werner Koch + + * scdaemon.c (handle_connections): Make sure that the signals we + are handling are not blocked.Block signals while creating new + threads. + (handle_connections): Include the file descriptor into the name of + the thread. + +2005-06-02 Werner Koch + + * app.c (app_dump_state, dump_mutex_state): New. + * scdaemon.c (handle_signal): Print it on SIGUSR1. + + * app-openpgp.c (do_writekey): Typo fix. + + * command.c (open_card): Check for locked state even if an + application context is available. + + * app-common.h: Add REF_COUNT field. + * app.c (release_application, select_application): Implement + reference counting to share the context beween connections. + + * app.c (lock_reader, unlock_reader): Take SLOT instead of APP as + argument. Changed all callers. + (select_application): Unlock the reader on error. This should fix + the hangs I noticed last week. + + * scdaemon.h: Removed card_ctx_t cruft. + 2005-06-01 Werner Koch * scdaemon.c: Include mkdtemp.h. diff --git a/scd/app-common.h b/scd/app-common.h index 812736ece..94087f221 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -39,6 +39,11 @@ struct app_ctx_s { function pointers may be used. Note that for unsupported operations the particular function pointer is set to NULL */ + + int ref_count; /* Number of connections currently using this + application context. fixme: We might want to + merg this witghn INITIALIZED above. */ + int slot; /* Used reader. */ /* If this is used by GnuPG 1.4 we need to know the assuan context @@ -123,6 +128,7 @@ size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff); /*-- app.c --*/ +void app_dump_state (void); gpg_error_t select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app); void release_application (app_t app); diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 14483869b..1ff096138 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -1745,7 +1745,7 @@ do_writekey (app_t app, ctrl_t ctrl, nbits = rsa_e? count_bits (rsa_e, rsa_e_len) : 0; if (nbits < 2 || nbits > 32) { - log_error (_("RSA public exponent missing or largerr than %d bits\n"), + log_error (_("RSA public exponent missing or larger than %d bits\n"), 32); err = gpg_error (GPG_ERR_BAD_SECKEY); goto leave; diff --git a/scd/app.c b/scd/app.c index f2c427f5b..2c8c915d7 100644 --- a/scd/app.c +++ b/scd/app.c @@ -39,25 +39,24 @@ static struct { int initialized; pth_mutex_t lock; + app_t app; /* Application context in use or NULL. */ } lock_table[10]; -/* Lock the reader associated with the APP context. This function - shall be used right before calling any of the actual application - functions to serialize access to the reader. We do this always - even if the reader is not actually used. This allows an actual - application to assume that it never shares a reader (while - performing one command). Returns 0 on success; only then the - unlock_reader function must be called after returning from the - handler. */ +/* Lock the reader SLOT. This function shall be used right before + calling any of the actual application functions to serialize access + to the reader. We do this always even if the reader is not + actually used. This allows an actual connection to assume that it + never shares a reader (while performing one command). Returns 0 on + success; only then the unlock_reader function must be called after + returning from the handler. */ static gpg_error_t -lock_reader (app_t app) +lock_reader (int slot) { gpg_error_t err; - int slot = app->slot; if (slot < 0 || slot >= DIM (lock_table)) - return gpg_error (app->slot<0? GPG_ERR_INV_VALUE : GPG_ERR_RESOURCE_LIMIT); + return gpg_error (slot<0? GPG_ERR_INV_VALUE : GPG_ERR_RESOURCE_LIMIT); if (!lock_table[slot].initialized) { @@ -68,6 +67,7 @@ lock_reader (app_t app) return err; } lock_table[slot].initialized = 1; + lock_table[slot].app = NULL; } if (!pth_mutex_acquire (&lock_table[slot].lock, 0, NULL)) @@ -83,10 +83,8 @@ lock_reader (app_t app) /* Release a lock on the reader. See lock_reader(). */ static void -unlock_reader (app_t app) +unlock_reader (int slot) { - int slot = app->slot; - if (slot < 0 || slot >= DIM (lock_table) || !lock_table[slot].initialized) log_bug ("unlock_reader called for invalid slot %d\n", slot); @@ -96,6 +94,39 @@ unlock_reader (app_t app) slot, strerror (errno)); } + + +static void +dump_mutex_state (pth_mutex_t *m) +{ + if (!(m->mx_state & PTH_MUTEX_INITIALIZED)) + log_printf ("not_initialized"); + else if (!(m->mx_state & PTH_MUTEX_LOCKED)) + log_printf ("not_locked"); + else + log_printf ("locked tid=0x%lx count=%lu", (long)m->mx_owner, m->mx_count); +} + + +/* This function may be called to print information pertaining to the + current state of this module to the log. */ +void +app_dump_state (void) +{ + int slot; + + for (slot=0; slot < DIM (lock_table); slot++) + if (lock_table[slot].initialized) + { + log_info ("app_dump_state: slot=%d lock=", slot); + dump_mutex_state (&lock_table[slot].lock); + if (lock_table[slot].app) + log_printf (" app=%p type=`%s'", + lock_table[slot].app, lock_table[slot].app->apptype); + log_printf ("\n"); + } +} + /* Check wether the application NAME is allowed. This does not mean we have support for it though. */ @@ -120,23 +151,48 @@ gpg_error_t select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app) { gpg_error_t err; - app_t app; + app_t app = NULL; unsigned char *result = NULL; size_t resultlen; *r_app = NULL; + + err = lock_reader (slot); + if (err) + return err; + + /* First check whether we already have an application to share. */ + app = lock_table[slot].initialized ? lock_table[slot].app : NULL; + if (app && name) + if (!app->apptype || ascii_strcasecmp (app->apptype, name)) + { + unlock_reader (slot); + if (app->apptype) + log_info ("application `%s' in use by reader %d - can't switch\n", + app->apptype, slot); + return gpg_error (GPG_ERR_CONFLICT); + } + + if (app) + { + if (app->slot != slot) + log_bug ("slot mismatch %d/%d\n", app->slot, slot); + app->ref_count++; + *r_app = app; + unlock_reader (slot); + return 0; /* Okay: We share that one. */ + } + app = xtrycalloc (1, sizeof *app); if (!app) { err = gpg_error_from_errno (errno); log_info ("error allocating context: %s\n", gpg_strerror (err)); + unlock_reader (slot); return err; } app->slot = slot; - err = lock_reader (app); - if (err) - return err; /* Fixme: We should now first check whether a card is at all present. */ @@ -162,7 +218,7 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app) have some test cards with such an invalid encoding and therefore I use this ugly workaround to return something I can further experiment with. */ - log_debug ("enabling BMI testcard workaround\n"); + log_info ("enabling BMI testcard workaround\n"); n--; } @@ -212,22 +268,41 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app) log_info ("no supported card application found: %s\n", gpg_strerror (err)); xfree (app); + unlock_reader (slot); return err; } app->initialized = 1; - unlock_reader (app); + app->ref_count = 1; + lock_table[slot].app = app; *r_app = app; + unlock_reader (slot); return 0; } +/* Free the resources associated with the application APP. APP is + allowed to be NULL in which case this is a no-op. Note that we are + using reference counting to track the users of the application. */ void release_application (app_t app) { + int slot; + if (!app) return; + if (app->ref_count < 1) + log_bug ("trying to release an already released context\n"); + if (--app->ref_count) + return; + + /* Clear the reference to the application from the lock table. */ + for (slot = 0; slot < DIM (lock_table); slot++) + if (lock_table[slot].initialized && lock_table[slot].app == app) + lock_table[slot].app = NULL; + + /* Deallocate. */ if (app->fnc.deinit) { app->fnc.deinit (app); @@ -320,11 +395,11 @@ app_write_learn_status (app_t app, CTRL ctrl) if (app->apptype) send_status_info (ctrl, "APPTYPE", app->apptype, strlen (app->apptype), NULL, 0); - err = lock_reader (app); + err = lock_reader (app->slot); if (err) return err; err = app->fnc.learn_status (app, ctrl); - unlock_reader (app); + unlock_reader (app->slot); return err; } @@ -345,11 +420,11 @@ app_readcert (app_t app, const char *certid, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.readcert) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_reader (app); + err = lock_reader (app->slot); if (err) return err; err = app->fnc.readcert (app, certid, cert, certlen); - unlock_reader (app); + unlock_reader (app->slot); return err; } @@ -377,11 +452,11 @@ app_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen) return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.readkey) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_reader (app); + err = lock_reader (app->slot); if (err) return err; err= app->fnc.readkey (app, keyid, pk, pklen); - unlock_reader (app); + unlock_reader (app->slot); return err; } @@ -419,11 +494,11 @@ app_getattr (app_t app, CTRL ctrl, const char *name) if (!app->fnc.getattr) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_reader (app); + err = lock_reader (app->slot); if (err) return err; err = app->fnc.getattr (app, ctrl, name); - unlock_reader (app); + unlock_reader (app->slot); return err; } @@ -442,11 +517,11 @@ app_setattr (app_t app, const char *name, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.setattr) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_reader (app); + err = lock_reader (app->slot); if (err) return err; err = app->fnc.setattr (app, name, pincb, pincb_arg, value, valuelen); - unlock_reader (app); + unlock_reader (app->slot); return err; } @@ -468,14 +543,14 @@ app_sign (app_t app, const char *keyidstr, int hashalgo, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.sign) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_reader (app); + err = lock_reader (app->slot); if (err) return err; err = app->fnc.sign (app, keyidstr, hashalgo, pincb, pincb_arg, indata, indatalen, outdata, outdatalen); - unlock_reader (app); + unlock_reader (app->slot); if (opt.verbose) log_info ("operation sign result: %s\n", gpg_strerror (err)); return err; @@ -500,14 +575,14 @@ app_auth (app_t app, const char *keyidstr, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.auth) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_reader (app); + err = lock_reader (app->slot); if (err) return err; err = app->fnc.auth (app, keyidstr, pincb, pincb_arg, indata, indatalen, outdata, outdatalen); - unlock_reader (app); + unlock_reader (app->slot); if (opt.verbose) log_info ("operation auth result: %s\n", gpg_strerror (err)); return err; @@ -532,14 +607,14 @@ app_decipher (app_t app, const char *keyidstr, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.decipher) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_reader (app); + err = lock_reader (app->slot); if (err) return err; err = app->fnc.decipher (app, keyidstr, pincb, pincb_arg, indata, indatalen, outdata, outdatalen); - unlock_reader (app); + unlock_reader (app->slot); if (opt.verbose) log_info ("operation decipher result: %s\n", gpg_strerror (err)); return err; @@ -562,12 +637,12 @@ app_writekey (app_t app, ctrl_t ctrl, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.writekey) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_reader (app); + err = lock_reader (app->slot); if (err) return err; err = app->fnc.writekey (app, ctrl, keyidstr, flags, pincb, pincb_arg, keydata, keydatalen); - unlock_reader (app); + unlock_reader (app->slot); if (opt.verbose) log_info ("operation writekey result: %s\n", gpg_strerror (err)); return err; @@ -589,11 +664,11 @@ app_genkey (app_t app, CTRL ctrl, const char *keynostr, unsigned int flags, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.genkey) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_reader (app); + err = lock_reader (app->slot); if (err) return err; err = app->fnc.genkey (app, ctrl, keynostr, flags, pincb, pincb_arg); - unlock_reader (app); + unlock_reader (app->slot); if (opt.verbose) log_info ("operation genkey result: %s\n", gpg_strerror (err)); return err; @@ -612,11 +687,11 @@ app_get_challenge (app_t app, size_t nbytes, unsigned char *buffer) return gpg_error (GPG_ERR_INV_VALUE); if (!app->initialized) return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - err = lock_reader (app); + err = lock_reader (app->slot); if (err) return err; err = iso7816_get_challenge (app->slot, nbytes, buffer); - unlock_reader (app); + unlock_reader (app->slot); return err; } @@ -636,12 +711,12 @@ app_change_pin (app_t app, CTRL ctrl, const char *chvnostr, int reset_mode, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.change_pin) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_reader (app); + err = lock_reader (app->slot); if (err) return err; err = app->fnc.change_pin (app, ctrl, chvnostr, reset_mode, pincb, pincb_arg); - unlock_reader (app); + unlock_reader (app->slot); if (opt.verbose) log_info ("operation change_pin result: %s\n", gpg_strerror (err)); return err; @@ -664,11 +739,11 @@ app_check_pin (app_t app, const char *keyidstr, return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!app->fnc.check_pin) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = lock_reader (app); + err = lock_reader (app->slot); if (err) return err; err = app->fnc.check_pin (app, keyidstr, pincb, pincb_arg); - unlock_reader (app); + unlock_reader (app->slot); if (opt.verbose) log_info ("operation check_pin result: %s\n", gpg_strerror (err)); return err; diff --git a/scd/command.c b/scd/command.c index 738b1f003..287f8c921 100644 --- a/scd/command.c +++ b/scd/command.c @@ -253,12 +253,12 @@ open_card (ctrl_t ctrl, const char *apptype) if (ctrl->server_local->card_removed) return map_to_assuan_status (gpg_error (GPG_ERR_CARD_REMOVED)); - if (ctrl->app_ctx) - return 0; /* Already initialized for one specific application. */ - if ( IS_LOCKED (ctrl) ) return gpg_error (GPG_ERR_LOCKED); + if (ctrl->app_ctx) + return 0; /* Already initialized for one specific application. */ + if (ctrl->reader_slot != -1) slot = ctrl->reader_slot; else diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 341719b1e..5b5e09176 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -809,6 +809,7 @@ handle_signal (int signo) case SIGUSR1: log_info ("SIGUSR1 received - printing internal information:\n"); pth_ctrl (PTH_CTRL_DUMPSTATE, log_get_stream ()); + app_dump_state (); break; case SIGUSR2: @@ -1013,7 +1014,6 @@ handle_connections (int listen_fd) tattr = pth_attr_new(); pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0); pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 512*1024); - pth_attr_set (tattr, PTH_ATTR_NAME, "scd-connections"); #ifndef HAVE_W32_SYSTEM /* fixme */ sigemptyset (&sigs ); @@ -1022,6 +1022,7 @@ handle_connections (int listen_fd) sigaddset (&sigs, SIGUSR2); sigaddset (&sigs, SIGINT); sigaddset (&sigs, SIGTERM); + pth_sigmask (SIG_UNBLOCK, &sigs, NULL); ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo); #else ev = NULL; @@ -1034,6 +1035,8 @@ handle_connections (int listen_fd) for (;;) { + sigset_t oldsigs; + if (shutdown_pending) { if (pth_ctrl (PTH_CTRL_GETTHREADS) == 1) @@ -1093,6 +1096,11 @@ handle_connections (int listen_fd) handle_tick (); } + /* We now might create new threads and because we don't want any + signals - we are handling here - to be delivered to a new + thread. Thus we need to block those signals. */ + pth_sigmask (SIG_BLOCK, &sigs, &oldsigs); + if (listen_fd != -1 && FD_ISSET (listen_fd, &read_fdset)) { plen = sizeof paddr; @@ -1101,15 +1109,26 @@ handle_connections (int listen_fd) { log_error ("accept failed: %s\n", strerror (errno)); } - else if (!pth_spawn (tattr, start_connection_thread, (void*)fd)) - { - log_error ("error spawning connection handler: %s\n", - strerror (errno) ); - close (fd); - } + else + { + char threadname[50]; + snprintf (threadname, sizeof threadname-1, "conn fd=%d", fd); + threadname[sizeof threadname -1] = 0; + pth_attr_set (tattr, PTH_ATTR_NAME, threadname); + + if (!pth_spawn (tattr, start_connection_thread, (void*)fd)) + { + log_error ("error spawning connection handler: %s\n", + strerror (errno) ); + close (fd); + } + } fd = -1; } + /* Restore the signal mask. */ + pth_sigmask (SIG_SETMASK, &oldsigs, NULL); + } pth_event_free (ev, PTH_FREE_ALL); diff --git a/scd/scdaemon.h b/scd/scdaemon.h index eaa9abd35..54566b6ad 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -77,19 +77,28 @@ struct { #define DBG_CARD_IO (opt.debug & DBG_CARD_IO_VALUE) struct server_local_s; -struct card_ctx_s; struct app_ctx_s; -struct server_control_s { +struct server_control_s +{ + /* Local data of the server; used only in command.c. */ struct server_local_s *server_local; - int reader_slot; /* Slot of the open reader or -1 if not open. */ - struct card_ctx_s *card_ctx; + + /* Slot of the open reader or -1 if not open. */ + int reader_slot; + + /* The application context used with this connection or NULL if none + associated. Note that this is shared with the other connections: + All connections accessing the same reader are using the same + application context. */ struct app_ctx_s *app_ctx; - struct { + + /* Helper to store the value we are going to sign */ + struct + { unsigned char *value; int valuelen; - } in_data; /* helper to store the value we are going to sign */ - + } in_data; }; typedef struct server_control_s *CTRL; -- cgit From 33701641829798ddd7fced64cf9a504cc5f48cc1 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 7 Jun 2005 19:09:18 +0000 Subject: New debugging optionhs, updates to the manual. --- NEWS | 5 +- agent/ChangeLog | 21 ++ agent/agent.h | 49 +++-- agent/cache.c | 36 +++- agent/call-scd.c | 30 +-- agent/command-ssh.c | 5 +- agent/command.c | 21 +- agent/findkey.c | 24 +-- agent/gpg-agent.c | 6 + agent/pkdecrypt.c | 3 +- agent/pksign.c | 18 +- agent/query.c | 30 ++- common/sysutils.c | 2 +- doc/ChangeLog | 4 + doc/Makefile.am | 24 ++- doc/debugging.texi | 44 +++++ doc/gnupg-card-architecture.fig | 419 ++++++++++++++++++++++++++++++++++++++++ doc/gnupg.texi | 19 +- doc/gpg-agent.texi | 47 ++++- doc/scdaemon.texi | 8 + scd/ChangeLog | 4 + scd/command.c | 3 +- scd/scdaemon.c | 18 ++ 23 files changed, 759 insertions(+), 81 deletions(-) create mode 100644 doc/gnupg-card-architecture.fig (limited to 'scd/scdaemon.c') diff --git a/NEWS b/NEWS index e28f1284a..daa18c4c0 100644 --- a/NEWS +++ b/NEWS @@ -13,9 +13,12 @@ Noteworthy changes in version 1.9.17 does allows only signing using TCOS cards but we are going to enhance it to match all the old capabilities. - * [gpg-agent] New option --rite-env-file and Assuan command + * [gpg-agent] New option --write-env-file and Assuan command UPDATESTARTUPTTY. + * [gpg-agent] New option --default-cache-ttl-ssh to set the TTL for + SSH passphrase caching independent from the other passphrases. + Noteworthy changes in version 1.9.16 (2005-04-21) ------------------------------------------------- diff --git a/agent/ChangeLog b/agent/ChangeLog index 9621e5de0..1a157fa52 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,24 @@ +2005-06-06 Werner Koch + + * gpg-agent.c: New option --default-cache-ttl-ssh. + * agent.h (cache_mode_t): New. + * pksign.c (agent_pksign_do): New arg CACHE_MODE to replace the + ARG IGNORE_CACHE. Changed all callers. + (agent_pksign): Ditto. + * findkey.c (agent_key_from_file): Ditto. Canged all callers. + (unprotect): Ditto. + * command-ssh.c (data_sign): Use CACHE_MODE_SSH. + * cache.c (agent_get_cache): New arg CACHE_MODE. + (agent_put_cache): Ditto. Store it in the cache. + + * query.c (agent_query_dump_state, dump_mutex_state): New. + (unlock_pinentry): Reset the global context before releasing the + mutex. + * gpg-agent.c (handle_signal): Dump query.c info on SIGUSR1. + + * call-scd.c (agent_scd_check_aliveness): Always do a waitpid and + add a timeout to the locking. + 2005-06-03 Werner Koch * command.c (cmd_updatestartuptty): New. diff --git a/agent/agent.h b/agent/agent.h index 51e66abee..350e5c0d2 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -69,9 +69,13 @@ struct { smartcard tasks. */ int disable_scdaemon; /* Never use the SCdaemon. */ int no_grab; /* Don't let the pinentry grab the keyboard */ - unsigned long def_cache_ttl; + + /* The default and maximum TTL of cache entries. */ + unsigned long def_cache_ttl; /* Normal. */ + unsigned long def_cache_ttl_ssh; /* SSH. */ unsigned long max_cache_ttl; + int running_detached; /* We are running detached from the tty. */ int ignore_cache_for_signing; @@ -147,12 +151,26 @@ struct pin_entry_info_s { }; -enum { - PRIVATE_KEY_UNKNOWN = 0, - PRIVATE_KEY_CLEAR = 1, - PRIVATE_KEY_PROTECTED = 2, - PRIVATE_KEY_SHADOWED = 3 -}; +enum + { + PRIVATE_KEY_UNKNOWN = 0, + PRIVATE_KEY_CLEAR = 1, + PRIVATE_KEY_PROTECTED = 2, + PRIVATE_KEY_SHADOWED = 3 + }; + + +/* Values for the cache_mode arguments. */ +typedef enum + { + CACHE_MODE_IGNORE = 0, /* Special mode to by pass the cache. */ + CACHE_MODE_ANY, /* Any mode except ignore matches. */ + CACHE_MODE_NORMAL, /* Normal cache (gpg-agent). */ + CACHE_MODE_USER, /* GET_PASSPHRASE related cache. */ + CACHE_MODE_SSH /* SSH related cache. */ + } +cache_mode_t; + /*-- gpg-agent.c --*/ void agent_exit (int rc) JNLIB_GCC_A_NR; /* Also implemented in other tools */ @@ -171,7 +189,8 @@ gpg_error_t agent_key_from_file (ctrl_t ctrl, const char *desc_text, const unsigned char *grip, unsigned char **shadow_info, - int ignore_cache, gcry_sexp_t *result); + cache_mode_t cache_mode, + gcry_sexp_t *result); gpg_error_t agent_public_key_from_file (ctrl_t ctrl, const unsigned char *grip, gcry_sexp_t *result); @@ -179,6 +198,7 @@ int agent_key_available (const unsigned char *grip); /*-- query.c --*/ void initialize_module_query (void); +void agent_query_dump_state (void); int agent_askpin (ctrl_t ctrl, const char *desc_text, const char *prompt_text, const char *inital_errtext, @@ -191,16 +211,19 @@ int agent_get_confirmation (ctrl_t ctrl, const char *desc, const char *ok, /*-- cache.c --*/ void agent_flush_cache (void); -int agent_put_cache (const char *key, const char *data, int ttl); -const char *agent_get_cache (const char *key, void **cache_id); +int agent_put_cache (const char *key, cache_mode_t cache_mode, + const char *data, int ttl); +const char *agent_get_cache (const char *key, cache_mode_t cache_mode, + void **cache_id); void agent_unlock_cache_entry (void **cache_id); /*-- pksign.c --*/ -int agent_pksign_do (CTRL ctrl, const char *desc_text, - gcry_sexp_t *signature_sexp, int ignore_cache); +int agent_pksign_do (ctrl_t ctrl, const char *desc_text, + gcry_sexp_t *signature_sexp, + cache_mode_t cache_mode); int agent_pksign (ctrl_t ctrl, const char *desc_text, - membuf_t *outbuf, int ignore_cache); + membuf_t *outbuf, cache_mode_t cache_mode); /*-- pkdecrypt.c --*/ int agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, diff --git a/agent/cache.c b/agent/cache.c index 18aa7653b..a032b4fa7 100644 --- a/agent/cache.c +++ b/agent/cache.c @@ -42,6 +42,7 @@ struct cache_item_s { int ttl; /* max. lifetime given in seconds, -1 one means infinite */ int lockcount; struct secret_data_s *pw; + cache_mode_t cache_mode; char key[1]; }; @@ -78,6 +79,7 @@ new_data (const void *data, size_t length) } + /* check whether there are items to expire */ static void housekeeping (void) @@ -85,7 +87,7 @@ housekeeping (void) ITEM r, rprev; time_t current = gnupg_get_time (); - /* first expire the actual data */ + /* First expire the actual data */ for (r=thecache; r; r = r->next) { if (!r->lockcount && r->pw @@ -100,7 +102,7 @@ housekeeping (void) } } - /* second, make sure that we also remove them based on the created stamp so + /* Second, make sure that we also remove them based on the created stamp so that the user has to enter it from time to time. We do this every hour */ for (r=thecache; r; r = r->next) { @@ -115,7 +117,7 @@ housekeeping (void) } } - /* third, make sure that we don't have too many items in the list. + /* Third, make sure that we don't have too many items in the list. Expire old and unused entries after 30 minutes */ for (rprev=NULL, r=thecache; r; ) { @@ -186,19 +188,27 @@ agent_flush_cache (void) with a maximum lifetime of TTL seconds. If there is already data under this key, it will be replaced. Using a DATA of NULL deletes the entry. A TTL of 0 is replaced by the default TTL and a TTL of - -1 set infinite timeout. */ + -1 set infinite timeout. CACHE_MODE is stored with the cache entry + and used t select different timeouts. */ int -agent_put_cache (const char *key, const char *data, int ttl) +agent_put_cache (const char *key, cache_mode_t cache_mode, + const char *data, int ttl) { ITEM r; if (DBG_CACHE) - log_debug ("agent_put_cache `%s' requested ttl=%d\n", key, ttl); + log_debug ("agent_put_cache `%s' requested ttl=%d mode=%d\n", + key, ttl, cache_mode); housekeeping (); if (!ttl) - ttl = opt.def_cache_ttl; - if (!ttl) + { + if (cache_mode == CACHE_MODE_SSH) + ttl = opt.def_cache_ttl_ssh; + else + ttl = opt.def_cache_ttl; + } + if (!ttl || cache_mode == CACHE_MODE_IGNORE) return 0; for (r=thecache; r; r = r->next) @@ -217,6 +227,7 @@ agent_put_cache (const char *key, const char *data, int ttl) { r->created = r->accessed = gnupg_get_time (); r->ttl = ttl; + r->cache_mode = cache_mode; r->pw = new_data (data, strlen (data)+1); if (!r->pw) log_error ("out of core while allocating new cache item\n"); @@ -232,6 +243,7 @@ agent_put_cache (const char *key, const char *data, int ttl) strcpy (r->key, key); r->created = r->accessed = gnupg_get_time (); r->ttl = ttl; + r->cache_mode = cache_mode; r->pw = new_data (data, strlen (data)+1); if (!r->pw) { @@ -249,12 +261,16 @@ agent_put_cache (const char *key, const char *data, int ttl) } -/* Try to find an item in the cache */ +/* Try to find an item in the cache. Note that we currently don't + make use of CACHE_MODE. */ const char * -agent_get_cache (const char *key, void **cache_id) +agent_get_cache (const char *key, cache_mode_t cache_mode, void **cache_id) { ITEM r; + if (cache_mode == CACHE_MODE_IGNORE) + return NULL; + if (DBG_CACHE) log_debug ("agent_get_cache `%s'...\n", key); housekeeping (); diff --git a/agent/call-scd.c b/agent/call-scd.c index 00c9df2a7..4dff8e3c1 100644 --- a/agent/call-scd.c +++ b/agent/call-scd.c @@ -372,25 +372,33 @@ start_scd (ctrl_t ctrl) void agent_scd_check_aliveness (void) { + pth_event_t evt; pid_t pid; int rc; - /* We can do so only if there is no more active primary connection. - With an active primary connection, this is all no problem because - with the end of gpg-agent's session a disconnect is send and the - this function will be used at a later time. */ - if (!primary_scd_ctx || !primary_scd_ctx_reusable) - return; + if (!primary_scd_ctx) + return; /* No scdaemon running. */ - if (!pth_mutex_acquire (&start_scd_lock, 0, NULL)) + /* This is not a critical function so we use a short timeout while + acquiring the lock. */ + evt = pth_event (PTH_EVENT_TIME, pth_timeout (1, 0)); + if (!pth_mutex_acquire (&start_scd_lock, 0, evt)) { - log_error ("failed to acquire the start_scd lock while" - " doing an aliveness check: %s\n", - strerror (errno)); + if (pth_event_occurred (evt)) + { + if (opt.verbose > 1) + log_info ("failed to acquire the start_scd lock while" + " doing an aliveness check: %s\n", "timeout"); + } + else + log_error ("failed to acquire the start_scd lock while" + " doing an aliveness check: %s\n", strerror (errno)); + pth_event_free (evt, PTH_FREE_THIS); return; } + pth_event_free (evt, PTH_FREE_THIS); - if (primary_scd_ctx && primary_scd_ctx_reusable) + if (primary_scd_ctx) { pid = assuan_get_pid (primary_scd_ctx); if (pid != (pid_t)(-1) && pid diff --git a/agent/command-ssh.c b/agent/command-ssh.c index 030cc70a0..870afe059 100644 --- a/agent/command-ssh.c +++ b/agent/command-ssh.c @@ -2014,7 +2014,8 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder, ctrl->use_auth_call = 1; err = agent_pksign_do (ctrl, _("Please enter the passphrase " - "for the ssh key%0A %c"), &signature_sexp, 0); + "for the ssh key%0A %c"), &signature_sexp, + CACHE_MODE_SSH); ctrl->use_auth_call = 0; if (err) goto out; @@ -2386,7 +2387,7 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl) for (i = 0; i < 20; i++) sprintf (key_grip + 2 * i, "%02X", key_grip_raw[i]); - err = agent_put_cache (key_grip, pi->pin, ttl); + err = agent_put_cache (key_grip, CACHE_MODE_SSH, pi->pin, ttl); if (err) goto out; diff --git a/agent/command.c b/agent/command.c index 56167118d..ebf3a8220 100644 --- a/agent/command.c +++ b/agent/command.c @@ -404,19 +404,19 @@ static int cmd_pksign (ASSUAN_CONTEXT ctx, char *line) { int rc; - int ignore_cache = 0; + cache_mode_t cache_mode = CACHE_MODE_NORMAL; ctrl_t ctrl = assuan_get_pointer (ctx); membuf_t outbuf; - + if (opt.ignore_cache_for_signing) - ignore_cache = 1; + cache_mode = CACHE_MODE_IGNORE; else if (!ctrl->server_local->use_cache_for_signing) - ignore_cache = 1; + cache_mode = CACHE_MODE_IGNORE; init_membuf (&outbuf, 512); rc = agent_pksign (ctrl, ctrl->server_local->keydesc, - &outbuf, ignore_cache); + &outbuf, cache_mode); if (rc) clear_outbuf (&outbuf); else @@ -623,7 +623,8 @@ cmd_get_passphrase (ASSUAN_CONTEXT ctx, char *line) desc = NULL; /* Note: we store the hexified versions in the cache. */ - pw = cacheid ? agent_get_cache (cacheid, &cache_marker) : NULL; + pw = cacheid ? agent_get_cache (cacheid, CACHE_MODE_NORMAL, &cache_marker) + : NULL; if (pw) { assuan_begin_confidential (ctx); @@ -647,7 +648,7 @@ cmd_get_passphrase (ASSUAN_CONTEXT ctx, char *line) if (!rc) { if (cacheid) - agent_put_cache (cacheid, response, 0); + agent_put_cache (cacheid, CACHE_MODE_USER, response, 0); assuan_begin_confidential (ctx); rc = assuan_set_okay_line (ctx, response); xfree (response); @@ -682,7 +683,7 @@ cmd_clear_passphrase (ASSUAN_CONTEXT ctx, char *line) if (!cacheid || !*cacheid || strlen (cacheid) > 50) return set_error (Parameter_Error, "invalid length of cacheID"); - agent_put_cache (cacheid, NULL, 0); + agent_put_cache (cacheid, CACHE_MODE_USER, NULL, 0); return 0; } @@ -772,7 +773,7 @@ cmd_passwd (ASSUAN_CONTEXT ctx, char *line) Assuan error code. */ rc = agent_key_from_file (ctrl, ctrl->server_local->keydesc, - grip, &shadow_info, 1, &s_skey); + grip, &shadow_info, CACHE_MODE_IGNORE, &s_skey); if (rc) ; else if (!s_skey) @@ -842,7 +843,7 @@ cmd_preset_passphrase (ASSUAN_CONTEXT ctx, char *line) else return map_to_assuan_status (gpg_error (GPG_ERR_NOT_IMPLEMENTED)); - rc = agent_put_cache (grip_clear, passphrase, ttl); + rc = agent_put_cache (grip_clear, CACHE_MODE_ANY, passphrase, ttl); if (rc) log_error ("command preset_passwd failed: %s\n", gpg_strerror (rc)); diff --git a/agent/findkey.c b/agent/findkey.c index 999a5d620..56433c9c4 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -230,8 +230,9 @@ modify_description (const char *in, const char *comment, char **result) caching mechanism. DESC_TEXT may be set to override the default description used for the pinentry. */ static int -unprotect (CTRL ctrl, const char *desc_text, - unsigned char **keybuf, const unsigned char *grip, int ignore_cache) +unprotect (ctrl_t ctrl, const char *desc_text, + unsigned char **keybuf, const unsigned char *grip, + cache_mode_t cache_mode) { struct pin_entry_info_s *pi; struct try_unprotect_arg_s arg; @@ -246,10 +247,12 @@ unprotect (CTRL ctrl, const char *desc_text, /* First try to get it from the cache - if there is none or we can't unprotect it, we fall back to ask the user */ - if (!ignore_cache) + if (cache_mode != CACHE_MODE_IGNORE) { void *cache_marker; - const char *pw = agent_get_cache (hexgrip, &cache_marker); + const char *pw; + + pw = agent_get_cache (hexgrip, cache_mode, &cache_marker); if (pw) { rc = agent_unprotect (*keybuf, pw, &result, &resultlen); @@ -280,7 +283,7 @@ unprotect (CTRL ctrl, const char *desc_text, if (!rc) { assert (arg.unprotected_key); - agent_put_cache (hexgrip, pi->pin, 0); + agent_put_cache (hexgrip, cache_mode, pi->pin, 0); xfree (*keybuf); *keybuf = arg.unprotected_key; } @@ -360,14 +363,13 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result) /* Return the secret key as an S-Exp in RESULT after locating it using the grip. Returns NULL in RESULT if the operation should be diverted to a token; SHADOW_INFO will point then to an allocated - S-Expression with the shadow_info part from the file. With - IGNORE_CACHE passed as true the passphrase is not taken from the - cache. DESC_TEXT may be set to present a custom description for the - pinentry. */ + S-Expression with the shadow_info part from the file. CACHE_MODE + defines now the cache shall be used. DESC_TEXT may be set to + present a custom description for the pinentry. */ gpg_error_t agent_key_from_file (ctrl_t ctrl, const char *desc_text, const unsigned char *grip, unsigned char **shadow_info, - int ignore_cache, gcry_sexp_t *result) + cache_mode_t cache_mode, gcry_sexp_t *result) { int rc; unsigned char *buf; @@ -447,7 +449,7 @@ agent_key_from_file (ctrl_t ctrl, const char *desc_text, if (!rc) { - rc = unprotect (ctrl, desc_text_final, &buf, grip, ignore_cache); + rc = unprotect (ctrl, desc_text_final, &buf, grip, cache_mode); if (rc) log_error ("failed to unprotect the secret key: %s\n", gpg_strerror (rc)); diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 90b071d5e..6cc08f845 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -83,6 +83,7 @@ enum cmd_and_opt_values oLCmessages, oScdaemonProgram, oDefCacheTTL, + oDefCacheTTLSSH, oMaxCacheTTL, oUseStandardSocket, oNoUseStandardSocket, @@ -140,6 +141,7 @@ static ARGPARSE_OPTS opts[] = { { oDefCacheTTL, "default-cache-ttl", 4, N_("|N|expire cached PINs after N seconds")}, + { oDefCacheTTLSSH, "default-cache-ttl-ssh", 4, "@" }, { oMaxCacheTTL, "max-cache-ttl", 4, "@" }, { oIgnoreCacheForSigning, "ignore-cache-for-signing", 0, N_("do not use the PIN cache when signing")}, @@ -367,6 +369,7 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) opt.pinentry_program = NULL; opt.scdaemon_program = NULL; opt.def_cache_ttl = DEFAULT_CACHE_TTL; + opt.def_cache_ttl_ssh = DEFAULT_CACHE_TTL; opt.max_cache_ttl = MAX_CACHE_TTL; opt.ignore_cache_for_signing = 0; opt.allow_mark_trusted = 0; @@ -402,6 +405,7 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) case oDisableScdaemon: opt.disable_scdaemon = 1; break; case oDefCacheTTL: opt.def_cache_ttl = pargs->r.ret_ulong; break; + case oDefCacheTTLSSH: opt.def_cache_ttl_ssh = pargs->r.ret_ulong; break; case oMaxCacheTTL: opt.max_cache_ttl = pargs->r.ret_ulong; break; case oIgnoreCacheForSigning: opt.ignore_cache_for_signing = 1; break; @@ -413,6 +417,7 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) default: return 0; /* not handled */ } + return 1; /* handled */ } @@ -1339,6 +1344,7 @@ handle_signal (int signo) case SIGUSR1: log_info ("SIGUSR1 received - printing internal information:\n"); pth_ctrl (PTH_CTRL_DUMPSTATE, log_get_stream ()); + agent_query_dump_state (); agent_scd_dump_state (); break; diff --git a/agent/pkdecrypt.c b/agent/pkdecrypt.c index 7a93e58f8..42ce69697 100644 --- a/agent/pkdecrypt.c +++ b/agent/pkdecrypt.c @@ -66,7 +66,8 @@ agent_pkdecrypt (CTRL ctrl, const char *desc_text, log_printhex ("cipher: ", ciphertext, ciphertextlen); } rc = agent_key_from_file (ctrl, desc_text, - ctrl->keygrip, &shadow_info, 0, &s_skey); + ctrl->keygrip, &shadow_info, + CACHE_MODE_NORMAL, &s_skey); if (rc) { log_error ("failed to read the secret key\n"); diff --git a/agent/pksign.c b/agent/pksign.c index 3337e188c..2a355e43e 100644 --- a/agent/pksign.c +++ b/agent/pksign.c @@ -79,8 +79,8 @@ do_encode_md (const byte * md, size_t mdlen, int algo, gcry_sexp_t * r_hash, /* SIGN whatever information we have accumulated in CTRL and return the signature S-Expression. */ int -agent_pksign_do (CTRL ctrl, const char *desc_text, - gcry_sexp_t *signature_sexp, int ignore_cache) +agent_pksign_do (ctrl_t ctrl, const char *desc_text, + gcry_sexp_t *signature_sexp, cache_mode_t cache_mode) { gcry_sexp_t s_skey = NULL, s_sig = NULL; unsigned char *shadow_info = NULL; @@ -90,16 +90,16 @@ agent_pksign_do (CTRL ctrl, const char *desc_text, return gpg_error (GPG_ERR_NO_SECKEY); rc = agent_key_from_file (ctrl, desc_text, ctrl->keygrip, - &shadow_info, ignore_cache, &s_skey); + &shadow_info, cache_mode, &s_skey); if (rc) { log_error ("failed to read the secret key\n"); goto leave; } - if (! s_skey) + if (!s_skey) { - /* divert operation to the smartcard */ + /* Divert operation to the smartcard */ unsigned char *buf = NULL; size_t len = 0; @@ -128,7 +128,7 @@ agent_pksign_do (CTRL ctrl, const char *desc_text, } else { - /* no smartcard, but a private key */ + /* No smartcard, but a private key */ gcry_sexp_t s_hash = NULL; @@ -176,15 +176,15 @@ agent_pksign_do (CTRL ctrl, const char *desc_text, /* SIGN whatever information we have accumulated in CTRL and write it back to OUTFP. */ int -agent_pksign (CTRL ctrl, const char *desc_text, - membuf_t *outbuf, int ignore_cache) +agent_pksign (ctrl_t ctrl, const char *desc_text, + membuf_t *outbuf, cache_mode_t cache_mode) { gcry_sexp_t s_sig = NULL; char *buf = NULL; size_t len = 0; int rc = 0; - rc = agent_pksign_do (ctrl, desc_text, &s_sig, ignore_cache); + rc = agent_pksign_do (ctrl, desc_text, &s_sig, cache_mode); if (rc) goto leave; diff --git a/agent/query.c b/agent/query.c index d3b42a416..c1e4dbacc 100644 --- a/agent/query.c +++ b/agent/query.c @@ -49,7 +49,7 @@ #define LOCK_TIMEOUT (1*60) -static ASSUAN_CONTEXT entry_ctx = NULL; +static assuan_context_t entry_ctx = NULL; #ifdef USE_GNU_PTH static pth_mutex_t entry_lock; #endif @@ -82,6 +82,30 @@ initialize_module_query (void) +static void +dump_mutex_state (pth_mutex_t *m) +{ + if (!(m->mx_state & PTH_MUTEX_INITIALIZED)) + log_printf ("not_initialized"); + else if (!(m->mx_state & PTH_MUTEX_LOCKED)) + log_printf ("not_locked"); + else + log_printf ("locked tid=0x%lx count=%lu", (long)m->mx_owner, m->mx_count); +} + + +/* This function may be called to print infromation pertaining to the + current state of this module to the log. */ +void +agent_query_dump_state (void) +{ + log_info ("agent_query_dump_state: entry_lock="); + dump_mutex_state (&entry_lock); + log_printf ("\n"); + log_info ("agent_query_dump_state: entry_ctx=%p pid=%ld\n", + entry_ctx, (long)assuan_get_pid (entry_ctx)); +} + /* Unlock the pinentry so that another thread can start one and disconnect that pinentry - we do this after the unlock so that a @@ -90,8 +114,9 @@ initialize_module_query (void) static int unlock_pinentry (int rc) { - ASSUAN_CONTEXT ctx = entry_ctx; + assuan_context_t ctx = entry_ctx; + entry_ctx = NULL; #ifdef USE_GNU_PTH if (!pth_mutex_release (&entry_lock)) { @@ -100,7 +125,6 @@ unlock_pinentry (int rc) rc = gpg_error (GPG_ERR_INTERNAL); } #endif - entry_ctx = NULL; assuan_disconnect (ctx); return rc; } diff --git a/common/sysutils.c b/common/sysutils.c index 97fa23d95..a8f6f6f5d 100644 --- a/common/sysutils.c +++ b/common/sysutils.c @@ -105,7 +105,7 @@ enable_core_dumps (void) setrlimit (RLIMIT_CORE, &limit); return 1; /* We always return true because trhis function is merely a debugging aid. */ -#endif +# endif return 1; #endif } diff --git a/doc/ChangeLog b/doc/ChangeLog index f353bdf03..c4d263513 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,5 +1,9 @@ 2005-06-03 Werner Koch + * debugging.texi (Architecture Details): New section, mostly empty. + * gnupg-card-architecture.fig: New. + * Makefile.am: Rules to build png and eps versions. + * gpg-agent.texi (Agent UPDATESTARTUPTTY): New. 2005-05-17 Werner Koch diff --git a/doc/Makefile.am b/doc/Makefile.am index 988bbf849..fdcd62dc0 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -18,14 +18,34 @@ ## Process this file with automake to produce Makefile.in -EXTRA_DIST = gnupg-badge-openpgp.eps gnupg-badge-openpgp.jpg +EXTRA_DIST = gnupg-badge-openpgp.eps gnupg-badge-openpgp.jpg \ + gnupg-card-architecture.eps gnupg-card-architecture.png + +BUILT_SOURCES = gnupg-card-architecture.eps gnupg-card-architecture.png info_TEXINFOS = gnupg.texi + + gnupg_TEXINFOS = \ gpg.texi gpgsm.texi gpg-agent.texi scdaemon.texi assuan.texi \ tools.texi debugging.texi glossary.texi contrib.texi gpl.texi \ - sysnotes.texi + sysnotes.texi gnupg-card-architecture.fig DISTCLEANFILES = gnupg.tmp gnupg.ops + + +.fig.png: + fig2dev -L png `test -f '$<' || echo '$(srcdir)/'`$< $@ + +.fig.jpg: + fig2dev -L jpg `test -f '$<' || echo '$(srcdir)/'`$< $@ + +.fig.eps: + fig2dev -L eps `test -f '$<' || echo '$(srcdir)/'`$< $@ + +.fig.pdf: + fig2dev -L pdf `test -f '$<' || echo '$(srcdir)/'`$< $@ + + diff --git a/doc/debugging.texi b/doc/debugging.texi index 49ab70bde..429dbd407 100644 --- a/doc/debugging.texi +++ b/doc/debugging.texi @@ -18,6 +18,7 @@ solve the problem at hand. @menu * Debugging Tools:: Description of some useful tools * Common Problems:: Commonly seen problems. +* Architecture Details:: How the whole thing works internally. @end menu @@ -105,6 +106,49 @@ shell). Even for GUI based Pinentries; you should have set on how to do it. +@item SSH hangs while a popping up pinentry was expected + +SSH has no way to tell the gpg-agent what terminal or X display it is +running on. So when remotely logging into a box where a gpg-agent with +SSH support is running, the pinentry will get popped up on whatever +display t he gpg-agent has been started. To solve this problem you may +issue the command + +@smallexample +echo UPDATESTARTUPTTY | gpg-connect-agent +@end smallexample + +and the next pinentry will pop up on your display or screen. However, +you need to kill the running pinentry first because only one pinentry +may be running at once. If you plan to use ssh on a new display you +should issue the above command before invoking ssh or any other service +making use of ssh. + @end itemize + +@c ******************************************** +@c *** Architecture Details ***************** +@c ******************************************** +@node Architecture Details +@section How the whole thing works internally. + + +@menu +* gpg 1.4 vs. 1.9:: Relationship between the two branches. +@end menu + +@node gpg 1.4 vs. 1.9 +@subsection Relationship between the two branches. + +Here is a little picture showing how the components work together: + +@image{gnupg-card-architecture, 14cm} + +@noindent +Lets try to explain it: + +TO BE DONE. + + diff --git a/doc/gnupg-card-architecture.fig b/doc/gnupg-card-architecture.fig new file mode 100644 index 000000000..e5772cd0f --- /dev/null +++ b/doc/gnupg-card-architecture.fig @@ -0,0 +1,419 @@ +#FIG 3.2 Produced by xfig version 3.2.5-alpha5 +# Copyright 2005 Werner Koch +# +# 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 +Landscape +Center +Metric +A4 +100.00 +Single +-2 +1200 2 +0 32 #414541 +0 33 #808080 +0 34 #c0c0c0 +0 35 #c6b797 +0 36 #eff8ff +0 37 #dccba6 +0 38 #e0e0e0 +0 39 #8e8f8e +0 40 #aaaaaa +0 41 #555555 +0 42 #404040 +0 43 #868286 +0 44 #c7c3c7 +0 45 #e7e3e7 +0 46 #8e8e8e +0 47 #444444 +0 48 #868686 +0 49 #c7c7c7 +0 50 #666666 +0 51 #e2e2ee +0 52 #94949a +0 53 #dbdbdb +0 54 #a1a1b7 +0 55 #9c0000 +0 56 #ededed +0 57 #86acff +0 58 #7070ff +0 59 #bebebe +0 60 #515151 +0 61 #000049 +0 62 #797979 +0 63 #303430 +0 64 #c7b696 +0 65 #d7d7d7 +0 66 #aeaeae +0 67 #85807d +0 68 #d2d2d2 +0 69 #3a3a3a +0 70 #4573aa +0 71 #000000 +0 72 #e7e7e7 +0 73 #f7f7f7 +0 74 #d6d7d6 +0 75 #7b79a5 +0 76 #effbff +0 77 #9e9e9e +0 78 #717571 +0 79 #73758c +0 80 #414141 +0 81 #635dce +0 82 #565151 +0 83 #dd9d93 +0 84 #f1ece0 +0 85 #c3c3c3 +0 86 #e2c8a8 +0 87 #e1e1e1 +0 88 #da7a1a +0 89 #f1e41a +0 90 #887dc2 +0 91 #d6d6d6 +0 92 #8c8ca5 +0 93 #4a4a4a +0 94 #8c6b6b +0 95 #5a5a5a +0 96 #636363 +0 97 #b79b73 +0 98 #4193ff +0 99 #bf703b +0 100 #db7700 +0 101 #dab800 +0 102 #006400 +0 103 #5a6b3b +0 104 #d3d3d3 +0 105 #8e8ea4 +0 106 #f3b95d +0 107 #89996b +0 108 #646464 +0 109 #b7e6ff +0 110 #86c0ec +0 111 #bdbdbd +0 112 #d39552 +0 113 #98d2fe +0 114 #8c9c6b +0 115 #f76b00 +0 116 #5a6b39 +0 117 #8c9c6b +0 118 #8c9c7b +0 119 #184a18 +0 120 #adadad +0 121 #f7bd5a +0 122 #636b9c +0 123 #de0000 +0 124 #adadad +0 125 #f7bd5a +0 126 #adadad +0 127 #f7bd5a +0 128 #636b9c +0 129 #526b29 +0 130 #949494 +0 131 #006300 +0 132 #00634a +0 133 #7b844a +0 134 #e7bd7b +0 135 #a5b5c6 +0 136 #6b6b94 +0 137 #846b6b +0 138 #529c4a +0 139 #d6e7e7 +0 140 #526363 +0 141 #186b4a +0 142 #9ca5b5 +0 143 #ff9400 +0 144 #ff9400 +0 145 #00634a +0 146 #7b844a +0 147 #63737b +0 148 #e7bd7b +0 149 #184a18 +0 150 #f7bd5a +0 151 #dedede +0 152 #f3eed3 +0 153 #f5ae5d +0 154 #95ce99 +0 155 #b5157d +0 156 #eeeeee +0 157 #848484 +0 158 #7b7b7b +0 159 #005a00 +0 160 #e77373 +0 161 #ffcb31 +0 162 #29794a +0 163 #de2821 +0 164 #2159c6 +0 165 #f8f8f8 +0 166 #e6e6e6 +0 167 #21845a +0 168 #ff9408 +0 169 #007000 +0 170 #d00000 +0 171 #fed600 +0 172 #d82010 +0 173 #003484 +0 174 #d62010 +0 175 #389000 +0 176 #ba0000 +0 177 #003380 +0 178 #00a7bd +0 179 #ffc500 +0 180 #087bd0 +0 181 #fbc100 +0 182 #840029 +0 183 #07399c +0 184 #0063bd +0 185 #39acdf +0 186 #42c0e0 +0 187 #31ceff +0 188 #ffde00 +0 189 #085a00 +0 190 #ff2100 +0 191 #f75e08 +0 192 #ef7b08 +0 193 #ff8200 +0 194 #007d00 +0 195 #0000be +0 196 #757575 +0 197 #f3f3f3 +0 198 #d7d3d7 +0 199 #aeaaae +0 200 #c2c2c2 +0 201 #303030 +0 202 #515551 +0 203 #f7f3f7 +0 204 #717171 +6 9270 1980 13230 6570 +6 9471 3906 13014 5677 +2 4 0 1 0 7 50 -1 -1 0.000 0 0 5 0 0 5 + 10540 4394 10540 3936 9471 3936 9471 4394 10540 4394 +2 4 0 1 0 7 50 -1 -1 0.000 0 0 5 0 0 5 + 10387 5616 10387 5158 9471 5158 9471 5616 10387 5616 +2 4 0 1 0 7 50 -1 -1 0.000 0 0 5 0 0 5 + 12984 5005 12984 4547 9471 4547 9471 5005 12984 5005 +2 4 0 1 0 7 50 -1 -1 0.000 0 0 5 0 0 5 + 12984 5616 12984 5158 12067 5158 12067 5616 12984 5616 +2 4 0 1 0 7 50 -1 -1 0.000 0 0 5 0 0 5 + 11701 5627 11701 5168 10784 5168 10784 5627 11701 5627 +4 0 0 50 -1 16 11 0.0000 4 173 835 9623 4242 OpenPGP\001 +4 0 0 50 -1 16 11 0.0000 4 132 2770 9776 4853 APDU and ISO-7816 access code\001 +4 0 0 50 -1 16 11 0.0000 4 132 448 9623 5464 CCID\001 +4 0 0 50 -1 16 11 0.0000 4 132 601 12220 5464 CT-API\001 +4 0 0 50 -1 16 11 0.0000 4 132 560 10957 5464 PC/SC\001 +-6 +6 10693 3906 13014 4394 +2 4 0 1 0 7 50 -1 -1 0.000 0 0 5 0 0 5 + 11762 4394 11762 3936 10693 3936 10693 4394 11762 4394 +2 4 0 1 0 7 50 -1 -1 0.000 0 0 5 0 0 5 + 12984 4394 12984 3936 11915 3936 11915 4394 12984 4394 +4 0 0 50 -1 16 11 0.0000 4 132 377 10998 4242 NKS\001 +4 0 0 50 -1 16 11 0.0000 4 132 804 12067 4242 PKCS#15\001 +-6 +2 4 0 2 0 6 60 -1 20 0.000 0 0 5 0 0 5 + 13137 2072 9318 2072 9318 5739 13137 5739 13137 2072 +2 1 2 1 0 7 50 -1 -1 3.000 0 0 -1 0 0 2 + 9318 3753 13137 3753 +2 4 0 2 0 6 60 -1 20 0.000 0 0 5 0 0 5 + 11691 6360 10774 6360 10774 5901 11691 5901 11691 6360 +2 1 2 2 0 7 50 -1 -1 4.500 0 0 -1 0 0 1 + 11762 5739 +2 1 1 2 0 7 50 -1 -1 6.000 0 0 -1 0 0 4 + 10693 5739 10693 6502 11762 6502 11762 5739 +4 0 0 50 -1 18 15 0.0000 4 183 1293 10540 2989 SCDaemon\001 +4 0 0 50 -1 16 11 0.0000 4 133 662 10896 6176 wrapper\001 +-6 +6 90 1980 4050 5760 +6 306 3906 3849 5677 +2 4 0 1 0 7 50 -1 -1 0.000 0 0 5 0 0 5 + 1375 4394 1375 3936 306 3936 306 4394 1375 4394 +2 4 0 1 0 7 50 -1 -1 0.000 0 0 5 0 0 5 + 1222 5616 1222 5158 306 5158 306 5616 1222 5616 +2 4 0 1 0 7 50 -1 -1 0.000 0 0 5 0 0 5 + 3819 5005 3819 4547 306 4547 306 5005 3819 5005 +2 4 0 1 0 7 50 -1 -1 0.000 0 0 5 0 0 5 + 3819 5616 3819 5158 2902 5158 2902 5616 3819 5616 +2 4 0 1 0 7 50 -1 -1 0.000 0 0 5 0 0 5 + 2536 5627 2536 5168 1619 5168 1619 5627 2536 5627 +4 0 0 50 -1 16 11 0.0000 4 173 835 458 4242 OpenPGP\001 +4 0 0 50 -1 16 11 0.0000 4 132 2770 611 4853 APDU and ISO-7816 access code\001 +4 0 0 50 -1 16 11 0.0000 4 132 448 458 5464 CCID\001 +4 0 0 50 -1 16 11 0.0000 4 132 601 3055 5464 CT-API\001 +4 0 0 50 -1 16 11 0.0000 4 132 560 1792 5464 PC/SC\001 +-6 +6 2139 3753 3208 4211 +2 4 0 1 0 7 50 -1 -1 4.000 0 0 5 0 0 5 + 3208 4211 3208 3753 2139 3753 2139 4211 3208 4211 +4 0 0 50 -1 16 11 0.0000 4 132 784 2291 4058 Gluecode\001 +-6 +2 1 2 2 0 7 50 -1 -1 4.500 0 0 -1 0 0 1 + 2597 5739 +2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 2 + 1 1 1.00 40.73 81.47 + 2139 4028 1405 4150 +2 1 2 1 0 7 50 -1 -1 3.000 0 0 -1 0 0 4 + 153 3753 1833 3753 1833 4364 3972 4364 +2 4 0 2 0 6 60 -1 20 0.000 0 0 5 0 0 5 + 3972 2072 153 2072 153 5739 3972 5739 3972 2072 +4 0 0 50 -1 18 15 0.0000 4 224 866 1375 2989 gpg 1.4\001 +-6 +6 4888 4058 5346 5433 +2 4 0 1 0 7 50 -1 -1 4.000 0 0 5 0 0 5 + 5346 5433 5346 4058 4888 4058 4888 5433 5346 5433 +4 0 0 50 -1 16 11 1.5708 4 132 611 5194 5128 Assuan\001 +-6 +6 4680 1980 8640 5760 +2 4 0 1 0 7 50 -1 -1 4.000 0 0 5 0 0 5 + 5346 3753 5346 2378 4888 2378 4888 3753 5346 3753 +2 4 0 2 0 6 60 -1 20 0.000 0 0 5 0 0 5 + 8554 5739 4735 5739 4735 2072 8554 2072 8554 5739 +4 0 0 50 -1 16 11 1.5708 4 173 804 5194 3447 ssh-agent\001 +-6 +6 5805 3447 7332 4975 +6 5957 3447 7179 4211 +2 4 0 1 0 7 50 -1 -1 4.000 0 0 5 0 0 5 + 7179 4211 7179 3447 5957 3447 5957 4211 7179 4211 +4 0 0 50 -1 16 11 0.0000 4 173 937 6110 3753 Private Key\001 +4 0 0 50 -1 16 11 0.0000 4 173 896 6110 4058 Operations\001 +-6 +2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 1 + 7195 4883 +2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 1 + 7195 4883 +2 4 0 1 0 7 50 -1 -1 4.000 0 0 5 0 0 5 + 7332 4975 7332 4517 6721 4517 6721 4975 7332 4975 +2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 1 1 2 + 1 1 1.00 40.73 81.47 + 1 1 1.00 40.73 81.47 + 6568 4211 7027 4517 +2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 1 1 2 + 1 1 1.00 40.73 81.47 + 1 1 1.00 40.73 81.47 + 6568 4211 6110 4517 +2 4 0 1 0 7 50 -1 -1 4.000 0 0 5 0 0 5 + 6416 4975 6416 4517 5805 4517 5805 4975 6416 4975 +4 0 0 50 -1 16 11 0.0000 4 132 397 6874 4822 Card\001 +4 0 0 50 -1 16 11 0.0000 4 132 356 5957 4822 Disk\001 +-6 +6 7638 3600 8401 4058 +2 4 0 1 0 7 50 -1 -1 4.000 0 0 5 0 0 5 + 8401 4058 8401 3600 7638 3600 7638 4058 8401 4058 +2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 1 + 7638 3814 +4 0 0 50 -1 16 11 0.0000 4 132 530 7790 3905 Cache\001 +-6 +6 9471 2225 9929 3600 +2 4 0 1 0 7 50 -1 -1 4.000 0 0 5 0 0 5 + 9929 3600 9929 2225 9471 2225 9471 3600 9929 3600 +4 0 0 50 -1 16 11 1.5708 4 132 611 9776 3294 Assuan\001 +-6 +6 6480 360 8640 1440 +2 4 0 2 0 6 60 -1 20 0.000 0 0 5 0 0 5 + 8554 1339 6568 1339 6568 423 8554 423 8554 1339 +4 0 0 50 -1 18 15 0.0000 4 234 967 7027 881 pinentry\001 +4 0 0 50 -1 16 10 0.0000 4 153 1375 6874 1187 (GTK+, Qt, Curses)\001 +-6 +6 10570 270 13137 1003 +2 1 1 1 1 2 50 -1 -1 4.000 0 0 -1 1 0 2 + 1 1 1.00 40.73 81.47 + 10632 331 11181 331 +2 1 0 2 1 2 50 -1 -1 6.000 0 0 -1 1 0 2 + 1 1 2.00 81.47 162.94 + 10632 637 11181 637 +2 1 0 1 0 2 50 -1 -1 4.000 0 0 -1 1 0 2 + 1 1 1.00 40.73 81.47 + 10632 942 11181 942 +4 0 0 50 -1 16 10 0.0000 4 163 1762 11365 392 Alternative access paths\001 +4 0 0 50 -1 16 10 0.0000 4 163 1426 11365 698 IPC (pipe or socket)\001 +4 0 0 50 -1 16 10 0.0000 4 122 1232 11365 1003 Internal data flow\001 +-6 +# Smartcard ID-1 +6 6840 6120 8550 7200 +6 7069 6526 7307 6746 +2 1 0 1 0 7 48 -1 -1 0.000 0 0 -1 0 0 2 + 7234 6691 7307 6691 +2 1 0 1 0 0 48 -1 20 0.000 0 0 -1 0 0 2 + 7069 6636 7143 6636 +2 1 0 1 0 7 48 -1 -1 0.000 0 0 -1 0 0 2 + 7069 6581 7143 6581 +2 1 0 1 0 7 48 -1 -1 0.000 0 0 -1 0 0 2 + 7069 6691 7143 6691 +2 1 0 1 0 7 48 -1 -1 0.000 0 0 -1 0 0 2 + 7143 6526 7143 6746 +2 1 0 1 0 7 48 -1 -1 0.000 0 0 -1 0 0 3 + 7307 6581 7234 6581 7234 6746 +2 1 0 1 0 7 48 -1 -1 0.000 0 0 -1 0 0 2 + 7234 6636 7307 6636 +2 4 0 1 0 31 49 -1 20 0.000 0 0 1 0 0 5 + 7069 6526 7307 6526 7307 6746 7069 6746 7069 6526 +-6 +2 4 0 1 -1 7 50 -1 20 0.000 0 0 1 0 0 5 + 8472 7185 6904 7185 6904 6197 8472 6197 8472 7185 +-6 +2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 2 + 1 1 1.00 40.73 81.47 + 5346 3142 5957 3753 +2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 2 + 1 1 1.00 40.73 81.47 + 5346 4669 5957 3905 +2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 1 1 2 + 1 1 1.00 40.73 81.47 + 1 1 1.00 40.73 81.47 + 7179 3814 7638 3814 +2 4 0 2 0 6 60 -1 20 0.000 0 0 5 0 0 5 + 11731 7480 10693 7480 10693 6991 11731 6991 11731 7480 +3 2 0 2 1 2 50 -1 -1 6.000 0 1 0 3 + 1 1 2.00 81.47 162.94 + 8022 3600 8096 2225 7513 1360 + 0.000 -1.000 0.000 +3 2 0 2 1 2 50 -1 -1 0.000 0 1 0 3 + 0 0 2.00 81.47 162.94 + 7332 4730 8737 4486 9471 2897 + 0.000 -1.000 0.000 +3 2 0 2 1 2 50 -1 -1 6.000 0 1 0 3 + 1 1 2.00 81.47 162.94 + 3238 3997 4216 4242 4888 4730 + 0.000 -1.000 0.000 +3 2 0 2 1 2 50 -1 -1 6.000 0 1 0 3 + 1 1 2.00 81.47 162.94 + 11243 6502 11304 6747 11181 6991 + 0.000 -1.000 0.000 +3 2 1 1 1 2 50 -1 -1 4.000 0 1 0 3 + 1 1 1.00 40.73 81.47 + 10693 7235 9471 7174 8493 6869 + 0.000 -1.000 0.000 +3 2 1 1 1 2 50 -1 -1 4.000 0 1 0 3 + 1 1 1.00 40.73 81.47 + 9898 5647 9532 6380 8493 6563 + 0.000 -1.000 0.000 +3 2 1 1 1 2 50 -1 -1 4.000 0 1 0 3 + 1 1 1.00 40.73 81.47 + 12465 5647 11731 6624 8493 6747 + 0.000 -1.000 0.000 +3 2 1 1 1 2 50 -1 -1 4.000 0 1 0 3 + 1 1 1.00 40.73 81.47 + 2077 5647 3177 6502 6843 6624 + 0.000 -1.000 0.000 +3 2 1 1 1 2 50 -1 -1 4.000 0 1 0 3 + 1 1 1.00 40.73 81.47 + 733 5647 2444 6808 6843 6747 + 0.000 -1.000 0.000 +3 2 1 1 1 2 50 -1 -1 4.000 0 1 0 3 + 1 1 1.00 40.73 81.47 + 3361 5647 4155 6319 6843 6502 + 0.000 -1.000 0.000 +4 0 0 50 -1 18 15 0.0000 4 214 1191 5957 2989 gpg-agent\001 +4 0 0 50 -1 16 11 0.0000 4 173 387 10998 7297 pcsd\001 diff --git a/doc/gnupg.texi b/doc/gnupg.texi index 4c30980b3..d92f01cd9 100644 --- a/doc/gnupg.texi +++ b/doc/gnupg.texi @@ -86,14 +86,15 @@ section entitled ``Copying''. @insertcopying @end titlepage - +@ifnothtml @summarycontents @contents @page +@end ifnothtml @ifnottex @node Top -@top The GNU Privacy Guard +@top @insertcopying This manual documents how to use the GNU Privacy Guard system as well as @@ -120,6 +121,20 @@ the administration and the architecture. * Index:: Index of concepts and symbol names. @end menu +@ifhtml + +@center @image{gnupg-badge-openpgp,6cm,,The GnuPG Logo} + +@end ifhtml + + +@ifhtml +@page +@summarycontents +@contents +@end ifhtml + + @include gpg.texi @include gpgsm.texi @include gpg-agent.texi diff --git a/doc/gpg-agent.texi b/doc/gpg-agent.texi index 5e8c19468..bad6639e2 100644 --- a/doc/gpg-agent.texi +++ b/doc/gpg-agent.texi @@ -42,7 +42,8 @@ else fi @end smallexample -The new option @option{--write-env-file} may be used instead. +@noindent +Note that the new option @option{--write-env-file} may be used instead. @noindent @@ -289,6 +290,11 @@ control this behaviour but this command line option takes precedence. Set the time a cache entry is valid to @var{n} seconds. The default are 600 seconds. +@item --default-cache-ttl-ssh @var{n} +@opindex default-cache-ttl +Set the time a cache entry used for SSH keys is valid to @var{n} +seconds. The default are 600 seconds. + @item --max-cache-ttl @var{n} @opindex max-cache-ttl Set the maximum time a cache entry is valid to @var{n} seconds. After @@ -506,12 +512,13 @@ are still pending, a shutdown is forced. @cpindex SIGINT Shuts down the process immediately. - @item SIGUSR1 -@itemx SIGUSR2 @cpindex SIGUSR1 +Dump internal information to the log file. + +@item SIGUSR2 @cpindex SIGUSR2 -These signals are used for internal purposes. +This signal is used for internal purposes. @end table @@ -523,12 +530,44 @@ These signals are used for internal purposes. @c man begin EXAMPLES +The usual way to invoke @command{gpg-agent} is + @example $ eval `gpg-agent --daemon` @end example @c man end +An alternative way is by replacing @command{ssh-agent} with +@command{gpg-agent}. If for example @command{ssh-agent} is started as +part of the Xsession intialization you may simply replace +@command{ssh-agent} by a script like: + +@cartouche +@example +#!/bin/sh + +exec /usr/local/bin/gpg-agent --enable-ssh-support --daemon \ + --write-env-file $@{HOME@}/.gpg-agent-info "$@@" +@end example +@end cartouche + +@noindent +and add something like (for Bourne shells) + +@cartouche +@example + if [ -f "$@{HOME@}/.gpg-agent-info" ]; then + . "$@{HOME@}/.gpg-agent-info" + export GPG_AGENT_INFO + export SSH_AUTH_SOCK + export SSH_AGENT_PID + fi +@end example +@end cartouche + +@noindent +to your shell initialization file (e.g. @file{~/.bashrc}). @c @c Assuan Protocol diff --git a/doc/scdaemon.texi b/doc/scdaemon.texi index 134ca40df..d4a21b5ce 100644 --- a/doc/scdaemon.texi +++ b/doc/scdaemon.texi @@ -181,6 +181,14 @@ protocol. Note that this option may reveal sensitive data. This option disables all ticker functions like checking for card insertions. +@item --debug-allow-core-dump +@opindex debug-allow-core-dump +For security reasons we won't create a core dump when the process +aborts. For debugging purposes it is sometimes better to allow core +dump. This options enables it and also changes the working directory to +@file{/tmp} when running in @option{--server} mode. + + @item --no-detach @opindex no-detach Don't detach the process from the console. This is manly usefule for diff --git a/scd/ChangeLog b/scd/ChangeLog index da433e2f8..0f7e4d2fa 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,7 @@ +2005-06-06 Werner Koch + + * scdaemon.c (main): New option --debug-allow-core-dump. + 2005-06-03 Werner Koch * scdaemon.c (handle_connections): Make sure that the signals we diff --git a/scd/command.c b/scd/command.c index 287f8c921..a308078d3 100644 --- a/scd/command.c +++ b/scd/command.c @@ -70,7 +70,7 @@ struct server_local_s { struct server_local_s *next_session; /* This object is usually assigned to a CTRL object (which is - globally visible). While enumeratin all sessions we sometimes + globally visible). While enumerating all sessions we sometimes need to access data of the CTRL object; thus we keep a backpointer here. */ ctrl_t ctrl_backlink; @@ -860,6 +860,7 @@ cmd_getattr (assuan_context_t ctx, char *line) /* FIXME: Applications should not return sensistive data if the card is locked. */ rc = app_getattr (ctrl->app_ctx, ctrl, keyword); + xfree (keyword); TEST_CARD_REMOVAL (ctrl, rc); return map_to_assuan_status (rc); diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 5b5e09176..c75e87a62 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -67,6 +67,7 @@ enum cmd_and_opt_values oDebugAll, oDebugLevel, oDebugWait, + oDebugAllowCoreDump, oDebugCCIDDriver, oNoGreeting, oNoOptions, @@ -110,6 +111,7 @@ static ARGPARSE_OPTS opts[] = { { oDebugAll, "debug-all" ,0, "@"}, { oDebugLevel, "debug-level" ,2, "@"}, { oDebugWait,"debug-wait",1, "@"}, + { oDebugAllowCoreDump, "debug-allow-core-dump", 0, "@" }, { oDebugCCIDDriver, "debug-ccid-driver", 0, "@"}, { oDebugDisableTicker, "debug-disable-ticker", 0, "@"}, { oNoDetach, "no-detach" ,0, N_("do not detach from the console")}, @@ -318,6 +320,7 @@ main (int argc, char **argv ) int debug_wait = 0; int gpgconf_list = 0; const char *config_filename = NULL; + int allow_coredump = 0; set_strusage (my_strusage); gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); @@ -448,6 +451,10 @@ main (int argc, char **argv ) case oDebugAll: opt.debug = ~0; break; case oDebugLevel: debug_level = pargs.r.ret_str; break; case oDebugWait: debug_wait = pargs.r.ret_int; break; + case oDebugAllowCoreDump: + enable_core_dumps (); + allow_coredump = 1; + break; case oDebugCCIDDriver: ccid_set_debug_level (ccid_set_debug_level (-1)+1); break; @@ -604,6 +611,17 @@ main (int argc, char **argv ) sigaction (SIGPIPE, &sa, NULL); } + /* If --debug-allow-core-dump has been given we also need to + switch the working directory to a place where we can actually + write. */ + if (allow_coredump) + { + if (chdir("/tmp")) + log_debug ("chdir to `/tmp' failed: %s\n", strerror (errno)); + else + log_debug ("changed working directory to `/tmp'\n"); + } + /* In multi server mode we need to listen on an additional socket. Create that socket now before starting the handler for the pipe connection. This allows that handler to send -- cgit From 68191d0c935719d9ab171e49b6456372c72cf1c4 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 20 Jul 2005 15:05:05 +0000 Subject: * keylist.c (email_kludge): Reworked. * certdump.c (gpgsm_print_serial, gpgsm_dump_serial): Cast printf arg to unsigned. * call-dirmngr.c (gpgsm_dirmngr_run_command): Ditto --- THANKS | 3 ++- TODO | 2 ++ scd/ChangeLog | 13 +++++++++++++ scd/apdu.c | 6 ++++-- scd/app-openpgp.c | 9 ++++++++- scd/scdaemon.c | 2 ++ sm/ChangeLog | 8 ++++++++ sm/call-dirmngr.c | 2 +- sm/certdump.c | 4 ++-- sm/keylist.c | 26 +++++++++++++++++++------- sm/server.c | 2 +- 11 files changed, 62 insertions(+), 15 deletions(-) (limited to 'scd/scdaemon.c') diff --git a/THANKS b/THANKS index 57add954b..408497046 100644 --- a/THANKS +++ b/THANKS @@ -1,7 +1,8 @@ Alexander Belopolsky belopolsky at mac.com Andrew J. Schorr aschorr at telemetry-investments.com -Kazu Yamamoto kazu@iij.ad.jp +Charly Avital shavital at mac.com +Kazu Yamamoto kazu at iij.ad.jp Michael Nottebrock michaelnottebrock at gmx.net Ray Link rlink at pitt.edu Richard Lefebvre rick at cerca.umontreal.ca diff --git a/TODO b/TODO index 5f1b57a0f..478f437c2 100644 --- a/TODO +++ b/TODO @@ -103,3 +103,5 @@ might want to have an agent context for each service request +* sm/ +** --include-certs seems to be a dummy option. diff --git a/scd/ChangeLog b/scd/ChangeLog index 0f7e4d2fa..970335aef 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,16 @@ +2005-07-05 Werner Koch + + * app-openpgp.c (do_readkey): Return a mallcoed copy of the key as + required by the description. Thanks to Moritz for tracking this + problem down. + +2005-06-21 Werner Koch + + * scdaemon.c (main): ifdef call to ccid_set_debug_level. + + * apdu.c (reset_pcsc_reader, open_pcsc_reader): Cast size_t to + ulong for printf. + 2005-06-06 Werner Koch * scdaemon.c (main): New option --debug-allow-core-dump. diff --git a/scd/apdu.c b/scd/apdu.c index 975fffa24..79022f21b 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -809,7 +809,8 @@ reset_pcsc_reader (int slot) len -= 4; /* Already read the error code. */ if (len > DIM (slotp->atr)) { - log_error ("PC/SC returned a too large ATR (len=%x)\n", len); + log_error ("PC/SC returned a too large ATR (len=%lx)\n", + (unsigned long)len); sw = SW_HOST_GENERAL_ERROR; goto command_failed; } @@ -1425,7 +1426,8 @@ open_pcsc_reader (const char *portstr) len -= 4; /* Already read the error code. */ if (len > DIM (slotp->atr)) { - log_error ("PC/SC returned a too large ATR (len=%x)\n", len); + log_error ("PC/SC returned a too large ATR (len=%lx)\n", + (unsigned long)len); goto command_failed; } err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8]; diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 11e6eebaf..bd56fb99d 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -1230,8 +1230,15 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen) buf = app->app_local->pk[keyno-1].key; if (!buf) return gpg_error (GPG_ERR_NO_PUBKEY); - *pk = buf; *pklen = app->app_local->pk[keyno-1].keylen;; + *pk = xtrymalloc (*pklen); + if (!*pk) + { + err = gpg_error_from_errno (errno); + *pklen = 0; + return err; + } + memcpy (*pk, buf, *pklen); return 0; #else return gpg_error (GPG_ERR_NOT_IMPLEMENTED); diff --git a/scd/scdaemon.c b/scd/scdaemon.c index c75e87a62..c6995abcc 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -456,7 +456,9 @@ main (int argc, char **argv ) allow_coredump = 1; break; case oDebugCCIDDriver: +#ifdef HAVE_LIBUSB ccid_set_debug_level (ccid_set_debug_level (-1)+1); +#endif /*HAVE_LIBUSB*/ break; case oDebugDisableTicker: ticker_disabled = 1; break; diff --git a/sm/ChangeLog b/sm/ChangeLog index f1eb49c2c..cefa77e32 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,3 +1,11 @@ +2005-07-20 Werner Koch + + * keylist.c (email_kludge): Reworked. + + * certdump.c (gpgsm_print_serial, gpgsm_dump_serial): Cast printf + arg to unsigned. + * call-dirmngr.c (gpgsm_dirmngr_run_command): Ditto + 2005-07-19 Werner Koch * fingerprint.c (gpgsm_get_certid): Cast printf arg to unsigned. diff --git a/sm/call-dirmngr.c b/sm/call-dirmngr.c index 847e78490..ead117dfd 100644 --- a/sm/call-dirmngr.c +++ b/sm/call-dirmngr.c @@ -827,7 +827,7 @@ gpgsm_dirmngr_run_command (CTRL ctrl, const char *command, *p++ = '+'; else if (!isprint (*s) || *s == '+') { - sprintf (p, "%%%02X", *s); + sprintf (p, "%%%02X", *(const unsigned char *)s); p += 3; } else diff --git a/sm/certdump.c b/sm/certdump.c index 98f019c4a..aae60e020 100644 --- a/sm/certdump.c +++ b/sm/certdump.c @@ -70,7 +70,7 @@ gpgsm_print_serial (FILE *fp, ksba_const_sexp_t sn) else { for (p++; n; n--, p++) - fprintf (fp, "%02X", *p); + fprintf (fp, "%02X", *(const unsigned char*)p); } } } @@ -98,7 +98,7 @@ gpgsm_dump_serial (ksba_const_sexp_t sn) else { for (p++; n; n--, p++) - log_printf ("%02X", *p); + log_printf ("%02X", *(const unsigned char *)p); } } } diff --git a/sm/keylist.c b/sm/keylist.c index a0ac73fb3..8a4eb3cdb 100644 --- a/sm/keylist.c +++ b/sm/keylist.c @@ -251,30 +251,42 @@ print_time (gnupg_isotime_t t, FILE *fp) } -/* return an allocated string with the email address extracted from a +/* Return an allocated string with the email address extracted from a DN */ static char * email_kludge (const char *name) { - const char *p; + const char *p, *string; unsigned char *buf; int n; - if (strncmp (name, "1.2.840.113549.1.9.1=#", 22)) - return NULL; + string = name; + for (;;) + { + p = strstr (string, "1.2.840.113549.1.9.1=#"); + if (!p) + return NULL; + if (p == name || (p > string+1 && p[-1] == ',' && p[-2] != '\\')) + { + name = p + 22; + break; + } + string = p + 22; + } + + /* This looks pretty much like an email address in the subject's DN we use this to add an additional user ID entry. This way, openSSL generated keys get a nicer and usable listing */ - name += 22; for (n=0, p=name; hexdigitp (p) && hexdigitp (p+1); p +=2, n++) ; - if (*p != '#' || !n) + if (!n) return NULL; buf = xtrymalloc (n+3); if (!buf) return NULL; /* oops, out of core */ *buf = '<'; - for (n=1, p=name; *p != '#'; p +=2, n++) + for (n=1, p=name; hexdigitp (p); p +=2, n++) buf[n] = xtoi_2 (p); buf[n++] = '>'; buf[n] = 0; diff --git a/sm/server.c b/sm/server.c index b3816d3d9..87a06ee4e 100644 --- a/sm/server.c +++ b/sm/server.c @@ -1109,7 +1109,7 @@ write_status_text_and_buffer ( int no, const char *string, if (s != buffer) fwrite (buffer, s-buffer, 1, statusfp ); if ( esc ) { - fprintf (statusfp, "%%%02X", *(const byte*)s ); + fprintf (statusfp, "%%%02X", *(const unsigned char*)s ); s++; n--; } buffer = s; -- cgit From 6a13cf2c3dbacb9f3afd3f64e5d0c78b9c0e77e9 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 28 Nov 2005 11:52:25 +0000 Subject: Preparing an interim release --- ChangeLog | 16 +++++ NEWS | 7 ++ TODO | 1 - agent/ChangeLog | 17 +++++ agent/agent.h | 7 +- agent/call-scd.c | 51 +++++++++----- agent/command.c | 9 ++- agent/divert-scd.c | 19 ++++- agent/minip12.c | 21 ++++-- agent/query.c | 196 +++++++++++++++++++++++++++++++++++++++++++-------- agent/t-protect.c | 3 +- am/cmacros.am | 3 +- configure.ac | 11 +-- kbx/keybox-blob.c | 2 +- po/POTFILES.in | 1 + po/de.po | 16 +++-- scd/ChangeLog | 55 +++++++++++++++ scd/apdu.c | 176 +++++++++++++++++++++++++++++++++++---------- scd/apdu.h | 9 ++- scd/app-dinsig.c | 41 +++++++++-- scd/app-nks.c | 11 +-- scd/app-openpgp.c | 9 ++- scd/app-p15.c | 13 ++-- scd/ccid-driver.c | 64 ++++++++++++++--- scd/ccid-driver.h | 7 +- scd/iso7816.c | 115 +++++++++++++++++++++++++----- scd/iso7816.h | 35 ++++++++- scd/scdaemon.c | 6 +- scd/scdaemon.h | 17 ++--- tools/ChangeLog | 8 +++ tools/gpgconf-comp.c | 7 +- tools/rfc822parse.c | 1 + 32 files changed, 784 insertions(+), 170 deletions(-) (limited to 'scd/scdaemon.c') diff --git a/ChangeLog b/ChangeLog index f63a035cc..46d66ed47 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2005-11-28 Werner Koch + + * configure.ac: Append the revision to the version string. + +2005-11-13 Werner Koch + + * am/cmacros.am (-DGNUPG_SYSCONFDIR): Define it. + +2005-11-11 Werner Koch + + * configure.ac (NEED_KSBA_VERSION: Require 0.9.13. + +2005-09-12 Werner Koch + + Released 1.9.19. + 2005-08-01 Werner Koch Released 1.9.18. diff --git a/NEWS b/NEWS index 1e1148748..edf29885d 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,10 @@ +Noteworthy changes in version 1.9.20 +------------------------------------------------- + + * [scdaemon] Support for keypads of some readers. Tested only with + SPR532. New option --disable-keypad. + + Noteworthy changes in version 1.9.19 (2005-09-12) ------------------------------------------------- diff --git a/TODO b/TODO index 32b728588..50f58cee9 100644 --- a/TODO +++ b/TODO @@ -26,7 +26,6 @@ might want to have an agent context for each service request * sm/gpgsm.c ** Support --output for all commands ** mark all unimplemented commands and options. -** Print a hint when MD2 is the cause for a problem. ** Implement --default-key ** support the anyPolicy semantic ** Check that we are really following the verification procedures in rfc3280. diff --git a/agent/ChangeLog b/agent/ChangeLog index 975484007..105178730 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,20 @@ +2005-11-24 Werner Koch + + * minip12.c (p12_parse): Fixed for case that the key object comes + prior to the certificate. + +2005-10-19 Werner Koch + + * divert-scd.c (getpin_cb): Hack to use it for a keypad message. + + * call-scd.c (inq_needpin): Reworked to support the new KEYPADINFO. + + * query.c (start_pinentry): Keep track of the owner. + (popup_message_thread, agent_popup_message_start) + (agent_popup_message_stop, agent_reset_query): New. + * command.c (start_command_handler): Make sure a popup window gets + closed. + 2005-10-08 Marcus Brinkmann * Makefile.am (gpg_protect_tool_LDADD): Add ../gl/libgnu.a. diff --git a/agent/agent.h b/agent/agent.h index 7a646a85f..0918395ce 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -133,7 +133,7 @@ struct server_control_s int have_keygrip; int use_auth_call; /* Hack to send the PKAUTH command instead of the - PKSIGN command tro scdaemon. */ + PKSIGN command to the scdaemon. */ }; typedef struct server_control_s *CTRL; typedef struct server_control_s *ctrl_t; @@ -200,6 +200,7 @@ int agent_key_available (const unsigned char *grip); /*-- query.c --*/ void initialize_module_query (void); void agent_query_dump_state (void); +void agent_reset_query (ctrl_t ctrl); int agent_askpin (ctrl_t ctrl, const char *desc_text, const char *prompt_text, const char *inital_errtext, @@ -209,6 +210,10 @@ int agent_get_passphrase (ctrl_t ctrl, char **retpass, const char *errtext); int agent_get_confirmation (ctrl_t ctrl, const char *desc, const char *ok, const char *cancel); +int agent_popup_message_start (ctrl_t ctrl, const char *desc, + const char *ok_btn, const char *cancel_btn); +void agent_popup_message_stop (ctrl_t ctrl); + /*-- cache.c --*/ void agent_flush_cache (void); diff --git a/agent/call-scd.c b/agent/call-scd.c index 7a623fda4..a883f2733 100644 --- a/agent/call-scd.c +++ b/agent/call-scd.c @@ -633,26 +633,43 @@ inq_needpin (void *opaque, const char *line) size_t pinlen; int rc; - if (!(!strncmp (line, "NEEDPIN", 7) && (line[7] == ' ' || !line[7]))) + if (!strncmp (line, "NEEDPIN", 7) && (line[7] == ' ' || !line[7])) { - log_error ("unsupported inquiry `%s'\n", line); - return ASSUAN_Inquire_Unknown; - } - line += 7; - while (*line == ' ') - line++; + line += 7; + while (*line == ' ') + line++; + + pinlen = 90; + pin = gcry_malloc_secure (pinlen); + if (!pin) + return ASSUAN_Out_Of_Core; - pinlen = 90; - pin = gcry_malloc_secure (pinlen); - if (!pin) - return ASSUAN_Out_Of_Core; + rc = parm->getpin_cb (parm->getpin_cb_arg, line, pin, pinlen); + if (rc) + rc = ASSUAN_Canceled; + if (!rc) + rc = assuan_send_data (parm->ctx, pin, pinlen); + xfree (pin); + } + else if (!strncmp (line, "KEYPADINFO", 10) && (line[10] == ' ' || !line[10])) + { + size_t code; + char *endp; - rc = parm->getpin_cb (parm->getpin_cb_arg, line, pin, pinlen); - if (rc) - rc = ASSUAN_Canceled; - if (!rc) - rc = assuan_send_data (parm->ctx, pin, pinlen); - xfree (pin); + code = strtoul (line+10, &endp, 10); + line = endp; + while (*line == ' ') + line++; + + rc = parm->getpin_cb (parm->getpin_cb_arg, line, NULL, code); + if (rc) + rc = ASSUAN_Canceled; + } + else + { + log_error ("unsupported inquiry `%s'\n", line); + rc = ASSUAN_Inquire_Unknown; + } return rc; } diff --git a/agent/command.c b/agent/command.c index c39bcc6ab..daf9b8698 100644 --- a/agent/command.c +++ b/agent/command.c @@ -316,11 +316,11 @@ cmd_sigkey (ASSUAN_CONTEXT ctx, char *line) this command is not used a default text will be used. Note, that this description implictly selects the label used for the entry box; if the string contains the string PIN (which in general will - not be translated), "PIN" is used, other wiese the translation of + not be translated), "PIN" is used, otherwise the translation of 'passphrase" is used. The description string should not contain blanks unless they are percent or '+' escaped. - The descrition is only valid for the next PKSIGN or PKDECRYPT + The description is only valid for the next PKSIGN or PKDECRYPT operation. */ static int @@ -399,7 +399,7 @@ cmd_sethash (ASSUAN_CONTEXT ctx, char *line) /* PKSIGN Perform the actual sign operation. Neither input nor output are - sensitive to eavesdropping */ + sensitive to eavesdropping. */ static int cmd_pksign (ASSUAN_CONTEXT ctx, char *line) { @@ -1085,6 +1085,9 @@ start_command_handler (int listen_fd, int fd) /* Reset the SCD if needed. */ agent_reset_scd (&ctrl); + /* Reset the pinentry (in case of popup messages). */ + agent_reset_query (&ctrl); + assuan_deinit_server (ctx); if (ctrl.display) free (ctrl.display); diff --git a/agent/divert-scd.c b/agent/divert-scd.c index 9d2fa446c..926df2622 100644 --- a/agent/divert-scd.c +++ b/agent/divert-scd.c @@ -204,7 +204,7 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf) const char *again_text = NULL; const char *prompt = "PIN"; - if (maxbuf < 2) + if (buf && maxbuf < 2) return gpg_error (GPG_ERR_INV_VALUE); /* Parse the flags. */ @@ -223,6 +223,23 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf) else if (info && *info == '|') log_debug ("pin_cb called without proper PIN info hack\n"); + /* If BUF has been passed as NULL, we are in keypad mode: The + callback opens the popup and immediatley returns. */ + if (!buf) + { + if (maxbuf == 0) /* Close the pinentry. */ + { + agent_popup_message_stop (ctrl); + rc = 0; + } + else if (maxbuf == 1) /* Open the pinentry. */ + { + rc = agent_popup_message_start (ctrl, info, NULL, NULL); + } + else + rc = gpg_error (GPG_ERR_INV_VALUE); + return rc; + } /* FIXME: keep PI and TRIES in OPAQUE. Frankly this is a whole mess because we should call the card's verify function from the diff --git a/agent/minip12.c b/agent/minip12.c index 91eef63f4..55f3946bf 100644 --- a/agent/minip12.c +++ b/agent/minip12.c @@ -511,7 +511,7 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, goto bailout; } - /* Loop over all certificates inside the bab. */ + /* Loop over all certificates inside the bag. */ while (n) { int isbag = 0; @@ -860,6 +860,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, size_t n = length; const char *where; int bagseqlength, len; + gcry_mpi_t *result = NULL; where = "pfx"; if (parse_tag (&p, &n, &ti)) @@ -936,10 +937,17 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, else if (ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_data) && !memcmp (p, oid_data, DIM(oid_data))) { - p += DIM(oid_data); - n -= DIM(oid_data); - len -= DIM(oid_data); - return parse_bag_data (p, n, (p-buffer), pw); + if (result) + log_info ("already got an data object, skipping next one\n"); + else + { + p += DIM(oid_data); + n -= DIM(oid_data); + len -= DIM(oid_data); + result = parse_bag_data (p, n, (p-buffer), pw); + if (!result) + goto bailout; + } } else log_info ( "unknown bag type - skipped\n"); @@ -950,9 +958,10 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, n -= len; } - return NULL; + return result; bailout: log_error ("error at \"%s\", offset %u\n", where, (p - buffer)); + /* fixme: need to release RESULT. */ return NULL; } diff --git a/agent/query.c b/agent/query.c index b231f6fc3..a5a3d0153 100644 --- a/agent/query.c +++ b/agent/query.c @@ -27,9 +27,10 @@ #include #include #include -#ifdef USE_GNU_PTH -# include +#ifndef HAVE_W32_SYSTEM +#include #endif +#include #include "agent.h" #include "i18n.h" @@ -48,14 +49,30 @@ time. */ #define LOCK_TIMEOUT (1*60) +/* The assuan context of the current pinentry. */ +static assuan_context_t entry_ctx; -static assuan_context_t entry_ctx = NULL; -#ifdef USE_GNU_PTH +/* The control variable of the connection owning the current pinentry. + This is only valid if ENTRY_CTX is not NULL. Note, that we care + only about the value of the pointer and that it should never be + dereferenced. */ +static ctrl_t entry_owner; + +/* A mutex used to serialize access to the pinentry. */ static pth_mutex_t entry_lock; -#endif -/* data to be passed to our callbacks */ -struct entry_parm_s { +/* The thread ID of the popup working thread. */ +static pth_t popup_tid; + +/* A flag used in communication between the popup working thread and + its stop function. */ +static int popup_finished; + + + +/* Data to be passed to our callbacks, */ +struct entry_parm_s +{ int lines; size_t size; unsigned char *buffer; @@ -67,17 +84,17 @@ struct entry_parm_s { /* This function must be called once to initialize this module. This has to be done before a second thread is spawned. We can't do the static initialization because Pth emulation code might not be able - to do a static init; in particualr, it is not possible for W32. */ + to do a static init; in particular, it is not possible for W32. */ void initialize_module_query (void) { -#ifdef USE_GNU_PTH static int initialized; if (!initialized) - if (pth_mutex_init (&entry_lock)) - initialized = 1; -#endif /*USE_GNU_PTH*/ + { + if (pth_mutex_init (&entry_lock)) + initialized = 1; + } } @@ -102,8 +119,19 @@ agent_query_dump_state (void) log_info ("agent_query_dump_state: entry_lock="); dump_mutex_state (&entry_lock); log_printf ("\n"); - log_info ("agent_query_dump_state: entry_ctx=%p pid=%ld\n", - entry_ctx, (long)assuan_get_pid (entry_ctx)); + log_info ("agent_query_dump_state: entry_ctx=%p pid=%ld popup_tid=%p\n", + entry_ctx, (long)assuan_get_pid (entry_ctx), popup_tid); +} + +/* Called to make sure that a popup window owned by the current + connection gets closed. */ +void +agent_reset_query (ctrl_t ctrl) +{ + if (entry_ctx && popup_tid && entry_owner == ctrl) + { + agent_popup_message_stop (ctrl); + } } @@ -117,14 +145,12 @@ unlock_pinentry (int rc) assuan_context_t ctx = entry_ctx; entry_ctx = NULL; -#ifdef USE_GNU_PTH if (!pth_mutex_release (&entry_lock)) { log_error ("failed to release the entry lock\n"); if (!rc) rc = gpg_error (GPG_ERR_INTERNAL); } -#endif assuan_disconnect (ctx); return rc; } @@ -145,7 +171,7 @@ atfork_cb (void *opaque, int where) pinentry - we will serialize _all_ pinentry calls. */ static int -start_pinentry (CTRL ctrl) +start_pinentry (ctrl_t ctrl) { int rc; const char *pgmname; @@ -153,13 +179,10 @@ start_pinentry (CTRL ctrl) const char *argv[5]; int no_close_list[3]; int i; + pth_event_t evt; -#ifdef USE_GNU_PTH - { - pth_event_t evt; - - evt = pth_event (PTH_EVENT_TIME, pth_timeout (LOCK_TIMEOUT, 0)); - if (!pth_mutex_acquire (&entry_lock, 0, evt)) + evt = pth_event (PTH_EVENT_TIME, pth_timeout (LOCK_TIMEOUT, 0)); + if (!pth_mutex_acquire (&entry_lock, 0, evt)) { if (pth_event_occurred (evt)) rc = gpg_error (GPG_ERR_TIMEOUT); @@ -170,9 +193,9 @@ start_pinentry (CTRL ctrl) gpg_strerror (rc)); return rc; } - pth_event_free (evt, PTH_FREE_THIS); - } -#endif + pth_event_free (evt, PTH_FREE_THIS); + + entry_owner = ctrl; if (entry_ctx) return 0; @@ -436,7 +459,7 @@ agent_askpin (ctrl_t ctrl, passphrase is returned in RETPASS as an hex encoded string to be freed by the caller */ int -agent_get_passphrase (CTRL ctrl, +agent_get_passphrase (ctrl_t ctrl, char **retpass, const char *desc, const char *prompt, const char *errtext) { @@ -517,11 +540,11 @@ agent_get_passphrase (CTRL ctrl, /* Pop up the PIN-entry, display the text and the prompt and ask the - user to confirm this. We return 0 for success, ie. the used + user to confirm this. We return 0 for success, ie. the user confirmed it, GPG_ERR_NOT_CONFIRMED for what the text says or an other error. */ int -agent_get_confirmation (CTRL ctrl, +agent_get_confirmation (ctrl_t ctrl, const char *desc, const char *ok, const char *cancel) { int rc; @@ -562,4 +585,119 @@ agent_get_confirmation (CTRL ctrl, } +/* The thread running the popup message. */ +static void * +popup_message_thread (void *arg) +{ + assuan_transact (entry_ctx, "CONFIRM", NULL, NULL, NULL, NULL, NULL, NULL); + popup_finished = 1; + return NULL; +} + + +/* Pop up a message window similar to the confirm one but keep it open + until agent_popup_message_stop has been called. It is crucial for + the caller to make sure that the stop function gets called as soon + as the message is not anymore required becuase the message is + system modal and all other attempts to use the pinentry will fail + (after a timeout). */ +int +agent_popup_message_start (ctrl_t ctrl, const char *desc, + const char *ok_btn, const char *cancel_btn) +{ + int rc; + char line[ASSUAN_LINELENGTH]; + pth_attr_t tattr; + + rc = start_pinentry (ctrl); + if (rc) + return rc; + + if (desc) + snprintf (line, DIM(line)-1, "SETDESC %s", desc); + else + snprintf (line, DIM(line)-1, "RESET"); + line[DIM(line)-1] = 0; + rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (map_assuan_err (rc)); + + if (ok_btn) + { + snprintf (line, DIM(line)-1, "SETOK %s", ok_btn); + line[DIM(line)-1] = 0; + rc = assuan_transact (entry_ctx, line, NULL,NULL,NULL,NULL,NULL,NULL); + if (rc) + return unlock_pinentry (map_assuan_err (rc)); + } + if (cancel_btn) + { + snprintf (line, DIM(line)-1, "SETCANCEL %s", cancel_btn); + line[DIM(line)-1] = 0; + rc = assuan_transact (entry_ctx, line, NULL,NULL,NULL,NULL,NULL,NULL); + if (rc) + return unlock_pinentry (map_assuan_err (rc)); + } + + tattr = pth_attr_new(); + pth_attr_set (tattr, PTH_ATTR_JOINABLE, 1); + pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 256*1024); + pth_attr_set (tattr, PTH_ATTR_NAME, "popup-message"); + + popup_finished = 0; + popup_tid = pth_spawn (tattr, popup_message_thread, NULL); + if (!popup_tid) + { + rc = gpg_error_from_errno (errno); + log_error ("error spawning popup message handler: %s\n", + strerror (errno) ); + pth_attr_destroy (tattr); + return unlock_pinentry (rc); + } + pth_attr_destroy (tattr); + + return 0; +} + +/* Close a popup window. */ +void +agent_popup_message_stop (ctrl_t ctrl) +{ + int rc; + pid_t pid; + + if (!popup_tid || !entry_ctx) + { + log_debug ("agent_popup_message_stop called with no active popup\n"); + return; + } + + pid = assuan_get_pid (entry_ctx); + if (pid == (pid_t)(-1)) + ; /* No pid available can't send a kill. */ + else if (popup_finished) + ; /* Already finished and ready for joining. */ + else if (pid && ((rc=waitpid (pid, NULL, WNOHANG))==-1 || (rc == pid)) ) + { /* The daemon already died. No need to send a kill. However + because we already waited for the process, we need to tell + assuan that it should not wait again (done by + unlock_pinentry). */ + if (rc == pid) + assuan_set_flag (entry_ctx, ASSUAN_NO_WAITPID, 1); + } + else + kill (pid, SIGINT); + + /* Now wait for the thread to terminate. */ + rc = pth_join (popup_tid, NULL); + if (!rc) + log_debug ("agent_popup_message_stop: pth_join failed: %s\n", + strerror (errno)); + popup_tid = NULL; + entry_owner = NULL; + + /* Now we can close the connection. */ + unlock_pinentry (0); +} + diff --git a/agent/t-protect.c b/agent/t-protect.c index 5187cf8f7..fee3c561d 100644 --- a/agent/t-protect.c +++ b/agent/t-protect.c @@ -173,7 +173,8 @@ test_agent_protect (void) for (i = 0; i < DIM (specs); i++) { - ret = agent_protect (specs[i].key, specs[i].passphrase, + ret = agent_protect ((const unsigned char*)specs[i].key, + specs[i].passphrase, &specs[i].result, &specs[i].resultlen); if (gpg_err_code (ret) != specs[i].ret_expected) { diff --git a/am/cmacros.am b/am/cmacros.am index 0f7a09fe0..de68b6f31 100644 --- a/am/cmacros.am +++ b/am/cmacros.am @@ -25,7 +25,8 @@ if ! HAVE_DOSISH_SYSTEM AM_CPPFLAGS += -DGNUPG_BINDIR="\"$(bindir)\"" \ -DGNUPG_LIBEXECDIR="\"$(libexecdir)\"" \ -DGNUPG_LIBDIR="\"$(libdir)/@PACKAGE@\"" \ - -DGNUPG_DATADIR="\"$(datadir)/@PACKAGE@\"" + -DGNUPG_DATADIR="\"$(datadir)/@PACKAGE@\"" \ + -DGNUPG_SYSCONFDIR="\"$(sysconfdir)/@PACKAGE@\"" endif if GNUPG_AGENT_PGM diff --git a/configure.ac b/configure.ac index 30b183046..6ba66968d 100644 --- a/configure.ac +++ b/configure.ac @@ -22,9 +22,12 @@ AC_PREREQ(2.52) min_automake_version="1.9.3" -# Version number: Remember to change it immediately *after* a release. -# Add a "-cvs" prefix for non-released code. -AC_INIT(gnupg, 1.9.19, gnupg-devel@gnupg.org) +# Remember to change the version number immediately *after* a release. +# Uncomment the my_iscvs macro for non-released code. +m4_define(my_version, [1.9.20]) +m4_define(my_iscvs, yes) +AC_INIT([gnupg], my_version[]m4_ifdef([my_iscvs], [-cvs[]m4_translit( + [$Revision$],[Ra-z $:])]), [gnupg-devel@gnupg.org]) # Set development_version to yes if the minor number is odd or you # feel that the default check for a development version is not # sufficient. @@ -36,7 +39,7 @@ NEED_LIBGCRYPT_VERSION=1.1.94 NEED_LIBASSUAN_VERSION=0.6.10 -NEED_KSBA_VERSION=0.9.12 +NEED_KSBA_VERSION=0.9.13 PACKAGE=$PACKAGE_NAME diff --git a/kbx/keybox-blob.c b/kbx/keybox-blob.c index 67c74b777..eacc0014a 100644 --- a/kbx/keybox-blob.c +++ b/kbx/keybox-blob.c @@ -61,7 +61,7 @@ X.509 specific are noted like [X.509: xxx] u32 offset to the n-th key's keyID (a keyID is always 8 byte) or 0 if not known which is the case only for X509. u16 special key flags - bit 0 = + bit 0 = qualified signature (not yet implemented} u16 reserved u16 size of serialnumber(may be zero) n u16 (see above) bytes of serial number diff --git a/po/POTFILES.in b/po/POTFILES.in index 8fff858db..eb5711ddb 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -17,6 +17,7 @@ kbx/kbxutil.c scd/scdaemon.c scd/app-openpgp.c +scd/app-nks.c sm/base64.c sm/call-agent.c diff --git a/po/de.po b/po/de.po index 6b28544cb..2201c0429 100644 --- a/po/de.po +++ b/po/de.po @@ -10,8 +10,8 @@ msgid "" msgstr "" "Project-Id-Version: gnupg2 1.9.18\n" "Report-Msgid-Bugs-To: translations@gnupg.org\n" -"POT-Creation-Date: 2005-11-23 13:00+0100\n" -"PO-Revision-Date: 2005-11-23 13:02+0100\n" +"POT-Creation-Date: 2005-11-28 12:14+0100\n" +"PO-Revision-Date: 2005-11-28 12:16+0100\n" "Last-Translator: Werner Koch \n" "Language-Team: de\n" "MIME-Version: 1.0\n" @@ -766,6 +766,10 @@ msgstr "" msgid "can't access %s - invalid OpenPGP card?\n" msgstr "Zugriff auf %s nicht möglich - ungültige OpenPGP Karte?\n" +#: scd/app-nks.c:344 +msgid "the NullPIN has not yet been changed\n" +msgstr "Die Nullpin wurde noch nicht geändert\n" + #: sm/base64.c:317 #, c-format msgid "invalid radix64 character %02x skipped\n" @@ -1369,7 +1373,7 @@ msgstr "Signieren mit `%s' nicht möglich: %s\n" #: sm/gpgsm.c:1475 msgid "this command has not yet been implemented\n" -msgstr "Diee Kommando wurde noch nicht implementiert\n" +msgstr "Dieses Kommando wurde noch nicht implementiert\n" #: sm/gpgsm.c:1705 sm/gpgsm.c:1742 sm/qualified.c:73 #, c-format @@ -1548,10 +1552,10 @@ msgid "" msgstr "" "Sie sind dabei, eine Signatur mit dem Zertifikat:\n" "\"%s\"\n" -"zu erzeugen. Dies wird einen qualifizierte Signatur erzeugen, \n" -"die gesetzlich einer handgeschriebene gleichgestellt ist.\n" +"zu erzeugen. Dies wird eine qualifizierte Signatur erzeugen, \n" +"die gesetzlich einer handgeschriebenen gleichgestellt ist.\n" "\n" -"%s%sSind Sie wirklich sicher, da Sie dies möchten?" +"%s%sSind Sie wirklich sicher, daß Sie dies möchten?" #: sm/qualified.c:224 msgid "" diff --git a/scd/ChangeLog b/scd/ChangeLog index ccea117dd..008d84080 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,8 +1,63 @@ +2005-11-23 Werner Koch + + * app-nks.c (verify_pin): Give a special error message for a Nullpin. + +2005-10-29 Werner Koch + + * ccid-driver.c (send_escape_cmd): New args RESULT, RESULTLEN and + RESULTMAX. Changed all callers. + (ccid_transceive_escape): New. + +2005-10-27 Werner Koch + + * apdu.c [__CYGWIN__]: Make cygwin environment similar to _WIN32. + Suggested by John P. Clizbe. + * scdaemon.c [__CYGWIN__]: Set default PC/SC driver to winscard.dll. + +2005-10-19 Werner Koch + + * ccid-driver.h (CCID_DRIVER_ERR_NO_KEYPAD): New. + * apdu.h (SW_HOST_NO_KEYPAD): New. + * iso7816.h (struct iso7816_pininfo_s): New. + * iso7816.c (map_sw): Support new code. + (iso7816_check_keypad): New. + (iso7816_verify_kp, iso7816_change_reference_data_kp) + (iso7816_reset_retry_counter_kp): New. Extended versions of the + original functions. + * apdu.c (host_sw_string): Support new code. + (reader_table_s): New field CHECK_KEYPAD. + (new_reader_slot, open_ct_reader, open_pcsc_reader) + (open_ccid_reader, open_rapdu_reader): Initialize it. + (check_ccid_keypad): New. + (apdu_check_keypad): New. + (apdu_send_le): Factored all code out to ... + (send_le): .. new. Takes an additional arg; changed all callers + of the orginal function to use this one with a NULL for the new + arg. + (apdu_send_simple_kp): New. + (ct_send_apdu, pcsc_send_apdu, my_rapdu_send_apdu) + (send_apdu_ccid): New arg PININFO. + (send_apdu_ccid): Use the new arg. + + * scdaemon.c: New option --disable-keypad. + 2005-10-08 Marcus Brinkmann * Makefile.am (scdaemon_LDADD): Add ../gl/libgnu.a after ../common/libcommon.a. +2005-09-20 Werner Koch + + * app-dinsig.c (verify_pin): Try ISO 9564 BCD encoding. + + * iso7816.c (iso7816_select_application): Add arg FLAGS. Changed + all callers to pass 0. + * app-openpgp.c (app_select_openpgp): But this one requires a + special flag. + + * app-p15.c (app_select_p15): Don't use select application for the + BELPIC. + 2005-09-09 Werner Koch * pcsc-wrapper.c (main): Removed bogus free. diff --git a/scd/apdu.c b/scd/apdu.c index 678ea12d3..f59d832d4 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -66,10 +66,10 @@ #include "ccid-driver.h" -/* To to conflicting use of threading libraries we usually can't link +/* Due to conflicting use of threading libraries we usually can't link against libpcsclite. Instead we use a wrapper program. */ #ifdef USE_GNU_PTH -#ifndef HAVE_W32_SYSTEM +#if !defined(HAVE_W32_SYSTEM) && !defined(__CYGWIN__) #define NEED_PCSC_WRAPPER 1 #endif #endif @@ -78,7 +78,7 @@ #define MAX_READER 4 /* Number of readers we support concurrently. */ -#ifdef _WIN32 +#if defined(_WIN32) || defined(__CYGWIN__) #define DLSTDCALL __stdcall #else #define DLSTDCALL @@ -90,6 +90,14 @@ #define MAX_OPEN_FDS 20 #endif +/* Helper to pass patrameters related to keypad based operations. */ +struct pininfo_s +{ + int mode; + int minlen; + int maxlen; + int padlen; +}; /* A structure to collect information pertaining to one reader slot. */ @@ -103,7 +111,8 @@ struct reader_table_s { int (*reset_reader)(int); int (*get_status_reader)(int, unsigned int *); int (*send_apdu_reader)(int,unsigned char *,size_t, - unsigned char *, size_t *); + unsigned char *, size_t *, struct pininfo_s *); + int (*check_keypad)(int, int, int, int, int, int); void (*dump_status_reader)(int); struct { @@ -320,6 +329,7 @@ new_reader_slot (void) reader_table[reader].reset_reader = NULL; reader_table[reader].get_status_reader = NULL; reader_table[reader].send_apdu_reader = NULL; + reader_table[reader].check_keypad = NULL; reader_table[reader].dump_status_reader = NULL; reader_table[reader].used = 1; @@ -372,6 +382,7 @@ host_sw_string (long err) case SW_HOST_GENERAL_ERROR: return "general error"; case SW_HOST_NO_READER: return "no reader"; case SW_HOST_ABORTED: return "aborted"; + case SW_HOST_NO_KEYPAD: return "no keypad"; default: return "unknown host status error"; } } @@ -533,7 +544,7 @@ ct_get_status (int slot, unsigned int *status) 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) + unsigned char *buffer, size_t *buflen, struct pininfo_s *pininfo) { int rc; unsigned char dad[1], sad[1]; @@ -596,6 +607,7 @@ open_ct_reader (int port) 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].check_keypad = NULL; reader_table[reader].dump_status_reader = ct_dump_reader_status; dump_reader_status (reader); @@ -1082,7 +1094,8 @@ pcsc_get_status (int slot, unsigned int *status) 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) + unsigned char *buffer, size_t *buflen, + struct pininfo_s *pininfo) { #ifdef NEED_PCSC_WRAPPER long err; @@ -1479,6 +1492,7 @@ open_pcsc_reader (const char *portstr) 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].check_keypad = NULL; reader_table[slot].dump_status_reader = dump_pcsc_reader_status; /* Read the status so that IS_T0 will be set. */ @@ -1625,6 +1639,7 @@ open_pcsc_reader (const char *portstr) 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].check_keypad = NULL; reader_table[slot].dump_status_reader = dump_pcsc_reader_status; /* log_debug ("state from pcsc_status: 0x%lx\n", card_state); */ @@ -1713,7 +1728,8 @@ get_status_ccid (int slot, unsigned int *status) set to BUFLEN. Returns: Internal CCID driver error code. */ static int send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen, - unsigned char *buffer, size_t *buflen) + unsigned char *buffer, size_t *buflen, + struct pininfo_s *pininfo) { long err; size_t maxbuflen; @@ -1727,9 +1743,18 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen, log_printhex (" APDU_data:", apdu, apdulen); maxbuflen = *buflen; - err = ccid_transceive (reader_table[slot].ccid.handle, - apdu, apdulen, - buffer, maxbuflen, buflen); + if (pininfo) + err = ccid_transceive_secure (reader_table[slot].ccid.handle, + apdu, apdulen, + pininfo->mode, + pininfo->minlen, + pininfo->maxlen, + pininfo->padlen, + buffer, maxbuflen, buflen); + else + err = ccid_transceive (reader_table[slot].ccid.handle, + apdu, apdulen, + buffer, maxbuflen, buflen); if (err) log_error ("ccid_transceive failed: (0x%lx)\n", err); @@ -1737,6 +1762,24 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen, return err; } + +/* Check whether the CCID reader supports the ISO command code COMMAND + on the keypad. Return 0 on success. For a description of the pin + parameters, see ccid-driver.c */ +static int +check_ccid_keypad (int slot, int command, int pin_mode, + int pinlen_min, int pinlen_max, int pin_padlen) +{ + unsigned char apdu[] = { 0, 0, 0, 0x81 }; + + apdu[1] = command; + return ccid_transceive_secure (reader_table[slot].ccid.handle, + apdu, sizeof apdu, + pin_mode, pinlen_min, pinlen_max, pin_padlen, + NULL, 0, NULL); +} + + /* Open the reader and try to read an ATR. */ static int open_ccid_reader (const char *portstr) @@ -1776,6 +1819,7 @@ open_ccid_reader (const char *portstr) 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].check_keypad = check_ccid_keypad; reader_table[slot].dump_status_reader = dump_ccid_reader_status; dump_reader_status (slot); @@ -1932,7 +1976,8 @@ my_rapdu_get_status (int slot, unsigned int *status) set to BUFLEN. Returns: APDU error code. */ static int my_rapdu_send_apdu (int slot, unsigned char *apdu, size_t apdulen, - unsigned char *buffer, size_t *buflen) + unsigned char *buffer, size_t *buflen, + struct pininfo_s *pininfo) { int err; reader_table_t slotp; @@ -2063,6 +2108,7 @@ open_rapdu_reader (int portno, 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].check_keypad = NULL; reader_table[slot].dump_status_reader = NULL; dump_reader_status (slot); @@ -2198,28 +2244,28 @@ apdu_open_reader (const char *portstr) pcsc_establish_context = dlsym (handle, "SCardEstablishContext"); pcsc_release_context = dlsym (handle, "SCardReleaseContext"); pcsc_list_readers = dlsym (handle, "SCardListReaders"); -#ifdef _WIN32 +#if defined(_WIN32) || defined(__CYGWIN__) if (!pcsc_list_readers) pcsc_list_readers = dlsym (handle, "SCardListReadersA"); #endif pcsc_get_status_change = dlsym (handle, "SCardGetStatusChange"); -#ifdef _WIN32 +#if defined(_WIN32) || defined(__CYGWIN__) if (!pcsc_get_status_change) pcsc_get_status_change = dlsym (handle, "SCardGetStatusChangeA"); #endif pcsc_connect = dlsym (handle, "SCardConnect"); -#ifdef _WIN32 +#if defined(_WIN32) || defined(__CYGWIN__) if (!pcsc_connect) pcsc_connect = dlsym (handle, "SCardConnectA"); #endif pcsc_reconnect = dlsym (handle, "SCardReconnect"); -#ifdef _WIN32 +#if defined(_WIN32) || defined(__CYGWIN__) if (!pcsc_reconnect) pcsc_reconnect = dlsym (handle, "SCardReconnectA"); #endif pcsc_disconnect = dlsym (handle, "SCardDisconnect"); pcsc_status = dlsym (handle, "SCardStatus"); -#ifdef _WIN32 +#if defined(_WIN32) || defined(__CYGWIN__) if (!pcsc_status) pcsc_status = dlsym (handle, "SCardStatusA"); #endif @@ -2492,11 +2538,30 @@ apdu_get_status (int slot, int hang, } +/* Check whether the reader supports the ISO command code COMMAND on + the keypad. Return 0 on success. For a description of the pin + parameters, see ccid-driver.c */ +int +apdu_check_keypad (int slot, int command, int pin_mode, + int pinlen_min, int pinlen_max, int pin_padlen) +{ + if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) + return SW_HOST_NO_DRIVER; + + if (reader_table[slot].check_keypad) + return reader_table[slot].check_keypad (slot, command, + pin_mode, pinlen_min, pinlen_max, + pin_padlen); + else + return SW_HOST_NOT_SUPPORTED; +} + + /* Dispatcher for the actual send_apdu function. Note, that this function should be called in locked state. */ static int send_apdu (int slot, unsigned char *apdu, size_t apdulen, - unsigned char *buffer, size_t *buflen) + unsigned char *buffer, size_t *buflen, struct pininfo_s *pininfo) { if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) return SW_HOST_NO_DRIVER; @@ -2504,24 +2569,20 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen, if (reader_table[slot].send_apdu_reader) return reader_table[slot].send_apdu_reader (slot, apdu, apdulen, - buffer, buflen); + buffer, buflen, pininfo); else return SW_HOST_NOT_SUPPORTED; } -/* Send an APDU to the card in SLOT. The APDU is created from all - given parameters: CLASS, INS, P0, P1, LC, DATA, LE. A value of -1 - for LC won't sent this field and the data field; in this case DATA - must also be passed as NULL. The return value is the status word - or -1 for an invalid SLOT or other non card related error. If - RETBUF is not NULL, it will receive an allocated buffer with the - returned data. The length of that data will be put into - *RETBUFLEN. The caller is reponsible for releasing the buffer even - in case of errors. */ -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) + +/* Core APDU trabceiver function. Parameters are described at + apdu_send_le with the exception of PININFO which indicates keypad + related operations if not NULL. */ +static int +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, + struct pininfo_s *pininfo) { #define RESULTLEN 256 unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in @@ -2570,7 +2631,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, /* As safeguard don't pass any garbage from the stack to the driver. */ memset (apdu+apdulen, 0, sizeof (apdu) - apdulen); resultlen = RESULTLEN; - rc = send_apdu (slot, apdu, apdulen, result, &resultlen); + rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo); if (rc || resultlen < 2) { log_error ("apdu_send_simple(%d) failed: %s\n", @@ -2638,7 +2699,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, apdu[apdulen++] = len; memset (apdu+apdulen, 0, sizeof (apdu) - apdulen); resultlen = RESULTLEN; - rc = send_apdu (slot, apdu, apdulen, result, &resultlen); + rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL); if (rc || resultlen < 2) { log_error ("apdu_send_simple(%d) for get response failed: %s\n", @@ -2703,6 +2764,27 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, #undef RESULTLEN } +/* Send an APDU to the card in SLOT. The APDU is created from all + given parameters: CLASS, INS, P0, P1, LC, DATA, LE. A value of -1 + for LC won't sent this field and the data field; in this case DATA + must also be passed as NULL. The return value is the status word + or -1 for an invalid SLOT or other non card related error. If + RETBUF is not NULL, it will receive an allocated buffer with the + returned data. The length of that data will be put into + *RETBUFLEN. The caller is reponsible for releasing the buffer even + in case of errors. */ +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) +{ + return send_le (slot, class, ins, p0, p1, + lc, data, le, + retbuf, retbuflen, + NULL); +} + + /* Send an APDU to the card in SLOT. The APDU is created from all given parameters: CLASS, INS, P0, P1, LC, DATA. A value of -1 for LC won't sent this field and the data field; in this case DATA must @@ -2716,8 +2798,8 @@ int apdu_send (int slot, int class, int ins, int p0, int p1, int lc, const char *data, unsigned char **retbuf, size_t *retbuflen) { - return apdu_send_le (slot, class, ins, p0, p1, lc, data, 256, - retbuf, retbuflen); + return send_le (slot, class, ins, p0, p1, lc, data, 256, + retbuf, retbuflen, NULL); } /* Send an APDU to the card in SLOT. The APDU is created from all @@ -2730,7 +2812,25 @@ int apdu_send_simple (int slot, int class, int ins, int p0, int p1, int lc, const char *data) { - return apdu_send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL); + return send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL, NULL); +} + + +/* Same as apdu_send_simple but uses the keypad of the reader. */ +int +apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1, + int lc, const char *data, + int pin_mode, + int pinlen_min, int pinlen_max, int pin_padlen) +{ + struct pininfo_s pininfo; + + pininfo.mode = pin_mode; + pininfo.minlen = pinlen_min; + pininfo.maxlen = pinlen_max; + pininfo.padlen = pin_padlen; + return send_le (slot, class, ins, p0, p1, lc, data, -1, + NULL, NULL, &pininfo); } @@ -2771,7 +2871,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen, class = apdulen? *apdu : 0; resultlen = RESULTLEN; - rc = send_apdu (slot, apdu, apdulen, result, &resultlen); + rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL); if (rc || resultlen < 2) { log_error ("apdu_send_direct(%d) failed: %s\n", @@ -2825,7 +2925,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen, apdu[apdulen++] = len; memset (apdu+apdulen, 0, sizeof (apdu) - apdulen); resultlen = RESULTLEN; - rc = send_apdu (slot, apdu, apdulen, result, &resultlen); + rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL); if (rc || resultlen < 2) { log_error ("apdu_send_direct(%d) for get response failed: %s\n", diff --git a/scd/apdu.h b/scd/apdu.h index 45388fdd1..c3af82506 100644 --- a/scd/apdu.h +++ b/scd/apdu.h @@ -63,7 +63,8 @@ enum { SW_HOST_CARD_IO_ERROR = 0x1000a, SW_HOST_GENERAL_ERROR = 0x1000b, SW_HOST_NO_READER = 0x1000c, - SW_HOST_ABORTED = 0x1000d + SW_HOST_ABORTED = 0x1000d, + SW_HOST_NO_KEYPAD = 0x1000e }; @@ -96,8 +97,14 @@ int apdu_activate (int slot); int apdu_reset (int slot); int apdu_get_status (int slot, int hang, unsigned int *status, unsigned int *changed); +int apdu_check_keypad (int slot, int command, int pin_mode, + int pinlen_min, int pinlen_max, int pin_padlen); int apdu_send_simple (int slot, int class, int ins, int p0, int p1, int lc, const char *data); +int apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1, + int lc, const char *data, + int pin_mode, + int pinlen_min, int pinlen_max, int pin_padlen); int apdu_send (int slot, int class, int ins, int p0, int p1, int lc, const char *data, unsigned char **retbuf, size_t *retbuflen); diff --git a/scd/app-dinsig.c b/scd/app-dinsig.c index 28b70c466..75cd12c59 100644 --- a/scd/app-dinsig.c +++ b/scd/app-dinsig.c @@ -1,5 +1,5 @@ /* app-dinsig.c - The DINSIG (DIN V 66291-1) card application. - * Copyright (C) 2002, 2004 Free Software Foundation, Inc. + * Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -280,10 +280,11 @@ verify_pin (app_t app, { if (!app->did_chv1 || app->force_chv1 ) { + const char *s; char *pinvalue; int rc; - rc = pincb (pincb_arg, "PIN", &pinvalue); + rc = pincb (pincb_arg, "PIN", &pinvalue); if (rc) { log_info ("PIN callback returned error: %s\n", gpg_strerror (rc)); @@ -291,8 +292,16 @@ verify_pin (app_t app, } /* We require the PIN to be at least 6 and at max 8 bytes. - According to the specs, this should all be ASCII but we don't - check this. */ + According to the specs, this should all be ASCII. */ + for (s=pinvalue; digitp (s); s++) + ; + if (*s) + { + log_error ("Non-numeric digits found in PIN\n"); + xfree (pinvalue); + return gpg_error (GPG_ERR_BAD_PIN); + } + if (strlen (pinvalue) < 6) { log_error ("PIN is too short; minimum length is 6\n"); @@ -307,6 +316,28 @@ verify_pin (app_t app, } rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue)); + if (gpg_err_code (rc) == GPG_ERR_INV_VALUE) + { + /* We assume that ISO 9564-1 encoding is used and we failed + because the first nibble we passed was 3 and not 2. DIN + says something about looking up such an encoding in the + SSD but I was not able to find any tag relevant to + this. */ + char paddedpin[8]; + int i, ndigits; + + for (ndigits=0, s=pinvalue; *s; ndigits++, s++) + ; + i = 0; + paddedpin[i++] = 0x20 | (ndigits & 0x0f); + for (s=pinvalue; i < sizeof paddedpin && *s && s[1]; s = s+2 ) + paddedpin[i++] = (((*s - '0') << 4) | ((s[1] - '0') & 0x0f)); + if (i < sizeof paddedpin && *s) + paddedpin[i++] = (((*s - '0') << 4) | 0x0f); + while (i < sizeof paddedpin) + paddedpin[i++] = 0xff; + rc = iso7816_verify (app->slot, 0x81, paddedpin, sizeof paddedpin); + } if (rc) { log_error ("verify PIN failed\n"); @@ -404,7 +435,7 @@ app_select_dinsig (APP app) int slot = app->slot; int rc; - rc = iso7816_select_application (slot, aid, sizeof aid); + rc = iso7816_select_application (slot, aid, sizeof aid, 0); if (!rc) { app->apptype = "DINSIG"; diff --git a/scd/app-nks.c b/scd/app-nks.c index b6a3037ed..73ec8ea01 100644 --- a/scd/app-nks.c +++ b/scd/app-nks.c @@ -27,7 +27,7 @@ #include #include "scdaemon.h" - +#include "i18n.h" #include "iso7816.h" #include "app-common.h" #include "tlv.h" @@ -320,7 +320,7 @@ verify_pin (app_t app, return rc; } - /* The follwoing limits are due to TCOS but also defined in the + /* The following limits are due to TCOS but also defined in the NKS specs. */ if (strlen (pinvalue) < 6) { @@ -340,7 +340,10 @@ verify_pin (app_t app, rc = iso7816_verify (app->slot, 0, pinvalue, strlen (pinvalue)); if (rc) { - log_error ("verify PIN failed\n"); + if ( gpg_error (rc) == GPG_ERR_USE_CONDITIONS ) + log_error (_("the NullPIN has not yet been changed\n")); + else + log_error ("verify PIN failed\n"); xfree (pinvalue); return rc; } @@ -492,7 +495,7 @@ app_select_nks (APP app) int slot = app->slot; int rc; - rc = iso7816_select_application (slot, aid, sizeof aid); + rc = iso7816_select_application (slot, aid, sizeof aid, 0); if (!rc) { app->apptype = "NKS"; diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 5625c729b..3d04be0be 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -1284,6 +1284,11 @@ verify_chv2 (app_t app, if (!app->did_chv2) { char *pinvalue; + iso7816_pininfo_t pininfo; + + memset (&pininfo, 0, sizeof pininfo); + pininfo.mode = 1; + pininfo.minlen = 6; rc = pincb (pincb_arg, "PIN", &pinvalue); if (rc) @@ -2455,7 +2460,9 @@ app_select_openpgp (app_t app) size_t buflen; void *relptr; - rc = iso7816_select_application (slot, aid, sizeof aid); + /* Note that the card can't cope with P2=0xCO, thus we need to pass a + special flag value. */ + rc = iso7816_select_application (slot, aid, sizeof aid, 0x0001); if (!rc) { unsigned int manufacturer; diff --git a/scd/app-p15.c b/scd/app-p15.c index 739a9ef95..8bb94cfcd 100644 --- a/scd/app-p15.c +++ b/scd/app-p15.c @@ -3268,18 +3268,15 @@ app_select_p15 (app_t app) int direct = 0; int is_belpic = 0; - rc = iso7816_select_application (slot, pkcs15_aid, sizeof pkcs15_aid); - if (rc) - { - rc = iso7816_select_application (slot, pkcs15be_aid,sizeof pkcs15be_aid); - if (!rc) - is_belpic = 1; - } + rc = iso7816_select_application (slot, pkcs15_aid, sizeof pkcs15_aid, 0); if (rc) { /* Not found: Try to locate it from 2F00. We use direct path selection here because it seems that the Belgian eID card does only allow for that. Many other cards supports this - selection method too. */ + selection method too. Note, that we don't use + select_application above for the Belgian card - the call + works but it seems that it did not switch to the correct DF. + Using the 2f02 just works. */ unsigned short path[1] = { 0x2f00 }; rc = iso7816_select_path (app->slot, path, 1, NULL, NULL); diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c index f82d93b00..fee733358 100644 --- a/scd/ccid-driver.c +++ b/scd/ccid-driver.c @@ -1240,7 +1240,9 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, if (CCID_COMMAND_FAILED (buffer)) print_command_failed (buffer); - /* Check whether a card is at all available. */ + /* Check whether a card is at all available. Note: If you add new + error codes here, check whether they need to be ignored in + send_escape_cmd. */ switch ((buffer[7] & 0x03)) { case 0: /* no error */ break; @@ -1253,16 +1255,23 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, /* Note that this function won't return the error codes NO_CARD or - CARD_INACTIVE */ + CARD_INACTIVE. IF RESULT is not NULL, the result from the + operation will get returned in RESULT and its length in RESULTLEN. + If the response is larger than RESULTMAX, an error is returned and + the required buffer length returned in RESULTLEN. */ static int send_escape_cmd (ccid_driver_t handle, - const unsigned char *data, size_t datalen) + const unsigned char *data, size_t datalen, + unsigned char *result, size_t resultmax, size_t *resultlen) { int i, rc; unsigned char msg[100]; size_t msglen; unsigned char seqno; + if (resultlen) + *resultlen = 0; + if (datalen > sizeof msg - 10) return CCID_DRIVER_ERR_INV_VALUE; /* Escape data too large. */ @@ -1285,11 +1294,42 @@ send_escape_cmd (ccid_driver_t handle, return rc; rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Escape, seqno, 5000, 0); - + if (result) + switch (rc) + { + /* We need to ignore certain errorcode here. */ + case 0: + case CCID_DRIVER_ERR_CARD_INACTIVE: + case CCID_DRIVER_ERR_NO_CARD: + { + if (msglen > resultmax) + rc = CCID_DRIVER_ERR_INV_VALUE; /* Response too large. */ + else + { + memcpy (result, msg, msglen); + *resultlen = msglen; + } + rc = 0; + } + break; + default: + break; + } + return rc; } +int +ccid_transceive_escape (ccid_driver_t handle, + const unsigned char *data, size_t datalen, + unsigned char *resp, size_t maxresplen, size_t *nresp) +{ + return send_escape_cmd (handle, data, datalen, resp, maxresplen, nresp); +} + + + /* experimental */ int ccid_poll (ccid_driver_t handle) @@ -1445,7 +1485,8 @@ ccid_get_atr (ccid_driver_t handle, { tried_iso = 1; /* Try switching to ISO mode. */ - if (!send_escape_cmd (handle, (const unsigned char*)"\xF1\x01", 2)) + if (!send_escape_cmd (handle, (const unsigned char*)"\xF1\x01", 2, + NULL, 0, NULL)) goto again; } else if (CCID_COMMAND_FAILED (msg)) @@ -1957,14 +1998,16 @@ ccid_transceive (ccid_driver_t handle, } -/* Send the CCID Secure command to the reader. APDU_BUF should contain the APDU template. PIN_MODE defines now the pin gets formatted: +/* Send the CCID Secure command to the reader. APDU_BUF should + contain the APDU template. PIN_MODE defines how the pin gets + formatted: 1 := The PIN is ASCII encoded and of variable length. The length of the PIN entered will be put into Lc by the reader. The APDU should me made up of 4 bytes without Lc. PINLEN_MIN and PINLEN_MAX define the limits for the pin length. 0 - may be used t enable usbale defaults. PIN_PADLEN should be 0 + may be used t enable reasonable defaults. PIN_PADLEN should be 0. When called with RESP and NRESP set to NULL, the function will merely check whether the reader supports the secure command for the @@ -1996,7 +2039,7 @@ ccid_transceive_secure (ccid_driver_t handle, else if (apdu_buflen >= 4 && apdu_buf[1] == 0x24 && (handle->has_pinpad & 2)) return CCID_DRIVER_ERR_NOT_SUPPORTED; /* Not yet by our code. */ else - return CCID_DRIVER_ERR_NOT_SUPPORTED; + return CCID_DRIVER_ERR_NO_KEYPAD; if (pin_mode != 1) return CCID_DRIVER_ERR_NOT_SUPPORTED; @@ -2027,7 +2070,8 @@ ccid_transceive_secure (ccid_driver_t handle, if (handle->id_vendor == VENDOR_SCM) { DEBUGOUT ("sending escape sequence to switch to a case 1 APDU\n"); - rc = send_escape_cmd (handle, (const unsigned char*)"\x80\x02\x00", 3); + rc = send_escape_cmd (handle, (const unsigned char*)"\x80\x02\x00", 3, + NULL, 0, NULL); if (rc) return rc; } @@ -2044,7 +2088,7 @@ ccid_transceive_secure (ccid_driver_t handle, if (handle->id_vendor == VENDOR_SCM) { /* For the SPR532 the next 2 bytes need to be zero. We do this - for all SCM product. Kudos to to Martin Paljak for this + for all SCM product. Kudos to Martin Paljak for this hint. */ msg[13] = msg[14] = 0; } diff --git a/scd/ccid-driver.h b/scd/ccid-driver.h index 1b9ac2f85..6f6527108 100644 --- a/scd/ccid-driver.h +++ b/scd/ccid-driver.h @@ -58,7 +58,7 @@ #ifndef CCID_DRIVER_H #define CCID_DRIVER_H -/* The CID driver returns the same error codes as the statsu words +/* The CID driver returns the same error codes as the status words used by GnuPG's apdu.h. For ease of maintenance they should always match. */ #define CCID_DRIVER_ERR_OUT_OF_CORE 0x10001 @@ -74,6 +74,7 @@ #define CCID_DRIVER_ERR_GENERAL_ERROR 0x1000b #define CCID_DRIVER_ERR_NO_READER 0x1000c #define CCID_DRIVER_ERR_ABORTED 0x1000d +#define CCID_DRIVER_ERR_NO_KEYPAD 0x1000e struct ccid_driver_s; typedef struct ccid_driver_s *ccid_driver_t; @@ -94,6 +95,10 @@ int ccid_transceive_secure (ccid_driver_t handle, int pin_mode, int pinlen_min, int pinlen_max, int pin_padlen, unsigned char *resp, size_t maxresplen, size_t *nresp); +int ccid_transceive_escape (ccid_driver_t handle, + const unsigned char *data, size_t datalen, + unsigned char *resp, size_t maxresplen, + size_t *nresp); diff --git a/scd/iso7816.c b/scd/iso7816.c index 5b985324f..5c62e1371 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -47,9 +47,9 @@ #define CMD_SELECT_FILE 0xA4 -#define CMD_VERIFY 0x20 -#define CMD_CHANGE_REFERENCE_DATA 0x24 -#define CMD_RESET_RETRY_COUNTER 0x2C +#define CMD_VERIFY ISO7816_VERIFY +#define CMD_CHANGE_REFERENCE_DATA ISO7816_CHANGE_REFERENCE_DATA +#define CMD_RESET_RETRY_COUNTER ISO7816_RESET_RETRY_COUNTER #define CMD_GET_DATA 0xCA #define CMD_PUT_DATA 0xDA #define CMD_MSE 0x22 @@ -95,6 +95,7 @@ map_sw (int sw) 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; + case SW_HOST_NO_KEYPAD: ec = GPG_ERR_NOT_SUPPORTED; break; default: if ((sw & 0x010000)) @@ -124,12 +125,15 @@ iso7816_map_sw (int sw) requested application ID. The function can't be used to enumerate AIDs and won't return the AID on success. The return value is 0 for okay or a GPG error code. Note that ISO error codes are - internally mapped. */ + internally mapped. Bit 0 of FLAGS should be set if the card does + not understand P2=0xC0. */ gpg_error_t -iso7816_select_application (int slot, const char *aid, size_t aidlen) +iso7816_select_application (int slot, const char *aid, size_t aidlen, + unsigned int flags) { int sw; - sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, 0, aidlen, aid); + sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, + (flags&1)? 0 :0x0c, aidlen, aid); return map_sw (sw); } @@ -221,27 +225,59 @@ iso7816_list_directory (int slot, int list_dirs, } +/* Check whether the reader supports the ISO command code COMMAND on + the keypad. Returns 0 on success. */ +gpg_error_t +iso7816_check_keypad (int slot, int command, iso7816_pininfo_t *pininfo) +{ + int sw; + + sw = apdu_check_keypad (slot, command, + pininfo->mode, pininfo->minlen, pininfo->maxlen, + pininfo->padlen); + return map_sw (sw); +} + /* Perform a VERIFY command on SLOT using the card holder verification - vector CHVNO with a CHV of lenght CHVLEN. Returns 0 on success. */ + vector CHVNO with a CHV of lenght CHVLEN. With PININFO non-NULL + the keypad of the reader will be used. Returns 0 on success. */ gpg_error_t -iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen) +iso7816_verify_kp (int slot, int chvno, const char *chv, size_t chvlen, + iso7816_pininfo_t *pininfo) { int sw; - sw = apdu_send_simple (slot, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv); + if (pininfo && pininfo->mode) + sw = apdu_send_simple_kp (slot, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv, + pininfo->mode, + pininfo->minlen, + pininfo->maxlen, + pininfo->padlen); + else + sw = apdu_send_simple (slot, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv); return map_sw (sw); } +/* Perform a VERIFY command on SLOT using the card holder verification + vector CHVNO with a CHV of lenght CHVLEN. Returns 0 on success. */ +gpg_error_t +iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen) +{ + return iso7816_verify_kp (slot, chvno, chv, chvlen, NULL); +} + /* Perform a CHANGE_REFERENCE_DATA command on SLOT for the card holder verification vector CHVNO. If the OLDCHV is NULL (and OLDCHVLEN 0), a "change reference data" is done, otherwise an "exchange reference data". The new reference data is expected in NEWCHV of - length NEWCHVLEN. */ + length NEWCHVLEN. With PININFO non-NULL the keypad of the reader + will be used. */ gpg_error_t -iso7816_change_reference_data (int slot, int chvno, - const char *oldchv, size_t oldchvlen, - const char *newchv, size_t newchvlen) +iso7816_change_reference_data_kp (int slot, int chvno, + const char *oldchv, size_t oldchvlen, + const char *newchv, size_t newchvlen, + iso7816_pininfo_t *pininfo) { int sw; char *buf; @@ -258,28 +294,69 @@ iso7816_change_reference_data (int slot, int chvno, memcpy (buf, oldchv, oldchvlen); memcpy (buf+oldchvlen, newchv, newchvlen); - sw = apdu_send_simple (slot, 0x00, CMD_CHANGE_REFERENCE_DATA, - oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf); + if (pininfo && pininfo->mode) + sw = apdu_send_simple_kp (slot, 0x00, CMD_CHANGE_REFERENCE_DATA, + oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf, + pininfo->mode, + pininfo->minlen, + pininfo->maxlen, + pininfo->padlen); + else + sw = apdu_send_simple (slot, 0x00, CMD_CHANGE_REFERENCE_DATA, + oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf); xfree (buf); return map_sw (sw); } +/* Perform a CHANGE_REFERENCE_DATA command on SLOT for the card holder + verification vector CHVNO. If the OLDCHV is NULL (and OLDCHVLEN + 0), a "change reference data" is done, otherwise an "exchange + reference data". The new reference data is expected in NEWCHV of + length NEWCHVLEN. */ +gpg_error_t +iso7816_change_reference_data (int slot, int chvno, + const char *oldchv, size_t oldchvlen, + const char *newchv, size_t newchvlen) +{ + return iso7816_change_reference_data_kp (slot, chvno, oldchv, oldchvlen, + newchv, newchvlen, NULL); +} + + gpg_error_t -iso7816_reset_retry_counter (int slot, int chvno, - const char *newchv, size_t newchvlen) +iso7816_reset_retry_counter_kp (int slot, int chvno, + const char *newchv, size_t newchvlen, + iso7816_pininfo_t *pininfo) { int sw; if (!newchv || !newchvlen ) return gpg_error (GPG_ERR_INV_VALUE); - sw = apdu_send_simple (slot, 0x00, CMD_RESET_RETRY_COUNTER, - 2, chvno, newchvlen, newchv); + if (pininfo && pininfo->mode) + sw = apdu_send_simple_kp (slot, 0x00, CMD_RESET_RETRY_COUNTER, + 2, chvno, newchvlen, newchv, + pininfo->mode, + pininfo->minlen, + pininfo->maxlen, + pininfo->padlen); + else + sw = apdu_send_simple (slot, 0x00, CMD_RESET_RETRY_COUNTER, + 2, chvno, newchvlen, newchv); return map_sw (sw); } +gpg_error_t +iso7816_reset_retry_counter (int slot, int chvno, + const char *newchv, size_t newchvlen) +{ + return iso7816_reset_retry_counter_kp (slot, chvno, newchv, newchvlen, NULL); +} + + + /* Perform a GET DATA command requesting TAG and storing the result in a newly allocated buffer at the address passed by RESULT. Return the length of this data at the address of RESULTLEN. */ diff --git a/scd/iso7816.h b/scd/iso7816.h index 04c7ae63e..8f7907405 100644 --- a/scd/iso7816.h +++ b/scd/iso7816.h @@ -28,10 +28,30 @@ #include "cardglue.h" #endif +/* Command codes used by iso7816_check_keypad. */ +#define ISO7816_VERIFY 0x20 +#define ISO7816_CHANGE_REFERENCE_DATA 0x24 +#define ISO7816_RESET_RETRY_COUNTER 0x2C + + +/* Information to be passed to keypad equipped readers. See + ccid-driver.c for details. */ +struct iso7816_pininfo_s +{ + int mode; /* A mode of 0 means: Do not use the keypad. */ + int minlen; + int maxlen; + int padlen; + int padchar; +}; +typedef struct iso7816_pininfo_s iso7816_pininfo_t; + + gpg_error_t iso7816_map_sw (int sw); gpg_error_t iso7816_select_application (int slot, - const char *aid, size_t aidlen); + const char *aid, size_t aidlen, + unsigned int flags); gpg_error_t iso7816_select_file (int slot, int tag, int is_dir, unsigned char **result, size_t *resultlen); gpg_error_t iso7816_select_path (int slot, @@ -39,13 +59,26 @@ gpg_error_t iso7816_select_path (int slot, unsigned char **result, size_t *resultlen); gpg_error_t iso7816_list_directory (int slot, int list_dirs, unsigned char **result, size_t *resultlen); +gpg_error_t iso7816_check_keypad (int slot, int command, + iso7816_pininfo_t *pininfo); gpg_error_t iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen); +gpg_error_t iso7816_verify_kp (int slot, + int chvno, const char *chv, size_t chvlen, + iso7816_pininfo_t *pininfo); gpg_error_t iso7816_change_reference_data (int slot, int chvno, const char *oldchv, size_t oldchvlen, const char *newchv, size_t newchvlen); +gpg_error_t iso7816_change_reference_data_kp (int slot, int chvno, + const char *oldchv, size_t oldchvlen, + const char *newchv, size_t newchvlen, + iso7816_pininfo_t *pininfo); gpg_error_t iso7816_reset_retry_counter (int slot, int chvno, const char *newchv, size_t newchvlen); +gpg_error_t iso7816_reset_retry_counter_kp (int slot, int chvno, + const char *newchv, + size_t newchvlen, + iso7816_pininfo_t *pininfo); gpg_error_t iso7816_get_data (int slot, int tag, unsigned char **result, size_t *resultlen); gpg_error_t iso7816_put_data (int slot, int tag, diff --git a/scd/scdaemon.c b/scd/scdaemon.c index c6995abcc..56c0d7600 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -84,6 +84,7 @@ enum cmd_and_opt_values opcscDriver, oDisableCCID, oDisableOpenSC, + oDisableKeypad, oAllowAdmin, oDenyAdmin, oDisableApplication, @@ -126,6 +127,7 @@ static ARGPARSE_OPTS opts[] = { "@" #endif /* end --disable-ccid */}, + { oDisableKeypad, "disable-keypad", 0, N_("do not use a reader's keypad")}, { oAllowAdmin, "allow-admin", 0, N_("allow the use of admin card commands")}, { oDenyAdmin, "deny-admin", 0, "@" }, { oDisableApplication, "disable-application", 2, "@"}, @@ -135,7 +137,7 @@ static ARGPARSE_OPTS opts[] = { /* The card dirver we use by default for PC/SC. */ -#ifdef HAVE_W32_SYSTEM +#if defined(HAVE_W32_SYSTEM) || defined(__CYGWIN__) #define DEFAULT_PCSC_DRIVER "winscard.dll" #else #define DEFAULT_PCSC_DRIVER "libpcsclite.so" @@ -489,6 +491,8 @@ main (int argc, char **argv ) case oDisableCCID: opt.disable_ccid = 1; break; case oDisableOpenSC: break; + case oDisableKeypad: opt.disable_keypad = 1; break; + case oAllowAdmin: opt.allow_admin = 1; break; case oDenyAdmin: opt.allow_admin = 0; break; diff --git a/scd/scdaemon.h b/scd/scdaemon.h index 54566b6ad..abe9730a7 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -39,21 +39,22 @@ #define MAX_DIGEST_LEN 24 -/* A large struct name "opt" to keep global flags */ +/* A large struct name "opt" to keep global flags. */ struct { - unsigned int debug; /* debug flags (DBG_foo_VALUE) */ - int verbose; /* verbosity level */ - int quiet; /* be as quiet as possible */ - int dry_run; /* don't change any persistent data */ - int batch; /* batch mode */ - const char *homedir; /* configuration directory name */ + unsigned int debug; /* Debug flags (DBG_foo_VALUE). */ + int verbose; /* Verbosity level. */ + int quiet; /* Be as quiet as possible. */ + int dry_run; /* Don't change any persistent data. */ + int batch; /* Batch mode. */ + const char *homedir; /* Configuration directory name. */ const char *ctapi_driver; /* Library to access the ctAPI. */ const char *pcsc_driver; /* Library to access the PC/SC system. */ const char *reader_port; /* NULL or reder port to use. */ int disable_ccid; /* Disable the use of the internal CCID driver. */ + int disable_keypad; /* Do not use a keypad. */ int allow_admin; /* Allow the use of admin commands for certain cards. */ - strlist_t disabled_applications; /* card applications we do not + strlist_t disabled_applications; /* Card applications we do not want to use. */ } opt; diff --git a/tools/ChangeLog b/tools/ChangeLog index ba4f0ec27..a57a0bccf 100644 --- a/tools/ChangeLog +++ b/tools/ChangeLog @@ -1,3 +1,11 @@ +2005-10-19 Werner Koch + + * gpgconf-comp.c (gc_options_scdaemon): New option --disable-keypad. + +2005-09-22 Werner Koch + + * rfc822parse.c (parse_field): Tread Content-Disposition special. + 2005-10-08 Marcus Brinkmann * Makefile.am (watchgnupg_LDADD): New variable. diff --git a/tools/gpgconf-comp.c b/tools/gpgconf-comp.c index e8d9ca27e..497707532 100644 --- a/tools/gpgconf-comp.c +++ b/tools/gpgconf-comp.c @@ -528,7 +528,9 @@ static gc_option_t gc_options_scdaemon[] = { "disable-ccid", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT, "gnupg", "do not use the internal CCID driver", GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON }, - + { "disable-keypad", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + "gnupg", "do not use a reader's keypad", + GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON }, { "Debug", GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED, @@ -2447,7 +2449,8 @@ gc_component_change_options (int component, FILE *in) { #ifdef HAVE_W32_SYSTEM /* FIXME: Won't work becuase W32 doesn't silently - overwrite. */ + overwrite. Fix it by creating a backup copy and + deliting the orginal file first. */ err = rename (src_pathname[i], dest_pathname[i]); #else /*!HAVE_W32_SYSTEM*/ /* This is a bit safer than rename() because we diff --git a/tools/rfc822parse.c b/tools/rfc822parse.c index 61377e7e6..df3b2e7a4 100644 --- a/tools/rfc822parse.c +++ b/tools/rfc822parse.c @@ -766,6 +766,7 @@ parse_field (HDR_LINE hdr) } tspecial_header[] = { { "Content-Type", 12}, { "Content-Transfer-Encoding", 25}, + { "Content-Disposition", 19}, { NULL, 0} }; const char *delimiters; -- cgit From c664309a0a9c2cd0aa0b1e10b488818ae7496f60 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 11 Apr 2006 13:53:21 +0000 Subject: Added command APDU --- doc/scdaemon.texi | 46 ++++++++++++++++++++ scd/ChangeLog | 9 ++++ scd/apdu.c | 4 +- scd/app-openpgp.c | 4 +- scd/command.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ scd/scdaemon.c | 2 + 6 files changed, 189 insertions(+), 4 deletions(-) (limited to 'scd/scdaemon.c') diff --git a/doc/scdaemon.texi b/doc/scdaemon.texi index 24a22355e..847001669 100644 --- a/doc/scdaemon.texi +++ b/doc/scdaemon.texi @@ -347,6 +347,8 @@ syncronizing access to a token between sessions. * Scdaemon RANDOM:: Return random bytes generate on-card. * Scdaemon PASSWD:: Change PINs. * Scdaemon CHECKPIN:: Perform a VERIFY operation. +* Scdaemon RESTART:: Restart connection +* Scdaemon APDU:: Send a verbatim APDU to the card @end menu @node Scdaemon SERIALNO @@ -553,3 +555,47 @@ and only if the retry counter is still at 3. @end table + +@node Scdaemon RESTART +@subsection Perform a RESTART operation. + +@example + RESTART +@end example + +Restart the current connection; this is a kind of warm reset. It +deletes the context used by this connection but does not actually +reset the card. + +This is used by gpg-agent to reuse a primary pipe connection and +may be used by clients to backup from a conflict in the serial +command; i.e. to select another application. + + + + +@node Scdaemon APDU +@subsection Send a verbatim APDU to the card. + +@example + APDU [--atr] [--more] [@var{hexstring}] +@end example + + +Send an APDU to the current reader. This command bypasses the high +level functions and sends the data directly to the card. +@var{hexstring} is expected to be a proper APDU. If @var{hexstring} is +not given no commands are send to the card; However the command will +implictly check whether the card is ready for use. + +Using the option @code{--atr} returns the ATR of the card as a status +message before any data like this: +@example + S CARD-ATR 3BFA1300FF813180450031C173C00100009000B1 +@end example + +Using the option @code{--more} handles the card status word MORE_DATA +(61xx) and concatenate all reponses to one block. + + + diff --git a/scd/ChangeLog b/scd/ChangeLog index 27c362d10..53ef676f9 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,12 @@ +2006-04-11 Werner Koch + + * command.c (hex_to_buffer): New. + (cmd_apdu): New. + +2006-04-03 Werner Koch + + * scdaemon.c [__GLIBC__]: Default to libpcsclite.so.1. + 2006-03-21 Werner Koch * command.c (cmd_pksign): Add --hash option. diff --git a/scd/apdu.c b/scd/apdu.c index adaaec612..d6bbdefd5 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -2848,8 +2848,8 @@ apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1, 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.*/ + on success. If the slot is locked, the function returns + immediately with an error. */ int apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen, int handle_more, diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index fff709a08..5e9281a38 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -112,8 +112,8 @@ struct app_local_s { encoded S-expression encoding a public key. Might be NULL if key is not available. */ - size_t keylen; /* The length of the above S-expression. Thsi - is usullay only required for corss checks + size_t keylen; /* The length of the above S-expression. This + is usullay only required for cross checks because the length of an S-expression is implicitly available. */ } pk[3]; diff --git a/scd/command.c b/scd/command.c index 70a426959..2ed685587 100644 --- a/scd/command.c +++ b/scd/command.c @@ -156,6 +156,38 @@ has_option (const char *line, const char *name) } +/* Convert the STRING into a newly allocated buffer while translating + the hex numbers. Stops at the first invalid character. Blanks and + colons are allowed to separate the hex digits. Returns NULL on + error or a newly malloced buffer and its length in LENGTH. */ +static unsigned char * +hex_to_buffer (const char *string, size_t *r_length) +{ + unsigned char *buffer; + const char *s; + size_t n; + + buffer = xtrymalloc (strlen (string)+1); + if (!buffer) + return NULL; + for (s=string, n=0; *s; s++) + { + if (spacep (s) || *s == ':') + continue; + if (hexdigitp (s) && hexdigitp (s+1)) + { + buffer[n++] = xtoi_2 (s); + s++; + } + else + break; + } + *r_length = n; + return buffer; +} + + + /* Reset the card and free the application context. With SEND_RESET set to true actually send a RESET to the reader. */ static void @@ -1372,6 +1404,101 @@ cmd_restart (assuan_context_t ctx, char *line) } +/* APDU [--atr] [--more] [hexstring] + + Send an APDU to the current reader. This command bypasses the high + level functions and sends the data directly to the card. HEXSTRING + is expected to be a proper APDU. If HEXSTRING is not given no + commands are set to the card but the command will implictly check + whether the card is ready for use. + + Using the option "--atr" returns the ATR of the card as a status + message before any data like this: + S CARD-ATR 3BFA1300FF813180450031C173C00100009000B1 + + Using the option --more handles the card status word MORE_DATA + (61xx) and concatenate all reponses to one block. + + */ +static int +cmd_apdu (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + int rc; + int rc_is_assuan = 0; + unsigned char *apdu; + size_t apdulen; + int with_atr; + int handle_more; + + with_atr = has_option (line, "--atr"); + handle_more = has_option (line, "--more"); + + /* Skip over options. */ + while ( *line == '-' && line[1] == '-' ) + { + while (*line && !spacep (line)) + line++; + while (spacep (line)) + line++; + } + + if ( IS_LOCKED (ctrl) ) + return gpg_error (GPG_ERR_LOCKED); + + if ((rc = open_card (ctrl, NULL))) + return rc; + + if (with_atr) + { + unsigned char *atr; + size_t atrlen; + int i; + char hexbuf[400]; + + atr = apdu_get_atr (ctrl->reader_slot, &atrlen); + if (!atr || atrlen > sizeof hexbuf - 2 ) + { + rc = gpg_error (GPG_ERR_INV_CARD); + goto leave; + } + for (i=0; i < atrlen; i++) + sprintf (hexbuf+2*i, "%02X", atr[i]); + xfree (atr); + send_status_info (ctrl, "CARD-ATR", hexbuf, strlen (hexbuf), NULL, 0); + } + + apdu = hex_to_buffer (line, &apdulen); + if (!apdu) + { + rc = gpg_error_from_errno (errno); + goto leave; + } + if (apdulen) + { + unsigned char *result = NULL; + size_t resultlen; + + rc = apdu_send_direct (ctrl->reader_slot, apdu, apdulen, handle_more, + &result, &resultlen); + if (rc) + log_error ("apdu_send_direct failed: %s\n", gpg_strerror (rc)); + else + { + rc_is_assuan = 1; + rc = assuan_send_data (ctx, result, resultlen); + xfree (result); + } + } + xfree (apdu); + + leave: + TEST_CARD_REMOVAL (ctrl, rc); + return rc_is_assuan? rc : map_to_assuan_status (rc); +} + + + /* Tell the assuan library about our commands */ @@ -1403,6 +1530,7 @@ register_commands (assuan_context_t ctx) { "UNLOCK", cmd_unlock }, { "GETINFO", cmd_getinfo }, { "RESTART", cmd_restart }, + { "APDU", cmd_apdu }, { NULL } }; int i, rc; diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 56c0d7600..e24b42132 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -139,6 +139,8 @@ static ARGPARSE_OPTS opts[] = { /* The card dirver we use by default for PC/SC. */ #if defined(HAVE_W32_SYSTEM) || defined(__CYGWIN__) #define DEFAULT_PCSC_DRIVER "winscard.dll" +#elif defined(__GLIBC__) +#define DEFAULT_PCSC_DRIVER "libpcsclite.so.1" #else #define DEFAULT_PCSC_DRIVER "libpcsclite.so" #endif -- cgit From f98537733ac96fd7e786286944fd3c2696229c4f Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 20 Jun 2006 17:21:37 +0000 Subject: Updated FSF's address. --- Makefile.am | 3 ++- NEWS | 3 +++ TODO | 5 ++--- agent/Makefile.am | 3 ++- agent/agent.h | 3 ++- agent/cache.c | 3 ++- agent/call-scd.c | 3 ++- agent/command-ssh.c | 4 ++-- agent/command.c | 3 ++- agent/divert-scd.c | 3 ++- agent/findkey.c | 3 ++- agent/genkey.c | 3 ++- agent/gpg-agent.c | 3 ++- agent/learncard.c | 3 ++- agent/minip12.c | 3 ++- agent/minip12.h | 3 ++- agent/pkdecrypt.c | 3 ++- agent/pksign.c | 3 ++- agent/preset-passphrase.c | 3 ++- agent/protect-tool.c | 3 ++- agent/protect.c | 3 ++- agent/query.c | 3 ++- agent/t-protect.c | 3 ++- agent/trans.c | 3 ++- agent/trustlist.c | 3 ++- am/cmacros.am | 3 ++- common/Makefile.am | 3 ++- common/asshelp.c | 3 ++- common/asshelp.h | 3 ++- common/b64enc.c | 3 ++- common/dynload.h | 3 ++- common/errors.h | 3 ++- common/estream.c | 37 +++++++++++++++++++------------------ common/estream.h | 37 +++++++++++++++++++------------------ common/exechelp.c | 3 ++- common/exechelp.h | 3 ++- common/gettime.c | 3 ++- common/homedir.c | 3 ++- common/i18n.h | 3 ++- common/iobuf.c | 3 ++- common/iobuf.h | 3 ++- common/isascii.c | 3 ++- common/maperror.c | 3 ++- common/membuf.c | 3 ++- common/membuf.h | 3 ++- common/miscellaneous.c | 3 ++- common/mkerrors | 3 ++- common/mkerrtok | 3 ++- common/sexp-parse.h | 3 ++- common/sexputil.c | 3 ++- common/signal.c | 3 ++- common/simple-gettext.c | 3 ++- common/simple-pwquery.c | 3 ++- common/simple-pwquery.h | 3 ++- common/sysutils.c | 3 ++- common/sysutils.h | 3 ++- common/ttyio.c | 3 ++- common/ttyio.h | 3 ++- common/util.h | 3 ++- common/vasprintf.c | 4 ++-- common/w32reg.c | 3 ++- common/xasprintf.c | 3 ++- common/xreadline.c | 3 ++- common/yesno.c | 3 ++- configure.ac | 3 ++- doc/Makefile.am | 3 ++- doc/gnupg-card-architecture.fig | 3 ++- g10/Makefile.am | 3 ++- g10/call-agent.c | 3 ++- g10/call-agent.h | 3 ++- g10/comment.c | 3 ++- g10/gpg.c | 9 +++++++++ g10/gpg.h | 3 ++- g10/pkglue.c | 3 ++- g10/pkglue.h | 3 ++- include/_regex.h | 4 ++-- include/errors.h | 3 ++- include/memory.h | 3 ++- include/mpi.h | 3 ++- include/util.h | 3 ++- jnlib/Makefile.am | 3 ++- jnlib/argparse.c | 30 ++++++++++++++++-------------- jnlib/argparse.h | 3 ++- jnlib/dotlock.c | 3 ++- jnlib/dotlock.h | 3 ++- jnlib/libjnlib-config.h | 3 ++- jnlib/logging.c | 3 ++- jnlib/logging.h | 3 ++- jnlib/mischelp.h | 3 ++- jnlib/stringhelp.c | 3 ++- jnlib/stringhelp.h | 3 ++- jnlib/strlist.c | 3 ++- jnlib/strlist.h | 3 ++- jnlib/types.h | 3 ++- jnlib/utf8conv.c | 3 ++- jnlib/utf8conv.h | 3 ++- jnlib/w32-afunix.c | 3 ++- jnlib/w32-afunix.h | 3 ++- jnlib/w32-pth.c | 3 ++- jnlib/w32-pth.h | 3 ++- jnlib/xmalloc.c | 3 ++- jnlib/xmalloc.h | 3 ++- kbx/Makefile.am | 3 ++- kbx/kbxutil.c | 3 ++- kbx/keybox-blob.c | 3 ++- kbx/keybox-defs.h | 3 ++- kbx/keybox-dump.c | 3 ++- kbx/keybox-file.c | 3 ++- kbx/keybox-init.c | 3 ++- kbx/keybox-openpgp.c | 3 ++- kbx/keybox-search-desc.h | 3 ++- kbx/keybox-search.c | 3 ++- kbx/keybox-update.c | 3 ++- kbx/keybox-util.c | 3 ++- kbx/keybox.h | 3 ++- kbx/mkerrors | 3 ++- scd/Makefile.am | 3 ++- scd/app-common.h | 3 ++- scd/app-dinsig.c | 3 ++- scd/app-help.c | 3 ++- scd/app-nks.c | 3 ++- scd/app-openpgp.c | 3 ++- scd/app-p15.c | 3 ++- scd/app.c | 3 ++- scd/atr.c | 3 ++- scd/atr.h | 3 ++- scd/card-common.h | 3 ++- scd/card-dinsig.c | 3 ++- scd/card-p15.c | 3 ++- scd/card.c | 3 ++- scd/command.c | 3 ++- scd/sc-copykeys.c | 3 ++- scd/scdaemon.c | 3 ++- scd/scdaemon.h | 3 ++- scd/tlv.c | 3 ++- scd/tlv.h | 3 ++- scripts/compile | 3 ++- scripts/config.guess | 3 ++- sm/ChangeLog | 7 +++++++ sm/Makefile.am | 3 ++- sm/base64.c | 3 ++- sm/call-agent.c | 3 ++- sm/call-dirmngr.c | 3 ++- sm/certchain.c | 3 ++- sm/certcheck.c | 3 ++- sm/certdump.c | 3 ++- sm/certlist.c | 3 ++- sm/certreqgen.c | 3 ++- sm/decrypt.c | 3 ++- sm/delete.c | 3 ++- sm/encrypt.c | 3 ++- sm/export.c | 3 ++- sm/fingerprint.c | 3 ++- sm/gpgsm.c | 16 ++++++++++++---- sm/gpgsm.h | 3 ++- sm/import.c | 3 ++- sm/keydb.c | 3 ++- sm/keydb.h | 3 ++- sm/keylist.c | 7 ++++++- sm/misc.c | 3 ++- sm/qualified.c | 3 ++- sm/server.c | 3 ++- sm/sign.c | 3 ++- sm/verify.c | 3 ++- tests/Makefile.am | 3 ++- tests/asschk.c | 3 ++- tests/pkits/Makefile.am | 3 ++- tests/pkits/common.sh | 3 ++- tests/pkits/import-all-certs | 3 ++- tests/pkits/validate-all-certs | 3 ++- tools/Makefile.am | 3 ++- tools/gpg-connect-agent.c | 3 ++- tools/gpgconf-comp.c | 36 +++++++++++++++++++----------------- tools/gpgconf.c | 3 ++- tools/gpgconf.h | 3 ++- tools/gpgkey2ssh.c | 37 +++++++++++++++++++------------------ tools/gpgparsemail.c | 3 ++- tools/no-libgcrypt.c | 3 ++- tools/symcryptrun.c | 3 ++- tools/watchgnupg.c | 3 ++- 180 files changed, 469 insertions(+), 265 deletions(-) (limited to 'scd/scdaemon.c') diff --git a/Makefile.am b/Makefile.am index 9fafb1102..0c5fbe4c3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,7 +15,8 @@ # # 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 +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. ## Process this file with automake to produce Makefile.in diff --git a/NEWS b/NEWS index 6413242c6..679bf7d5b 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,9 @@ Noteworthy changes in version 1.9.21 * [scdaemon] Added --hash=xxx option to the PKSIGN command. + * [gpg-protect-tool] Does now create a MAC for P12 files. This is for + better interoperability. + Noteworthy changes in version 1.9.20 (2005-12-20) ------------------------------------------------- diff --git a/TODO b/TODO index 7958ed18e..da3a76e06 100644 --- a/TODO +++ b/TODO @@ -21,7 +21,7 @@ might want to have an agent context for each service request ** When a certificate chain was sucessfully verified, make ephemeral certs used in this chain permanent. ** Try to keep certificate references somewhere This will help with some of our caching code. We also need to test - that cachining; in particular "regtp_ca_chainlen". + that caching; in particular "regtp_ca_chainlen". * sm/decrypt.c ** replace leading zero in integer hack by a cleaner solution @@ -101,7 +101,6 @@ might want to have an agent context for each service request * sm/ -** --include-certs is as of now still a dummy command line option ** check that we issue NO_SECKEY xxx if a -u key was not found * gpg/ @@ -117,4 +116,4 @@ might want to have an agent context for each service request ** ttyio Add completion support. ** yesno - Update to gpg 1.4.3 version \ No newline at end of file + Update to gpg 1.4.3 version diff --git a/agent/Makefile.am b/agent/Makefile.am index bc96531e0..961f0bb97 100644 --- a/agent/Makefile.am +++ b/agent/Makefile.am @@ -14,7 +14,8 @@ # # 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 +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. ## Process this file with automake to produce Makefile.in diff --git a/agent/agent.h b/agent/agent.h index 1542d6b9f..fdfe510fb 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef AGENT_H diff --git a/agent/cache.c b/agent/cache.c index 32b6ac0c7..2f468396d 100644 --- a/agent/cache.c +++ b/agent/cache.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/agent/call-scd.c b/agent/call-scd.c index ff241ce41..d0d24f9d5 100644 --- a/agent/call-scd.c +++ b/agent/call-scd.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/agent/command-ssh.c b/agent/command-ssh.c index 23f083c2f..18375a9ae 100644 --- a/agent/command-ssh.c +++ b/agent/command-ssh.c @@ -15,8 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ /* Only v2 of the ssh-agent protocol is implemented. */ diff --git a/agent/command.c b/agent/command.c index daf9b8698..12770dac8 100644 --- a/agent/command.c +++ b/agent/command.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ /* FIXME: we should not use the default assuan buffering but setup diff --git a/agent/divert-scd.c b/agent/divert-scd.c index 926df2622..3dc7984e6 100644 --- a/agent/divert-scd.c +++ b/agent/divert-scd.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/agent/findkey.c b/agent/findkey.c index 73ffb530d..3f793e5dd 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/agent/genkey.c b/agent/genkey.c index d0319f7b4..04ee865f4 100644 --- a/agent/genkey.c +++ b/agent/genkey.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 22bd5589d..fc2a2001a 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -16,7 +16,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/agent/learncard.c b/agent/learncard.c index 72238507f..8ddf4ee54 100644 --- a/agent/learncard.c +++ b/agent/learncard.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/agent/minip12.c b/agent/minip12.c index 6f99bf24d..912d387d8 100644 --- a/agent/minip12.c +++ b/agent/minip12.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifdef HAVE_CONFIG_H diff --git a/agent/minip12.h b/agent/minip12.h index 2fbb490d7..6275f9ccb 100644 --- a/agent/minip12.h +++ b/agent/minip12.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef MINIP12_H diff --git a/agent/pkdecrypt.c b/agent/pkdecrypt.c index 1d64c1b15..f61f0f844 100644 --- a/agent/pkdecrypt.c +++ b/agent/pkdecrypt.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/agent/pksign.c b/agent/pksign.c index e9df19351..9863f9de0 100644 --- a/agent/pksign.c +++ b/agent/pksign.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/agent/preset-passphrase.c b/agent/preset-passphrase.c index 6a9f07a3e..013c9411d 100644 --- a/agent/preset-passphrase.c +++ b/agent/preset-passphrase.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/agent/protect-tool.c b/agent/protect-tool.c index 5f59d5e06..bb14ca1e1 100644 --- a/agent/protect-tool.c +++ b/agent/protect-tool.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/agent/protect.c b/agent/protect.c index 45bdae496..19f6ccbc6 100644 --- a/agent/protect.c +++ b/agent/protect.c @@ -16,7 +16,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/agent/query.c b/agent/query.c index a5a3d0153..0516bec03 100644 --- a/agent/query.c +++ b/agent/query.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/agent/t-protect.c b/agent/t-protect.c index fee3c561d..9ddd49414 100644 --- a/agent/t-protect.c +++ b/agent/t-protect.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/agent/trans.c b/agent/trans.c index 7fa5e3d6b..5eb7d25c0 100644 --- a/agent/trans.c +++ b/agent/trans.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ /* To avoid any problems with the gettext implementation (there used diff --git a/agent/trustlist.c b/agent/trustlist.c index edb00650d..d234af692 100644 --- a/agent/trustlist.c +++ b/agent/trustlist.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/am/cmacros.am b/am/cmacros.am index de68b6f31..7b449e2c0 100644 --- a/am/cmacros.am +++ b/am/cmacros.am @@ -15,7 +15,8 @@ # # 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 +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. localedir = $(datadir)/locale diff --git a/common/Makefile.am b/common/Makefile.am index 34819e93f..085440bb3 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -15,7 +15,8 @@ # # 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 +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. ## Process this file with automake to produce Makefile.in diff --git a/common/asshelp.c b/common/asshelp.c index 0edaeae0e..1da899522 100644 --- a/common/asshelp.c +++ b/common/asshelp.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/common/asshelp.h b/common/asshelp.h index 2d6dc79e6..9f4b5806b 100644 --- a/common/asshelp.h +++ b/common/asshelp.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef GNUPG_COMMON_ASSHELP_H diff --git a/common/b64enc.c b/common/b64enc.c index 5b7a42ab3..bfc49deb6 100644 --- a/common/b64enc.c +++ b/common/b64enc.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/common/dynload.h b/common/dynload.h index 2c074141f..9b67fa9ed 100644 --- a/common/dynload.h +++ b/common/dynload.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef GNUPG_DYNLOAD_H diff --git a/common/errors.h b/common/errors.h index f34f3ba79..131891f65 100644 --- a/common/errors.h +++ b/common/errors.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef GNUPG_COMMON_ERRORS_H diff --git a/common/estream.c b/common/estream.c index 70b3d9c6e..c2030371b 100644 --- a/common/estream.c +++ b/common/estream.c @@ -1,22 +1,23 @@ /* estream.c - Extended stream I/O/ Library - Copyright (C) 2004 g10 Code GmbH - - This file is part of Libestream. - - Libestream 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. - - Libestream 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Libestream; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. */ + * Copyright (C) 2004 g10 Code GmbH + * + * This file is part of Libestream. + * + * Libestream 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. + * + * Libestream 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Libestream; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ #ifdef USE_ESTREAM_SUPPORT_H # include diff --git a/common/estream.h b/common/estream.h index ebe575926..a9b4847c8 100644 --- a/common/estream.h +++ b/common/estream.h @@ -1,22 +1,23 @@ /* estream.h - Extended stream I/O/ Library - Copyright (C) 2004 g10 Code GmbH - - This file is part of Libestream. - - Libestream 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. - - Libestream 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Libestream; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. */ + * Copyright (C) 2004 g10 Code GmbH + * + * This file is part of Libestream. + * + * Libestream 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. + * + * Libestream 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Libestream; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ #ifndef ESTREAM_H #define ESTREAM_H diff --git a/common/exechelp.c b/common/exechelp.c index dc0a6b0e1..e64b69022 100644 --- a/common/exechelp.c +++ b/common/exechelp.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/common/exechelp.h b/common/exechelp.h index f00d18dd8..1df029b7e 100644 --- a/common/exechelp.h +++ b/common/exechelp.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef GNUPG_COMMON_EXECHELP_H diff --git a/common/gettime.c b/common/gettime.c index ecdc7df95..c4ea3283a 100644 --- a/common/gettime.c +++ b/common/gettime.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/common/homedir.c b/common/homedir.c index a118cbac1..39d6dce20 100644 --- a/common/homedir.c +++ b/common/homedir.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/common/i18n.h b/common/i18n.h index 0e13dca4d..0187ba265 100644 --- a/common/i18n.h +++ b/common/i18n.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef GNUPG_COMMON_I18N_H diff --git a/common/iobuf.c b/common/iobuf.c index bbb666f67..8f7374f8c 100644 --- a/common/iobuf.c +++ b/common/iobuf.c @@ -16,7 +16,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/common/iobuf.h b/common/iobuf.h index 3b8f4b572..a3dd7f1c5 100644 --- a/common/iobuf.h +++ b/common/iobuf.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef GNUPG_COMMON_IOBUF_H diff --git a/common/isascii.c b/common/isascii.c index 565c71664..b71febe99 100644 --- a/common/isascii.c +++ b/common/isascii.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifdef HAVE_CONFIG_H diff --git a/common/maperror.c b/common/maperror.c index 9efd64338..06546b501 100644 --- a/common/maperror.c +++ b/common/maperror.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/common/membuf.c b/common/membuf.c index 75f6bdb2a..2d35fefab 100644 --- a/common/membuf.c +++ b/common/membuf.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/common/membuf.h b/common/membuf.h index c199363cc..9033be61e 100644 --- a/common/membuf.h +++ b/common/membuf.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef GNUPG_COMMON_MEMBUF_H diff --git a/common/miscellaneous.c b/common/miscellaneous.c index e9f8ed27f..da74f65bc 100644 --- a/common/miscellaneous.c +++ b/common/miscellaneous.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/common/mkerrors b/common/mkerrors index 5a1ef33da..994c61352 100755 --- a/common/mkerrors +++ b/common/mkerrors @@ -17,7 +17,8 @@ # # 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 +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. cat < diff --git a/common/simple-gettext.c b/common/simple-gettext.c index b6b851c77..56a305fd8 100644 --- a/common/simple-gettext.c +++ b/common/simple-gettext.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ /* This is a simplified version of gettext written by Ulrich Drepper. diff --git a/common/simple-pwquery.c b/common/simple-pwquery.c index f156ca3f1..e405c1ec0 100644 --- a/common/simple-pwquery.c +++ b/common/simple-pwquery.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ /* This module is intended as a standalone client implementation to diff --git a/common/simple-pwquery.h b/common/simple-pwquery.h index e3270d6c5..5b941d06f 100644 --- a/common/simple-pwquery.h +++ b/common/simple-pwquery.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef SIMPLE_PWQUERY_H diff --git a/common/sysutils.c b/common/sysutils.c index a8f6f6f5d..3e52cdaa3 100644 --- a/common/sysutils.c +++ b/common/sysutils.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/common/sysutils.h b/common/sysutils.h index 08198f685..c40dbfaa9 100644 --- a/common/sysutils.h +++ b/common/sysutils.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef GNUPG_COMMON_SYSUTILS_H diff --git a/common/ttyio.c b/common/ttyio.c index c9f41c626..38883afa5 100644 --- a/common/ttyio.c +++ b/common/ttyio.c @@ -16,7 +16,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/common/ttyio.h b/common/ttyio.h index 6148d644a..32d159863 100644 --- a/common/ttyio.h +++ b/common/ttyio.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef GNUPG_COMMON_TTYIO_H #define GNUPG_COMMON_TTYIO_H diff --git a/common/util.h b/common/util.h index 295d785c5..29106bf9c 100644 --- a/common/util.h +++ b/common/util.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef GNUPG_COMMON_UTIL_H diff --git a/common/vasprintf.c b/common/vasprintf.c index 9efea33f2..4bed8de66 100644 --- a/common/vasprintf.c +++ b/common/vasprintf.c @@ -15,8 +15,8 @@ Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with libiberty; see the file COPYING.LIB. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ +not, write to the Free Software Foundation, Inc., 51 Franklin Street, +Fifth Floor, Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include diff --git a/common/w32reg.c b/common/w32reg.c index a85ac7348..84308e916 100644 --- a/common/w32reg.c +++ b/common/w32reg.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/common/xasprintf.c b/common/xasprintf.c index 46740a2e6..75ae18072 100644 --- a/common/xasprintf.c +++ b/common/xasprintf.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/common/xreadline.c b/common/xreadline.c index 23aa35269..8400df330 100644 --- a/common/xreadline.c +++ b/common/xreadline.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/common/yesno.c b/common/yesno.c index 737071691..9ca513740 100644 --- a/common/yesno.c +++ b/common/yesno.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/configure.ac b/configure.ac index f3066a0a9..d77093a63 100644 --- a/configure.ac +++ b/configure.ac @@ -16,7 +16,8 @@ # # 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 +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. # Process this file with autoconf to produce a configure script. AC_PREREQ(2.52) diff --git a/doc/Makefile.am b/doc/Makefile.am index 47dd36208..dae053ec2 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -14,7 +14,8 @@ # # 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 +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. ## Process this file with automake to produce Makefile.in diff --git a/doc/gnupg-card-architecture.fig b/doc/gnupg-card-architecture.fig index e5772cd0f..49351c720 100644 --- a/doc/gnupg-card-architecture.fig +++ b/doc/gnupg-card-architecture.fig @@ -15,7 +15,8 @@ # # 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 +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. Landscape Center Metric diff --git a/g10/Makefile.am b/g10/Makefile.am index aeb24d7b3..fb54dd9f0 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -15,7 +15,8 @@ # # 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 +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. ## Process this file with automake to produce Makefile.in diff --git a/g10/call-agent.c b/g10/call-agent.c index 55fc62569..e3bd7ed57 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #if 0 /* let Emacs display a red warning */ diff --git a/g10/call-agent.h b/g10/call-agent.h index 71044e38b..d09b87e3a 100644 --- a/g10/call-agent.h +++ b/g10/call-agent.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef GNUPG_G10_CALL_AGENT_H #define GNUPG_G10_CALL_AGENT_H diff --git a/g10/comment.c b/g10/comment.c index b52104cd7..193087107 100644 --- a/g10/comment.c +++ b/g10/comment.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/g10/gpg.c b/g10/gpg.c index 52ae553c1..4235f3f7a 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -356,6 +356,7 @@ enum cmd_and_opt_values oAllowMultisigVerification, oEnableDSA2, oDisableDSA2, + oDebugAllowRun, oNoop }; @@ -701,6 +702,8 @@ static ARGPARSE_OPTS opts[] = { { oNoRequireCrossCert, "no-require-cross-certification", 0, "@"}, { oAutoKeyLocate, "auto-key-locate", 2, "@"}, { oNoAutoKeyLocate, "no-auto-key-locate", 0, "@"}, + + { oDebugAllowRun, "debug_allow_run", 0, "@"}, {0,NULL,0,NULL} }; @@ -1684,6 +1687,7 @@ main (int argc, char **argv ) int with_fpr = 0; /* make an option out of --fingerprint */ int any_explicit_recipient = 0; int require_secmem=0,got_secmem=0; + int allow_run = 0; #ifdef __riscos__ opt.lock_once = 1; @@ -2663,6 +2667,8 @@ main (int argc, char **argv ) case oEnableDSA2: opt.flags.dsa2=1; break; case oDisableDSA2: opt.flags.dsa2=0; break; + case oDebugAllowRun: allow_run = 1; break; + case oNoop: break; default : pargs.err = configfp? 1:2; break; @@ -2716,6 +2722,9 @@ main (int argc, char **argv ) } #endif + if (!allow_run) + log_fatal ("This version of gpg is not ready for use, use gpg 1.4.x\n"); + /* FIXME: We should use logging to a file only in server mode; however we have not yet implemetyed that. Thus we try to get away with --batch as indication for logging to file diff --git a/g10/gpg.h b/g10/gpg.h index 8ef46fdca..100a8e349 100644 --- a/g10/gpg.h +++ b/g10/gpg.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef GNUPG_G10_GPG_H #define GNUPG_G10_GPG_H diff --git a/g10/pkglue.c b/g10/pkglue.c index f062d8366..3f9669d27 100644 --- a/g10/pkglue.c +++ b/g10/pkglue.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/g10/pkglue.h b/g10/pkglue.h index 43b82785b..866960bfd 100644 --- a/g10/pkglue.h +++ b/g10/pkglue.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef GNUPG_G10_PKGLUE_H diff --git a/include/_regex.h b/include/_regex.h index fac441dc6..ddd002484 100644 --- a/include/_regex.h +++ b/include/_regex.h @@ -16,8 +16,8 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. */ #ifndef _REGEX_H #define _REGEX_H 1 diff --git a/include/errors.h b/include/errors.h index ed437fa99..f3269ce5b 100644 --- a/include/errors.h +++ b/include/errors.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef GNUPG_INCLUDE_ERRORS_H #define GNUPG_INCLUDE_ERRORS_H diff --git a/include/memory.h b/include/memory.h index 35719da62..2e2f8fdce 100644 --- a/include/memory.h +++ b/include/memory.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef G10_MEMORY_H diff --git a/include/mpi.h b/include/mpi.h index b732923a2..7402ef6d3 100644 --- a/include/mpi.h +++ b/include/mpi.h @@ -16,7 +16,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. * * Note: This code is heavily based on the GNU MP Library. * Actually it's the same code with only minor changes in the diff --git a/include/util.h b/include/util.h index c579c152e..1d6d01366 100644 --- a/include/util.h +++ b/include/util.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef G10_UTIL_H #define G10_UTIL_H diff --git a/jnlib/Makefile.am b/jnlib/Makefile.am index 69eac4bf7..5fd48495c 100644 --- a/jnlib/Makefile.am +++ b/jnlib/Makefile.am @@ -14,7 +14,8 @@ # # 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 +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. ## Process this file with automake to produce Makefile.in diff --git a/jnlib/argparse.c b/jnlib/argparse.c index 980d1186c..15a7c546e 100644 --- a/jnlib/argparse.c +++ b/jnlib/argparse.c @@ -1,21 +1,22 @@ /* [argparse.c wk 17.06.97] Argument Parser for option handling * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * - * This file is part of GnuPG. + * 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 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. + * 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 + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include @@ -904,7 +905,7 @@ strusage( int level ) switch( level ) { case 11: p = "foo"; break; case 13: p = "0.0"; break; - case 14: p = "Copyright (C) 2005 Free Software Foundation, Inc."; break; + case 14: p = "Copyright (C) 2006 Free Software Foundation, Inc."; break; case 15: p = "This program comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to redistribute it\n" @@ -920,7 +921,8 @@ strusage( int level ) "GNU General Public License for more details.\n\n" "You should have received a copy of the GNU General Public License\n" "along with this program; if not, write to the Free Software\n" -"Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n"; +"Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,\n" +"USA.\n"; break; case 40: /* short and long usage */ case 41: p = ""; break; diff --git a/jnlib/argparse.h b/jnlib/argparse.h index e8922faa4..531622ea5 100644 --- a/jnlib/argparse.h +++ b/jnlib/argparse.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef LIBJNLIB_ARGPARSE_H diff --git a/jnlib/dotlock.c b/jnlib/dotlock.c index b7f892717..89edb7b91 100644 --- a/jnlib/dotlock.c +++ b/jnlib/dotlock.c @@ -16,7 +16,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/jnlib/dotlock.h b/jnlib/dotlock.h index 2cb39008a..1c0f05cb2 100644 --- a/jnlib/dotlock.h +++ b/jnlib/dotlock.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef LIBJNLIB_DOTLOCK_H diff --git a/jnlib/libjnlib-config.h b/jnlib/libjnlib-config.h index da3991432..ded6e057b 100644 --- a/jnlib/libjnlib-config.h +++ b/jnlib/libjnlib-config.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ /**************** diff --git a/jnlib/logging.c b/jnlib/logging.c index c944006a5..20ba02ccd 100644 --- a/jnlib/logging.c +++ b/jnlib/logging.c @@ -16,7 +16,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ diff --git a/jnlib/logging.h b/jnlib/logging.h index b5c0bd741..3ad43b4ec 100644 --- a/jnlib/logging.h +++ b/jnlib/logging.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef LIBJNLIB_LOGGING_H diff --git a/jnlib/mischelp.h b/jnlib/mischelp.h index 54da4cc1f..8e7f9c346 100644 --- a/jnlib/mischelp.h +++ b/jnlib/mischelp.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef LIBJNLIB_MISCHELP_H diff --git a/jnlib/stringhelp.c b/jnlib/stringhelp.c index 27b8a25e8..9df852754 100644 --- a/jnlib/stringhelp.c +++ b/jnlib/stringhelp.c @@ -16,7 +16,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/jnlib/stringhelp.h b/jnlib/stringhelp.h index 405d6dbc4..b8f4dbec0 100644 --- a/jnlib/stringhelp.h +++ b/jnlib/stringhelp.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef LIBJNLIB_STRINGHELP_H diff --git a/jnlib/strlist.c b/jnlib/strlist.c index 52b4d5869..87e121705 100644 --- a/jnlib/strlist.c +++ b/jnlib/strlist.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/jnlib/strlist.h b/jnlib/strlist.h index 3c1252a44..ee9f1fa60 100644 --- a/jnlib/strlist.h +++ b/jnlib/strlist.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef LIBJNLIB_STRLIST_H diff --git a/jnlib/types.h b/jnlib/types.h index 934b7a6ee..89d245943 100644 --- a/jnlib/types.h +++ b/jnlib/types.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef LIBJNLIB_TYPES_H diff --git a/jnlib/utf8conv.c b/jnlib/utf8conv.c index 4df8b7b32..9fba1ed4f 100644 --- a/jnlib/utf8conv.c +++ b/jnlib/utf8conv.c @@ -16,7 +16,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/jnlib/utf8conv.h b/jnlib/utf8conv.h index 6e2ce9944..344c47f92 100644 --- a/jnlib/utf8conv.h +++ b/jnlib/utf8conv.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef LIBJNLIB_UTF8CONF_H diff --git a/jnlib/w32-afunix.c b/jnlib/w32-afunix.c index c93d389da..84d799f1f 100644 --- a/jnlib/w32-afunix.c +++ b/jnlib/w32-afunix.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifdef _WIN32 #include diff --git a/jnlib/w32-afunix.h b/jnlib/w32-afunix.h index 367832299..d0eb8cf7e 100644 --- a/jnlib/w32-afunix.h +++ b/jnlib/w32-afunix.h @@ -15,7 +15,8 @@ * * You should have received a copy of the GNU Lesser 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifdef _WIN32 #ifndef W32AFUNIX_DEFS_H diff --git a/jnlib/w32-pth.c b/jnlib/w32-pth.c index 2f041c490..4107c7cb3 100644 --- a/jnlib/w32-pth.c +++ b/jnlib/w32-pth.c @@ -16,7 +16,8 @@ * * You should have received a copy of the GNU Lesser 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. * * ------------------------------------------------------------------ * This code is based on Ralf Engelschall's GNU Pth, a non-preemptive diff --git a/jnlib/w32-pth.h b/jnlib/w32-pth.h index 5ef0ab240..524010d92 100644 --- a/jnlib/w32-pth.h +++ b/jnlib/w32-pth.h @@ -16,7 +16,8 @@ * * You should have received a copy of the GNU Lesser 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. * * ------------------------------------------------------------------ * This code is based on Ralf Engelschall's GNU Pth, a non-preemptive diff --git a/jnlib/xmalloc.c b/jnlib/xmalloc.c index 1cfaab9f7..f5b92ba41 100644 --- a/jnlib/xmalloc.c +++ b/jnlib/xmalloc.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/jnlib/xmalloc.h b/jnlib/xmalloc.h index 150ef3664..8bfa7df79 100644 --- a/jnlib/xmalloc.h +++ b/jnlib/xmalloc.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef LIBJNLIB_XMALLOC_H diff --git a/kbx/Makefile.am b/kbx/Makefile.am index f42e517bf..063dbb4c0 100644 --- a/kbx/Makefile.am +++ b/kbx/Makefile.am @@ -15,7 +15,8 @@ # # 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 +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. ## Process this file with automake to produce Makefile.in diff --git a/kbx/kbxutil.c b/kbx/kbxutil.c index 0569b5a67..19d356007 100644 --- a/kbx/kbxutil.c +++ b/kbx/kbxutil.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/kbx/keybox-blob.c b/kbx/keybox-blob.c index eacc0014a..f3fe31617 100644 --- a/kbx/keybox-blob.c +++ b/kbx/keybox-blob.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ diff --git a/kbx/keybox-defs.h b/kbx/keybox-defs.h index 7bbed8519..ad53c71a7 100644 --- a/kbx/keybox-defs.h +++ b/kbx/keybox-defs.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef KEYBOX_DEFS_H diff --git a/kbx/keybox-dump.c b/kbx/keybox-dump.c index 495fb249e..d28611377 100644 --- a/kbx/keybox-dump.c +++ b/kbx/keybox-dump.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/kbx/keybox-file.c b/kbx/keybox-file.c index 3883ce607..e68e96cf9 100644 --- a/kbx/keybox-file.c +++ b/kbx/keybox-file.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/kbx/keybox-init.c b/kbx/keybox-init.c index 46a29978a..6c01b4f3a 100644 --- a/kbx/keybox-init.c +++ b/kbx/keybox-init.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/kbx/keybox-openpgp.c b/kbx/keybox-openpgp.c index 7401949c9..8ac713979 100644 --- a/kbx/keybox-openpgp.c +++ b/kbx/keybox-openpgp.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ /* This is a simple OpenPGP parser suitable for all OpenPGP key diff --git a/kbx/keybox-search-desc.h b/kbx/keybox-search-desc.h index 4be59c27d..f3a69d0f1 100644 --- a/kbx/keybox-search-desc.h +++ b/kbx/keybox-search-desc.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ /* diff --git a/kbx/keybox-search.c b/kbx/keybox-search.c index 2ce3c1923..f95e6eb06 100644 --- a/kbx/keybox-search.c +++ b/kbx/keybox-search.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/kbx/keybox-update.c b/kbx/keybox-update.c index a16c18e23..bb43d287b 100644 --- a/kbx/keybox-update.c +++ b/kbx/keybox-update.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/kbx/keybox-util.c b/kbx/keybox-util.c index ed5d93de0..6eb85ba3f 100644 --- a/kbx/keybox-util.c +++ b/kbx/keybox-util.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/kbx/keybox.h b/kbx/keybox.h index af1fc4516..0f97fb7fc 100644 --- a/kbx/keybox.h +++ b/kbx/keybox.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef KEYBOX_H diff --git a/kbx/mkerrors b/kbx/mkerrors index 5adb7bfdf..d3d096c5d 100755 --- a/kbx/mkerrors +++ b/kbx/mkerrors @@ -17,7 +17,8 @@ # # 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 +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. cat < diff --git a/scd/app-nks.c b/scd/app-nks.c index 73ec8ea01..1ca8d4187 100644 --- a/scd/app-nks.c +++ b/scd/app-nks.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 5e9281a38..842881f3a 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. * * $Id$ */ diff --git a/scd/app-p15.c b/scd/app-p15.c index 4203a6840..8a7732c85 100644 --- a/scd/app-p15.c +++ b/scd/app-p15.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ /* Information pertaining to the BELPIC developer card samples: diff --git a/scd/app.c b/scd/app.c index 363e386ce..e3d42054b 100644 --- a/scd/app.c +++ b/scd/app.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/scd/atr.c b/scd/atr.c index 6475e83f8..bd5a22621 100644 --- a/scd/atr.c +++ b/scd/atr.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/scd/atr.h b/scd/atr.h index 5fdd57457..c70089ca5 100644 --- a/scd/atr.h +++ b/scd/atr.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef ATR_H diff --git a/scd/card-common.h b/scd/card-common.h index cefaf120f..dd7529d5b 100644 --- a/scd/card-common.h +++ b/scd/card-common.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef CARD_COMMON_H diff --git a/scd/card-dinsig.c b/scd/card-dinsig.c index df09bfb57..d50d758f2 100644 --- a/scd/card-dinsig.c +++ b/scd/card-dinsig.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ /* The German signature law and its bylaw (SigG and SigV) is currently diff --git a/scd/card-p15.c b/scd/card-p15.c index ae3ef148f..63d537d5a 100644 --- a/scd/card-p15.c +++ b/scd/card-p15.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/scd/card.c b/scd/card.c index 9ec2a52c5..7a41ab7bb 100644 --- a/scd/card.c +++ b/scd/card.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/scd/command.c b/scd/command.c index 2ed685587..4629d9edf 100644 --- a/scd/command.c +++ b/scd/command.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/scd/sc-copykeys.c b/scd/sc-copykeys.c index 66b6894e0..395b4625a 100644 --- a/scd/sc-copykeys.c +++ b/scd/sc-copykeys.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/scd/scdaemon.c b/scd/scdaemon.c index e24b42132..b11cc7a91 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/scd/scdaemon.h b/scd/scdaemon.h index abe9730a7..f9689ee09 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef SCDAEMON_H diff --git a/scd/tlv.c b/scd/tlv.c index b436d956a..6ddbeaf1f 100644 --- a/scd/tlv.c +++ b/scd/tlv.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/scd/tlv.h b/scd/tlv.h index f587dd9df..877573d25 100644 --- a/scd/tlv.h +++ b/scd/tlv.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef SCD_TLV_H diff --git a/scripts/compile b/scripts/compile index ac07cc541..b6e6dcb0f 100755 --- a/scripts/compile +++ b/scripts/compile @@ -17,7 +17,8 @@ # # 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. +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA.. # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a diff --git a/scripts/config.guess b/scripts/config.guess index 77c7cbab0..a4929a979 100755 --- a/scripts/config.guess +++ b/scripts/config.guess @@ -17,7 +17,8 @@ timestamp='2004-08-13' # # 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. +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA.. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a diff --git a/sm/ChangeLog b/sm/ChangeLog index 48e8473fa..f191e7512 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,3 +1,10 @@ +2006-06-20 Werner Koch + + * gpgsm.c (gpgsm_init_default_ctrl): Take care of the command line + option --include-certs. + + * keylist.c (list_cert_raw): Print the certid. + 2006-05-23 Werner Koch * keydb.c (hextobyte): Deleted as it is now defined in jnlib. diff --git a/sm/Makefile.am b/sm/Makefile.am index b5882ae1d..be53e8d25 100644 --- a/sm/Makefile.am +++ b/sm/Makefile.am @@ -14,7 +14,8 @@ # # 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 +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. ## Process this file with automake to produce Makefile.in diff --git a/sm/base64.c b/sm/base64.c index 62c2c9ad9..59ab6f24b 100644 --- a/sm/base64.c +++ b/sm/base64.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/sm/call-agent.c b/sm/call-agent.c index 9942672ae..85ec78c63 100644 --- a/sm/call-agent.c +++ b/sm/call-agent.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/sm/call-dirmngr.c b/sm/call-dirmngr.c index 85467d4a2..0de09a9ba 100644 --- a/sm/call-dirmngr.c +++ b/sm/call-dirmngr.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/sm/certchain.c b/sm/certchain.c index 44d72efd3..4a4ac49f6 100644 --- a/sm/certchain.c +++ b/sm/certchain.c @@ -16,7 +16,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/sm/certcheck.c b/sm/certcheck.c index 5fb376712..732356149 100644 --- a/sm/certcheck.c +++ b/sm/certcheck.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/sm/certdump.c b/sm/certdump.c index 1f2ea7b18..0d5146abc 100644 --- a/sm/certdump.c +++ b/sm/certdump.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/sm/certlist.c b/sm/certlist.c index b036a85d7..cde2930ec 100644 --- a/sm/certlist.c +++ b/sm/certlist.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/sm/certreqgen.c b/sm/certreqgen.c index c523c992a..744969719 100644 --- a/sm/certreqgen.c +++ b/sm/certreqgen.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ /* diff --git a/sm/decrypt.c b/sm/decrypt.c index 9e5518b0f..70d48c983 100644 --- a/sm/decrypt.c +++ b/sm/decrypt.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/sm/delete.c b/sm/delete.c index 7533f7291..0d2f1fd9d 100644 --- a/sm/delete.c +++ b/sm/delete.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/sm/encrypt.c b/sm/encrypt.c index e4c0d5437..07c2ba8ce 100644 --- a/sm/encrypt.c +++ b/sm/encrypt.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/sm/export.c b/sm/export.c index f9d6dac62..b08a017d2 100644 --- a/sm/export.c +++ b/sm/export.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/sm/fingerprint.c b/sm/fingerprint.c index 9441483bf..d6a3900f0 100644 --- a/sm/fingerprint.c +++ b/sm/fingerprint.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/sm/gpgsm.c b/sm/gpgsm.c index 7347bf575..5363b8ad6 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -1,5 +1,6 @@ /* gpgsm.c - GnuPG for S/MIME - * Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + * Copyright (C) 2001, 2002, 2003, 2004, 2005, + * 2006 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +16,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include @@ -461,6 +463,10 @@ static unsigned int debug_value; /* Option --enable-special-filenames */ static int allow_special_filenames; +/* Default value for include-certs. */ +static int default_include_certs = 1; /* Only include the signer's cert. */ + + static char *build_list (const char *text, const char *(*mapf)(int), int (*chkf)(int)); @@ -998,7 +1004,9 @@ main ( int argc, char **argv) ctrl.use_ocsp = opt.enable_ocsp = 1; break; - case oIncludeCerts: ctrl.include_certs = pargs.r.ret_int; break; + case oIncludeCerts: + ctrl.include_certs = default_include_certs = pargs.r.ret_int; + break; case oPolicyFile: xfree (opt.policy_file); @@ -1657,7 +1665,7 @@ gpgsm_exit (int rc) void gpgsm_init_default_ctrl (struct server_control_s *ctrl) { - ctrl->include_certs = 1; /* only include the signer's cert */ + ctrl->include_certs = default_include_certs; ctrl->use_ocsp = opt.enable_ocsp; } diff --git a/sm/gpgsm.h b/sm/gpgsm.h index 438252050..b49f34640 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef GPGSM_H diff --git a/sm/import.c b/sm/import.c index 6d00e91ea..b56014a1a 100644 --- a/sm/import.c +++ b/sm/import.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/sm/keydb.c b/sm/keydb.c index d5932135d..81936cf6a 100644 --- a/sm/keydb.c +++ b/sm/keydb.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/sm/keydb.h b/sm/keydb.h index fb4001b64..814ae9f1e 100644 --- a/sm/keydb.h +++ b/sm/keydb.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef GNUPG_KEYDB_H diff --git a/sm/keylist.c b/sm/keylist.c index b744a157f..9baf065d0 100644 --- a/sm/keylist.c +++ b/sm/keylist.c @@ -16,7 +16,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include @@ -598,6 +599,10 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd, fprintf (fp, " md5_fpr: %s\n", dn?dn:"error"); xfree (dn); + dn = gpgsm_get_certid (cert); + fprintf (fp, " certid: %s\n", dn?dn:"error"); + xfree (dn); + dn = gpgsm_get_keygrip_hexstring (cert); fprintf (fp, " keygrip: %s\n", dn?dn:"error"); xfree (dn); diff --git a/sm/misc.c b/sm/misc.c index cd072ce6b..86cb506d6 100644 --- a/sm/misc.c +++ b/sm/misc.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/sm/qualified.c b/sm/qualified.c index 07abaadc4..474e1488d 100644 --- a/sm/qualified.c +++ b/sm/qualified.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/sm/server.c b/sm/server.c index 87a06ee4e..57e5d8f38 100644 --- a/sm/server.c +++ b/sm/server.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/sm/sign.c b/sm/sign.c index 74bfe41aa..d656825e8 100644 --- a/sm/sign.c +++ b/sm/sign.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/sm/verify.c b/sm/verify.c index f37cf4a75..4e6574078 100644 --- a/sm/verify.c +++ b/sm/verify.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/tests/Makefile.am b/tests/Makefile.am index 5264b8859..38b64c6ea 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -15,7 +15,8 @@ # # 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 +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. ## Process this file with automake to produce Makefile.in diff --git a/tests/asschk.c b/tests/asschk.c index 6a05fe1a8..40b95ba7d 100644 --- a/tests/asschk.c +++ b/tests/asschk.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ /* This is a simple stand-alone Assuan server test program. We don't diff --git a/tests/pkits/Makefile.am b/tests/pkits/Makefile.am index 41fdec497..d53d35a25 100644 --- a/tests/pkits/Makefile.am +++ b/tests/pkits/Makefile.am @@ -15,7 +15,8 @@ # # 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 +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. ## Process this file with automake to produce Makefile.in diff --git a/tests/pkits/common.sh b/tests/pkits/common.sh index 5e773ea5d..09fb62bc8 100644 --- a/tests/pkits/common.sh +++ b/tests/pkits/common.sh @@ -16,7 +16,8 @@ # # 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 +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. # reset some environment variables because we do not want to test locals export LANG=C diff --git a/tests/pkits/import-all-certs b/tests/pkits/import-all-certs index d1af5fb03..2d70d06df 100755 --- a/tests/pkits/import-all-certs +++ b/tests/pkits/import-all-certs @@ -15,7 +15,8 @@ # # 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 +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. . ${srcdir:-.}/common.sh || exit 2 diff --git a/tests/pkits/validate-all-certs b/tests/pkits/validate-all-certs index f482fdb51..08f72af71 100755 --- a/tests/pkits/validate-all-certs +++ b/tests/pkits/validate-all-certs @@ -16,7 +16,8 @@ # # 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 +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. . ${srcdir:-.}/common.sh || exit 2 diff --git a/tools/Makefile.am b/tools/Makefile.am index d9ef8812a..6b4767a79 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -15,7 +15,8 @@ # # 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 +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. EXTRA_DIST = Manifest watchgnupg.c \ addgnupghome gpgsm-gencert.sh diff --git a/tools/gpg-connect-agent.c b/tools/gpg-connect-agent.c index c9a324fa8..90e321000 100644 --- a/tools/gpg-connect-agent.c +++ b/tools/gpg-connect-agent.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/tools/gpgconf-comp.c b/tools/gpgconf-comp.c index 2da88bc49..04a61a193 100644 --- a/tools/gpgconf-comp.c +++ b/tools/gpgconf-comp.c @@ -1,21 +1,23 @@ /* gpgconf-comp.c - Configuration utility for GnuPG. - Copyright (C) 2004 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 GnuPG; if not, write to the Free Software Foundation, - Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + * Copyright (C) 2004 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 GnuPG; if not, write to the Free Software Foundation, + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ #if HAVE_CONFIG_H #include diff --git a/tools/gpgconf.c b/tools/gpgconf.c index dd505e99d..87ba45ae1 100644 --- a/tools/gpgconf.c +++ b/tools/gpgconf.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/tools/gpgconf.h b/tools/gpgconf.h index 138380b6d..c083c26aa 100644 --- a/tools/gpgconf.h +++ b/tools/gpgconf.h @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifndef GPGCONF_H diff --git a/tools/gpgkey2ssh.c b/tools/gpgkey2ssh.c index e874ab22e..3dcb6516e 100644 --- a/tools/gpgkey2ssh.c +++ b/tools/gpgkey2ssh.c @@ -1,22 +1,23 @@ /* gpgkey2ssh.c - Converter ... - Copyright (C) 2005 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. */ + * Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ #include diff --git a/tools/gpgparsemail.c b/tools/gpgparsemail.c index 566f5747f..30759f9a4 100644 --- a/tools/gpgparsemail.c +++ b/tools/gpgparsemail.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ diff --git a/tools/no-libgcrypt.c b/tools/no-libgcrypt.c index 82f6a8bb5..636df10c6 100644 --- a/tools/no-libgcrypt.c +++ b/tools/no-libgcrypt.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include diff --git a/tools/symcryptrun.c b/tools/symcryptrun.c index 075e0b444..406cbb2a2 100644 --- a/tools/symcryptrun.c +++ b/tools/symcryptrun.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ diff --git a/tools/watchgnupg.c b/tools/watchgnupg.c index 6cb570fbc..051ca50fe 100644 --- a/tools/watchgnupg.c +++ b/tools/watchgnupg.c @@ -15,7 +15,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #ifdef HAVE_CONFIG_H -- cgit