From 3af261572bdf938f0a2fdde4d9aec82153a7e0e4 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 24 Feb 2005 17:36:11 +0000 Subject: [PATCH] * 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 | 417 ++++++++++++++++++++++---------------- 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, 839 insertions(+), 239 deletions(-) create mode 100644 tools/gpg-connect-agent.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. */ +/* 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 + +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; +}