From 3bbc481935713a0661a5084f624c72ae3b59a3ce Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 3 Apr 2007 16:57:37 +0000 Subject: [PATCH] Add a way to get a listing of available CCID readers. --- common/sexputil.c | 2 +- doc/gpg-agent.texi | 2 +- doc/scdaemon.texi | 7 ++ doc/tools.texi | 18 +++++ scd/ChangeLog | 6 ++ scd/ccid-driver.c | 17 +++-- scd/command.c | 21 ++++++ sm/ChangeLog | 4 +- tools/ChangeLog | 6 ++ tools/gpg-connect-agent.c | 139 ++++++++++++++++++++++++++------------ 10 files changed, 170 insertions(+), 52 deletions(-) diff --git a/common/sexputil.c b/common/sexputil.c index fe0870c56..da6124d91 100644 --- a/common/sexputil.c +++ b/common/sexputil.c @@ -1,4 +1,4 @@ -/* sexputil.c - Utility fnctions for S-expressions. +/* sexputil.c - Utility functions for S-expressions. * Copyright (C) 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. diff --git a/doc/gpg-agent.texi b/doc/gpg-agent.texi index a886d1534..533c97fd3 100644 --- a/doc/gpg-agent.texi +++ b/doc/gpg-agent.texi @@ -501,7 +501,7 @@ checking of some root certificate requirements. As a special feature a line @code{include-default} will include a global list of trusted certificates (e.g. @file{/etc/gnupg/trustlist.txt}). -This global list is also used if the local list ios not available. +This global list is also used if the local list is not available. @item sshcontrol diff --git a/doc/scdaemon.texi b/doc/scdaemon.texi index 6eb2f1656..f8bab803b 100644 --- a/doc/scdaemon.texi +++ b/doc/scdaemon.texi @@ -251,6 +251,13 @@ readers might need a string here; run the program in verbose mode to get a list of available readers. The default is then the first reader found. +To get a list of available CCID readers you may use this command: +@smallexample +echo scd getinfo reader_list | gpg-connect-agent --decode | awk '/^D/ @{print $2@}' +@end smallexample + + + @item --disable-keypad @opindex disable-keypad Even if a card reader features a keypad, do not try to use it. diff --git a/doc/tools.texi b/doc/tools.texi index d10b806eb..4187293c6 100644 --- a/doc/tools.texi +++ b/doc/tools.texi @@ -955,6 +955,16 @@ When using @option{-S} or @option{--exec}, @command{gpg-connect-agent} connects to the assuan server in extended mode to allow descriptor passing. This option makes it use the old mode. +@item --hex +@opindex hex +Print data lines in a hex format and the ASCII representation of +non-control characters. + +@item --decode +@opindex decode +Decode data lines. That is to remove percent escapes but make sure that +a new line always starts with a D and a space. + @end table @mansect control commands @@ -995,6 +1005,14 @@ input source for other commands. @item /recvfd Not yet implemented. +@item /hex +@itemx /nohex +Same as the command line option @option{--hex}. + +@item /decode +@itemx /nodecode +Same as the command line option @option{--decode}. + @item /help Print a list of available control commands. diff --git a/scd/ChangeLog b/scd/ChangeLog index 571457576..769ef0a14 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,9 @@ +2007-04-03 Werner Koch + + * command.c (cmd_getinfo): New subcommand "reader_list". + * ccid-driver.c (scan_or_find_devices): Ignore EBUSY in scan mode + for special transports. + 2007-03-07 Werner Koch * app-dinsig.c: Include i18n.h. diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c index 8ad329d67..33de4921b 100644 --- a/scd/ccid-driver.c +++ b/scd/ccid-driver.c @@ -989,7 +989,13 @@ scan_or_find_devices (int readerno, const char *readerid, char *rid, *p; fd = open (transports[i].name, O_RDWR); - if (fd == -1) + if (fd == -1 && scan_mode && errno == EBUSY) + { + /* Ignore this error in scan mode because it indicates that + the device exists but is already open (most likely by us) + and thus in general suitable as a reader. */ + } + else if (fd == -1) { DEBUGOUT_2 ("failed to open `%s': %s\n", transports[i].name, strerror (errno)); @@ -999,7 +1005,8 @@ scan_or_find_devices (int readerno, const char *readerid, rid = malloc (strlen (transports[i].name) + 30 + 10); if (!rid) { - close (fd); + if (fd != -1) + close (fd); free (rid_list); return -1; /* Error. */ } @@ -1010,7 +1017,8 @@ scan_or_find_devices (int readerno, const char *readerid, p = malloc ((rid_list? strlen (rid_list):0) + 1 + strlen (rid) + 1); if (!p) { - close (fd); + if (fd != -1) + close (fd); free (rid_list); free (rid); return -1; /* Error. */ @@ -1046,7 +1054,8 @@ scan_or_find_devices (int readerno, const char *readerid, --readerno; } free (rid); - close (fd); + if (fd != -1) + close (fd); } if (scan_mode) diff --git a/scd/command.c b/scd/command.c index 579145711..93df064af 100644 --- a/scd/command.c +++ b/scd/command.c @@ -38,6 +38,9 @@ #include "app-common.h" #include "apdu.h" /* Required for apdu_*_reader (). */ #include "exechelp.h" +#ifdef HAVE_LIBUSB +#include "ccid-driver.h" +#endif /* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN */ #define MAXLEN_PIN 100 @@ -1382,12 +1385,16 @@ cmd_unlock (assuan_context_t ctx, char *line) Supported values of WHAT are: socket_name - Return the name of the socket. + status - Return the status of the current slot (in the future, may also return the status of all slots). The status is a list of one-character flags. The following flags are currently defined: 'u' Usable card present. This is the normal state during operation. 'r' Card removed. A reset is necessary. These flags are exclusive. + + reader_list - Return a list of detected card readers. Does + currently only work with the internal CCID driver. */ static int @@ -1427,6 +1434,20 @@ cmd_getinfo (assuan_context_t ctx, char *line) } rc = assuan_send_data (ctx, &flag, 1); } + else if (!strcmp (line, "reader_list")) + { +#ifdef HAVE_LIBUSB + char *s = ccid_get_reader_list (); +#else + char *s = NULL; +#endif + + if (s) + rc = assuan_send_data (ctx, s, strlen (s)); + else + rc = gpg_error (GPG_ERR_NO_DATA); + xfree (s); + } else rc = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT"); return rc; diff --git a/sm/ChangeLog b/sm/ChangeLog index 4ffa9678e..4a8e90e56 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -6,9 +6,9 @@ as much memory as required. (gpgsm_get_keygrip_hexstring): Use bin2hex. - * certchain.c (gpgsm_validate_chain): Keep terack of the + * certchain.c (gpgsm_validate_chain): Keep track of the certificate chain and reset the ephemeral flags. - * keydb.c (keydb_set_cert_flags): New args EPHEMERAL MASK. + * keydb.c (keydb_set_cert_flags): New args EPHEMERAL and MASK. Changed caller to use a mask of ~0. Return a proper error code if the certificate is not available. diff --git a/tools/ChangeLog b/tools/ChangeLog index d5f3c4d75..7ef39133c 100644 --- a/tools/ChangeLog +++ b/tools/ChangeLog @@ -1,3 +1,9 @@ +2007-04-03 Werner Koch + + * gpg-connect-agent.c (main): New option --decode and commands + decode and undecode. + (read_and_print_response): Implement option. + 2007-03-20 Werner Koch * gpgconf-comp.c (gc_options_gpgsm): Add p12-charset. diff --git a/tools/gpg-connect-agent.c b/tools/gpg-connect-agent.c index bc51584ba..98a13de11 100644 --- a/tools/gpg-connect-agent.c +++ b/tools/gpg-connect-agent.c @@ -1,5 +1,5 @@ /* gpg-connect-agent.c - Tool to connect to the agent. - * Copyright (C) 2005 Free Software Foundation, Inc. + * Copyright (C) 2005, 2007 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -46,6 +46,7 @@ enum cmd_and_opt_values oNoVerbose = 500, oHomedir, oHex, + oDecode, oNoExtConnect }; @@ -59,6 +60,7 @@ static ARGPARSE_OPTS opts[] = { oVerbose, "verbose", 0, N_("verbose") }, { oQuiet, "quiet", 0, N_("quiet") }, { oHex, "hex", 0, N_("print data out hex encoded") }, + { oDecode,"decode", 0, N_("decode received data lines") }, { oRawSocket, "raw-socket", 2, N_("|NAME|connect to Assuan socket NAME")}, { oExec, "exec", 0, N_("run the Assuan server given on the command line")}, { oNoExtConnect, "no-ext-connect", @@ -78,6 +80,7 @@ struct int quiet; /* Be extra quiet. */ const char *homedir; /* Configuration directory name */ int hex; /* Print data lines in hex format. */ + int decode; /* Decode received data lines. */ const char *raw_socket; /* Name of socket to connect in raw mode. */ int exec; /* Run the pgm given on the command line. */ unsigned int connect_flags; /* Flags used for connecting. */ @@ -306,6 +309,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 oDecode: opt.decode = 1; break; case oRawSocket: opt.raw_socket = pargs.r.ret_str; break; case oExec: opt.exec = 1; break; case oNoExtConnect: opt.connect_flags &= ~(1); break; @@ -436,6 +440,14 @@ main (int argc, char **argv) do_recvfd (ctx, p); continue; } + else if (!strcmp (cmd, "hex")) + opt.hex = 1; + else if (!strcmp (cmd, "nohex")) + opt.hex = 0; + else if (!strcmp (cmd, "decode")) + opt.decode = 1; + else if (!strcmp (cmd, "nodecode")) + opt.decode = 0; else if (!strcmp (cmd, "help")) { puts ( @@ -451,7 +463,9 @@ main (int argc, char **argv) "/cleardef Delete all definitions.\n" "/sendfd FILE MODE Open FILE and pass descriptor to server.\n" "/recvfd Receive FD from server and print. \n" - "/help Print this help."); +"/[no]hex Enable hex dumping of received data lines.\n" +"/[no]decode Enable decoding of received data lines.\n" +"/help Print this help."); } else log_error (_("unknown command `%s'\n"), cmd ); @@ -577,6 +591,7 @@ read_and_print_response (assuan_context_t ctx) size_t linelen; assuan_error_t rc; int i, j; + int need_lf = 0; for (;;) { @@ -628,56 +643,92 @@ read_and_print_response (assuan_context_t ctx) putchar ('\n'); } } + else if (opt.decode) + { + const unsigned char *s; + int need_d = 1; + int c = 0; + + for (j=2, s=(unsigned char*)line+2; j < linelen; j++, s++ ) + { + if (need_d) + { + fputs ("D ", stdout); + need_d = 0; + } + if (*s == '%' && j+2 < linelen) + { + s++; j++; + c = xtoi_2 ( s ); + s++; j++; + } + else + c = *s; + if (c == '\n') + need_d = 1; + putchar (c); + } + need_lf = (c != '\n'); + } else { fwrite (line, linelen, 1, stdout); putchar ('\n'); } } - else if (linelen >= 1 - && line[0] == 'S' - && (line[1] == '\0' || line[1] == ' ')) + else { - 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; + if (need_lf) + { + putchar ('\n'); + need_lf = 0; + } + + 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'); + if (!handle_inquire (ctx, line)) + assuan_write_line (ctx, "CANCEL"); + } + 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 gpg_error (GPG_ERR_ASS_INV_RESPONSE); } - 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'); - if (!handle_inquire (ctx, line)) - assuan_write_line (ctx, "CANCEL"); - } - 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 gpg_error (GPG_ERR_ASS_INV_RESPONSE); } }