From 830af45ca22a540fa13edc41846304b36d0b9382 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 3 May 2010 11:10:49 +0000 Subject: [PATCH] Collected changes --- agent/ChangeLog | 9 +++++++ agent/call-scd.c | 11 +++++++++ agent/gpg-agent.c | 2 +- common/ChangeLog | 5 ++++ common/asshelp.c | 63 +++++++++++++++++++++++++++++++++++++++-------- scd/ChangeLog | 11 +++++++++ scd/apdu.c | 20 ++++++++++++--- scd/apdu.h | 4 +-- scd/command.c | 23 ++++++++++++++--- scd/sc-copykeys.c | 2 +- 10 files changed, 129 insertions(+), 21 deletions(-) diff --git a/agent/ChangeLog b/agent/ChangeLog index 68accb88e..0bca557c9 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,12 @@ +2010-05-03 Werner Koch + + * gpg-agent.c (check_own_socket_thread): Do not release SOCKNAME + too early. + +2010-03-17 Werner Koch + + * call-scd.c (unlock_scd): Send a BYE under certain conditions. + 2010-02-19 Werner Koch * call-pinentry.c (start_pinentry): Remove a translation prefix. diff --git a/agent/call-scd.c b/agent/call-scd.c index f3303c11d..93f1cf742 100644 --- a/agent/call-scd.c +++ b/agent/call-scd.c @@ -176,6 +176,17 @@ agent_scd_dump_state (void) static int unlock_scd (ctrl_t ctrl, int rc) { + if (gpg_err_code (rc) == GPG_ERR_NOT_OPERATIONAL + && gpg_err_source (rc) == GPG_ERR_SOURCE_SCD) + { + /* If the SCdaemon returned this error, it detected a major + problem, like no reader connected. To finish this we need to + stop the connection. This simulates an explicit killing of + the SCdaemon. */ + assuan_transact (primary_scd_ctx, "BYE", + NULL, NULL, NULL, NULL, NULL, NULL); + } + if (ctrl->scd_local->locked != 1) { log_error ("unlock_scd: invalid lock count (%d)\n", diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 73f84ed82..eec2ca1e0 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -2101,7 +2101,6 @@ check_own_socket_thread (void *arg) check_own_socket_running++; rc = assuan_new (&ctx); - xfree (sockname); if (rc) { log_error ("can't allocate assuan context: %s\n", gpg_strerror (rc)); @@ -2137,6 +2136,7 @@ check_own_socket_thread (void *arg) xfree (buffer); leave: + xfree (sockname); if (ctx) assuan_release (ctx); if (rc) diff --git a/common/ChangeLog b/common/ChangeLog index 99a1a0994..5ca04c07d 100644 --- a/common/ChangeLog +++ b/common/ChangeLog @@ -1,3 +1,8 @@ +2010-03-17 Werner Koch + + * asshelp.c (start_new_gpg_agent) [W32]: Use a named mutex to + avoid starting two agents. + 2010-03-12 Werner Koch * status.h (STATUS_ENTER): New. diff --git a/common/asshelp.c b/common/asshelp.c index b2d13f32b..615bb8d3b 100644 --- a/common/asshelp.c +++ b/common/asshelp.c @@ -231,25 +231,68 @@ start_new_gpg_agent (assuan_context_t *r_ctx, and thus there is no need for the GPG_AGENT_INFO envvar. This is possible as we don't have a real unix domain socket but use a plain file and thus there is no - need to care about non-local file systems. */ + need to care about non-local file systems. We use a + named mutex to interlock the spawning. There is just + one problem with that: If gpg-agent needs more than 3 + seconds to come up and listen on the socket we might + still spawn another agent. However this is no serious + problem because an agent detects this and handles it. + Thus the mutex merely helps to save resources in the + most common cases. */ const char *argv[3]; + HANDLE mutex; + int waitrc; argv[0] = "--daemon"; argv[1] = "--use-standard-socket"; argv[2] = NULL; - rc = gnupg_spawn_process_detached (agent_program, argv, NULL); - if (rc) - log_debug ("failed to start agent `%s': %s\n", - agent_program, gpg_strerror (rc)); + mutex = CreateMutex (NULL, FALSE, "GnuPG_spawn_agent_sentinel"); + if (!mutex) + { + log_error ("failed to create the spawn_agent mutex: %s\n", + w32_strerror (-1)); + rc = gpg_error (GPG_ERR_GENERAL); + } + else if ((waitrc = WaitForSingleObject (mutex, 5000)) + == WAIT_OBJECT_0) + { + rc = assuan_socket_connect (&ctx, sockname, 0); + if (rc) + { + /* Still not available. */ + rc = gnupg_spawn_process_detached (agent_program, + argv, NULL); + if (rc) + log_debug ("failed to start agent `%s': %s\n", + agent_program, gpg_strerror (rc)); + else + { + /* Give the agent some time to prepare itself. */ + gnupg_sleep (3); + /* Now try again to connect the agent. */ + rc = assuan_socket_connect (&ctx, sockname, 0); + } + } + if (!ReleaseMutex (mutex)) + log_error ("failed to release the spawn_agent mutex: %s\n", + w32_strerror (-1)); + } + else if (waitrc == WAIT_TIMEOUT) + { + log_info ("error waiting for the spawn_agent mutex: timeout\n"); + rc = gpg_error (GPG_ERR_GENERAL); + } else { - /* Give the agent some time to prepare itself. */ - gnupg_sleep (3); - /* Now try again to connect the agent. */ - rc = assuan_socket_connect (ctx, sockname, 0, 0); + log_debug ("error waiting for the spawn_agent mutex: " + "(code=%d) %s\n", waitrc, w32_strerror (-1)); + rc = gpg_error (GPG_ERR_GENERAL); } - } + + if (mutex) + CloseHandle (mutex); + } #else /*!HAVE_W32_SYSTEM*/ { const char *pgmname; diff --git a/scd/ChangeLog b/scd/ChangeLog index 7c72ca8a3..419f9d44d 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,14 @@ +2010-03-17 Werner Koch + + * command.c (open_card): Return GPG_ERR_NOT_OPERATIONAL if no + card services are available. + (get_reader_slot): Detect no services status. + (cmd_serialno): No reset if there are no services. + (scd_command_handler): Stop scdaemon in that case. + * apdu.c (pcsc_no_service): New. + (open_pcsc_reader_direct): Set it. + (apdu_open_reader): Add arg R_NO_SERVICE. + 2010-02-11 Marcus Brinkmann From trunk 2009-09-23, 2009-10-16, 2009-11-02, 2009-11-04, 2009-11-05, diff --git a/scd/apdu.c b/scd/apdu.c index f382aea85..0fe044cbe 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -287,6 +287,9 @@ long (* DLSTDCALL pcsc_transmit) (unsigned long card, long (* DLSTDCALL pcsc_set_timeout) (unsigned long context, unsigned long timeout); +/* Flag set if PC/SC returned the no-service error. */ +static int pcsc_no_service; + /* Prototypes. */ static int pcsc_get_status (int slot, unsigned int *status); @@ -1487,8 +1490,11 @@ open_pcsc_reader_direct (const char *portstr) log_error ("pcsc_establish_context failed: %s (0x%lx)\n", pcsc_error_string (err), err); reader_table[slot].used = 0; + if (err == 0x8010001d) + pcsc_no_service = 1; return -1; } + pcsc_no_service = 0; err = pcsc_list_readers (reader_table[slot].pcsc.context, NULL, NULL, &nreader); @@ -2321,14 +2327,18 @@ unlock_slot (int slot) error. If PORTSTR is NULL we default to a suitable port (for ctAPI: the first USB reader. For PC/SC the first listed reader). */ int -apdu_open_reader (const char *portstr) +apdu_open_reader (const char *portstr, int *r_no_service) { static int pcsc_api_loaded, ct_api_loaded; + int slot; + + if (r_no_service) + *r_no_service = 0; #ifdef HAVE_LIBUSB if (!opt.disable_ccid) { - int slot, i; + int i; const char *s; slot = open_ccid_reader (portstr); @@ -2458,7 +2468,11 @@ apdu_open_reader (const char *portstr) pcsc_api_loaded = 1; } - return open_pcsc_reader (portstr); + slot = open_pcsc_reader (portstr); + if (slot == -1 && r_no_service && pcsc_no_service) + *r_no_service = 1; + + return slot; } diff --git a/scd/apdu.h b/scd/apdu.h index c47dea882..d79f8b48f 100644 --- a/scd/apdu.h +++ b/scd/apdu.h @@ -80,8 +80,8 @@ enum { #define APDU_CARD_ACTIVE (4) /* Card is active. */ -/* Note , that apdu_open_reader returns no status word but -1 on error. */ -int apdu_open_reader (const char *portstr); +/* Note, that apdu_open_reader returns no status word but -1 on error. */ +int apdu_open_reader (const char *portstr, int *r_no_service); int apdu_open_remote_reader (const char *portstr, const unsigned char *cookie, size_t length, int (*readfnc) (void *opaque, diff --git a/scd/command.c b/scd/command.c index ad1d6b9bd..e2cd1f514 100644 --- a/scd/command.c +++ b/scd/command.c @@ -70,6 +70,10 @@ && (c)->reader_slot == locked_session->ctrl_backlink->reader_slot) +/* Flag indicating that the reader has been disabled. */ +static int reader_disabled; + + /* This structure is used to keep track of open readers (slots). */ struct slot_status_s { @@ -394,7 +398,15 @@ get_reader_slot (void) /* Try to open the reader. */ if (ss->slot == -1) - ss->slot = apdu_open_reader (opt.reader_port); + { + int no_service_flag; + ss->slot = apdu_open_reader (opt.reader_port, &no_service_flag); + if (no_service_flag) + { + log_info ("no card services - disabling scdaemon\n"); + reader_disabled = 1; + } + } /* Return the slot_table index. */ return 0; @@ -409,6 +421,9 @@ open_card (ctrl_t ctrl, const char *apptype) gpg_error_t err; int slot; + if (reader_disabled) + return gpg_error (GPG_ERR_NOT_OPERATIONAL); + /* 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. */ @@ -441,7 +456,7 @@ open_card (ctrl_t ctrl, const char *apptype) slot = get_reader_slot (); ctrl->reader_slot = slot; if (slot == -1) - err = gpg_error (GPG_ERR_CARD); + err = gpg_error (reader_disabled? GPG_ERR_NOT_OPERATIONAL: GPG_ERR_CARD); else { /* Fixme: We should move the apdu_connect call to @@ -495,7 +510,7 @@ cmd_serialno (assuan_context_t ctx, char *line) time_t stamp; /* Clear the remove flag so that the open_card is able to reread it. */ - if (ctrl->server_local->card_removed) + if (!reader_disabled && ctrl->server_local->card_removed) { if ( IS_LOCKED (ctrl) ) return gpg_error (GPG_ERR_LOCKED); @@ -1995,7 +2010,7 @@ scd_command_handler (ctrl_t ctrl, int fd) BUG (); sl->next_session = ctrl->server_local->next_session; } - stopme = ctrl->server_local->stopme; + stopme = ctrl->server_local->stopme || reader_disabled; xfree (ctrl->server_local); ctrl->server_local = NULL; diff --git a/scd/sc-copykeys.c b/scd/sc-copykeys.c index 615e4b28a..b863b01b3 100644 --- a/scd/sc-copykeys.c +++ b/scd/sc-copykeys.c @@ -139,7 +139,7 @@ main (int argc, char **argv ) if (argc != 1) usage (1); - slot = apdu_open_reader (reader_port); + slot = apdu_open_reader (reader_port, NULL); if (slot == -1) exit (1); if (apdu_connect (slot))