From 8943c2cfefa1a3cf520bc2d0606da96f72043776 Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Fri, 17 Feb 2017 03:30:05 -0500 Subject: [PATCH] scd: Fix factory-reset. Signed-off-by: NIIBE Yutaka Backport from master branch: 99d4dfe83 e2792813a 031e3fa7b Additionally, fix another bug when tested with 2.1.18-7 with PC/SC. Gbp-Pq: Name 0030-scd-Fix-factory-reset.patch --- scd/app-common.h | 3 +- scd/app.c | 86 +++++++++++++++++++++++++++--------------------- scd/command.c | 6 ++-- scd/scdaemon.c | 51 +++++++++++++++++++++++++--- scd/scdaemon.h | 1 + 5 files changed, 102 insertions(+), 45 deletions(-) diff --git a/scd/app-common.h b/scd/app-common.h index b979f54..c7a0575 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -54,6 +54,7 @@ struct app_ctx_s { const char *apptype; unsigned int card_version; unsigned int card_status; + unsigned int reset_requested:1; unsigned int require_get_status:1; unsigned int did_chv1:1; unsigned int force_chv1:1; /* True if the card does not cache CHV1. */ @@ -134,7 +135,7 @@ gpg_error_t select_application (ctrl_t ctrl, const char *name, app_t *r_app, int scan, const unsigned char *serialno_bin, size_t serialno_bin_len); char *get_supported_applications (void); -void release_application (app_t app); +void release_application (app_t app, int locked_already); gpg_error_t app_munge_serialno (app_t app); gpg_error_t app_write_learn_status (app_t app, ctrl_t ctrl, unsigned int flags); diff --git a/scd/app.c b/scd/app.c index 8fb0d45..3f3f3ef 100644 --- a/scd/app.c +++ b/scd/app.c @@ -136,40 +136,32 @@ check_application_conflict (const char *name, app_t app) } -static void -release_application_internal (app_t app) -{ - if (!app->ref_count) - log_bug ("trying to release an already released context\n"); - - --app->ref_count; -} - gpg_error_t app_reset (app_t app, ctrl_t ctrl, int send_reset) { - gpg_error_t err; - - err = lock_app (app, ctrl); - if (err) - return err; + gpg_error_t err = 0; if (send_reset) { - int sw = apdu_reset (app->slot); + int sw; + + lock_app (app, ctrl); + sw = apdu_reset (app->slot); if (sw) err = gpg_error (GPG_ERR_CARD_RESET); - /* Release the same application which is used by other sessions. */ - send_client_notifications (app, 1); + app->reset_requested = 1; + unlock_app (app); + + scd_kick_the_loop (); + gnupg_sleep (1); } else { ctrl->app_ctx = NULL; - release_application_internal (app); + release_application (app, 0); } - unlock_app (app); return err; } @@ -370,6 +362,7 @@ select_application (ctrl_t ctrl, const char *name, app_t *r_app, } apdu_dev_list_finish (l); + scd_kick_the_loop (); } npth_mutex_lock (&app_list_lock); @@ -465,6 +458,8 @@ deallocate_app (app_t app) } xfree (app->serialno); + + unlock_app (app); xfree (app); } @@ -474,7 +469,7 @@ deallocate_app (app_t app) actually deferring the deallocation to allow for a later reuse by a new connection. */ void -release_application (app_t app) +release_application (app_t app, int locked_already) { if (!app) return; @@ -484,9 +479,15 @@ release_application (app_t app) is using the card - this way the PIN cache and other cached data are preserved. */ - lock_app (app, NULL); - release_application_internal (app); - unlock_app (app); + if (!locked_already) + lock_app (app, NULL); + + if (!app->ref_count) + log_bug ("trying to release an already released context\n"); + + --app->ref_count; + if (!locked_already) + unlock_app (app); } @@ -1023,11 +1024,16 @@ scd_update_reader_status_file (void) npth_mutex_lock (&app_list_lock); for (a = app_top; a; a = app_next) { + unsigned int status; + + lock_app (a, NULL); app_next = a->next; - if (a->require_get_status) + + if (a->reset_requested) + status = 0; + else { int sw; - unsigned int status; sw = apdu_get_status (a->slot, 0, &status); if (sw == SW_HOST_NO_READER) @@ -1038,24 +1044,30 @@ scd_update_reader_status_file (void) else if (sw) { /* Get status failed. Ignore that. */ + unlock_app (a); continue; } + } + + if (a->card_status != status) + { + report_change (a->slot, a->card_status, status); + send_client_notifications (a, status == 0); - if (a->card_status != status) + if (status == 0) { - report_change (a->slot, a->card_status, status); - send_client_notifications (a, status == 0); - - if (status == 0) - { - log_debug ("Removal of a card: %d\n", a->slot); - apdu_close_reader (a->slot); - deallocate_app (a); - } - else - a->card_status = status; + log_debug ("Removal of a card: %d\n", a->slot); + apdu_close_reader (a->slot); + deallocate_app (a); + } + else + { + a->card_status = status; + unlock_app (a); } } + else + unlock_app (a); } npth_mutex_unlock (&app_list_lock); } diff --git a/scd/command.c b/scd/command.c index 0ae6d29..b17c4a1 100644 --- a/scd/command.c +++ b/scd/command.c @@ -227,7 +227,7 @@ open_card_with_request (ctrl_t ctrl, const char *apptype, const char *serialno) /* Re-scan USB devices. Release APP, before the scan. */ ctrl->app_ctx = NULL; - release_application (app); + release_application (app, 0); if (serialno) serialno_bin = hex_to_buffer (serialno, &serialno_bin_len); @@ -1492,7 +1492,7 @@ cmd_restart (assuan_context_t ctx, char *line) if (app) { ctrl->app_ctx = NULL; - release_application (app); + release_application (app, 0); } if (locked_session && ctrl->server_local == locked_session) { @@ -1919,7 +1919,7 @@ send_client_notifications (app_t app, int removal) { sl->ctrl_backlink->app_ctx = NULL; sl->card_removed = 1; - release_application (app); + release_application (app, 1); } if (!sl->event_signal || !sl->assuan_ctx) diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 74fed44..02f0e72 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -52,6 +52,7 @@ #include "ccid-driver.h" #include "gc-opt-flags.h" #include "asshelp.h" +#include "exechelp.h" #include "../common/init.h" #ifndef ENAMETOOLONG @@ -224,7 +225,8 @@ static assuan_sock_nonce_t socket_nonce; disabled but it won't perform any ticker specific actions. */ static int ticker_disabled; - +/* FD to notify update of usb devices. */ +static int notify_fd; static char *create_socket_name (char *standard_name); static gnupg_fd_t create_server_socket (const char *name, @@ -1181,6 +1183,16 @@ start_connection_thread (void *arg) } +void +scd_kick_the_loop (void) +{ + int ret; + + /* Kick the select loop. */ + ret = write (notify_fd, "", 1); + (void)ret; +} + /* 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 @@ -1202,9 +1214,23 @@ handle_connections (int listen_fd) #ifndef HAVE_W32_SYSTEM int signo; #endif + int pipe_fd[2]; + + ret = gnupg_create_pipe (pipe_fd); + if (ret) + { + log_error ("pipe creation failed: %s\n", gpg_strerror (ret)); + return; + } + notify_fd = pipe_fd[1]; ret = npth_attr_init(&tattr); - /* FIXME: Check error. */ + if (ret) + { + log_error ("npth_attr_init failed: %s\n", strerror (ret)); + return; + } + npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED); #ifndef HAVE_W32_SYSTEM @@ -1233,6 +1259,8 @@ handle_connections (int listen_fd) for (;;) { + int max_fd; + if (shutdown_pending) { if (active_connections == 0) @@ -1261,14 +1289,20 @@ handle_connections (int listen_fd) thus a simple assignment is fine to copy the entire set. */ read_fdset = fdset; + FD_SET (pipe_fd[0], &read_fdset); + if (nfd < pipe_fd[0]) + max_fd = pipe_fd[0]; + else + max_fd = nfd; + #ifndef HAVE_W32_SYSTEM - ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, &timeout, npth_sigev_sigmask()); + ret = npth_pselect (max_fd+1, &read_fdset, NULL, NULL, &timeout, npth_sigev_sigmask()); saved_errno = errno; while (npth_sigev_get_pending(&signo)) handle_signal (signo); #else - ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, &timeout, NULL, NULL); + ret = npth_eselect (max_fd+1, &read_fdset, NULL, NULL, &timeout, NULL, NULL); saved_errno = errno; #endif @@ -1284,6 +1318,13 @@ handle_connections (int listen_fd) /* Timeout. Will be handled when calculating the next timeout. */ continue; + if (FD_ISSET (pipe_fd[0], &read_fdset)) + { + char buf[256]; + + ret = read (pipe_fd[0], buf, sizeof buf); + } + if (listen_fd != -1 && FD_ISSET (listen_fd, &read_fdset)) { ctrl_t ctrl; @@ -1322,6 +1363,8 @@ handle_connections (int listen_fd) } } + close (pipe_fd[0]); + close (pipe_fd[1]); cleanup (); log_info (_("%s %s stopped\n"), strusage(11), strusage(13)); npth_attr_destroy (&tattr); diff --git a/scd/scdaemon.h b/scd/scdaemon.h index d0bc98e..fcab648 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -125,6 +125,7 @@ void send_status_info (ctrl_t ctrl, const char *keyword, ...) void send_status_direct (ctrl_t ctrl, const char *keyword, const char *args); void scd_update_reader_status_file (void); void send_client_notifications (app_t app, int removal); +void scd_kick_the_loop (void); #endif /*SCDAEMON_H*/ -- 2.30.2