chiark / gitweb /
scd: Fix factory-reset.
authorNIIBE Yutaka <gniibe@fsij.org>
Fri, 17 Feb 2017 08:30:05 +0000 (03:30 -0500)
committerDaniel Kahn Gillmor <dkg@fifthhorseman.net>
Mon, 18 Sep 2017 20:41:12 +0000 (21:41 +0100)
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
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
scd/app.c
scd/command.c
scd/scdaemon.c
scd/scdaemon.h

index b979f5476b9258c8d99378f685f02f360188b0ab..c7a05752104ec00144280b48f76e8fa1c30cce90 100644 (file)
@@ -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);
index 8fb0d45537fd96fc9dc574d79e5ea74f6c75d0cb..3f3f3ef8479e102fe8a3bddd4f4c133dc53cea5d 100644 (file)
--- 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);
 }
index 0ae6d29aa83d076c7f876323092747be7f66415a..b17c4a1096a96f85ab219d09909a36f6904e542d 100644 (file)
@@ -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)
index 74fed44543ce489cd4b614f40ef79fe4ce93b2bb..02f0e722160c60106ea13c01feacc4fa68db7577 100644 (file)
@@ -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;
 \f
 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);
index d0bc98efe21834b62069f020bafa952e19062a07..fcab6489f14f3b5ba90f894957cb0a6094236e55 100644 (file)
@@ -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*/