gpg: Also detect a plaintext packet before an encrypted packet.
[gnupg.git] / scd / app.c
index 7e72870..f3f1205 100644 (file)
--- a/scd/app.c
+++ b/scd/app.c
 #include <npth.h>
 
 #include "scdaemon.h"
-#include "exechelp.h"
+#include "../common/exechelp.h"
 #include "app-common.h"
 #include "iso7816.h"
 #include "apdu.h"
-#include "tlv.h"
+#include "../common/tlv.h"
 
 static npth_mutex_t app_list_lock;
 static app_t app_top;
@@ -99,7 +99,7 @@ app_dump_state (void)
   npth_mutex_unlock (&app_list_lock);
 }
 
-/* Check wether the application NAME is allowed.  This does not mean
+/* Check whether the application NAME is allowed.  This does not mean
    we have support for it though.  */
 static int
 is_app_allowed (const char *name)
@@ -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;
 }
 
@@ -216,9 +208,9 @@ app_new_register (int slot, ctrl_t ctrl, const char *name,
      We skip this if the undefined application has been requested. */
   if (!want_undefined)
     {
-      err = iso7816_select_file (slot, 0x3F00, 1, NULL, NULL);
+      err = iso7816_select_file (slot, 0x3F00, 1);
       if (!err)
-        err = iso7816_select_file (slot, 0x2F02, 0, NULL, NULL);
+        err = iso7816_select_file (slot, 0x2F02, 0);
       if (!err)
         err = iso7816_read_binary (slot, 0, 0, &result, &resultlen);
       if (!err)
@@ -324,14 +316,14 @@ select_application (ctrl_t ctrl, const char *name, app_t *r_app,
                     size_t serialno_bin_len)
 {
   gpg_error_t err = 0;
-  app_t a;
+  app_t a, a_prev = NULL;
 
   *r_app = NULL;
 
   if (scan || !app_top)
     {
       struct dev_list *l;
-      int periodical_check_needed = 0;
+      int new_app = 0;
 
       /* Scan the devices to find new device(s).  */
       err = apdu_dev_list_start (opt.reader_port, &l);
@@ -357,8 +349,7 @@ select_application (ctrl_t ctrl, const char *name, app_t *r_app,
             {
               err = app_new_register (slot, ctrl, name,
                                       periodical_check_needed_this);
-              if (periodical_check_needed_this)
-                periodical_check_needed = 1;
+              new_app++;
             }
 
           if (err)
@@ -367,9 +358,8 @@ select_application (ctrl_t ctrl, const char *name, app_t *r_app,
 
       apdu_dev_list_finish (l);
 
-      /* If periodical check is needed for new device(s), kick the
-       scdaemon loop.  */
-      if (periodical_check_needed)
+      /* If new device(s), kick the scdaemon loop.  */
+      if (new_app)
         scd_kick_the_loop ();
     }
 
@@ -383,6 +373,7 @@ select_application (ctrl_t ctrl, const char *name, app_t *r_app,
           && !memcmp (a->serialno, serialno_bin, a->serialnolen))
         break;
       unlock_app (a);
+      a_prev = a;
     }
 
   if (a)
@@ -392,7 +383,13 @@ select_application (ctrl_t ctrl, const char *name, app_t *r_app,
         {
           a->ref_count++;
           *r_app = a;
-        }
+          if (a_prev)
+            {
+              a_prev->next = a->next;
+              a->next = app_top;
+              app_top = a;
+            }
+      }
       unlock_app (a);
     }
   else
@@ -466,6 +463,8 @@ deallocate_app (app_t app)
     }
 
   xfree (app->serialno);
+
+  unlock_app (app);
   xfree (app);
 }
 
@@ -475,7 +474,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;
@@ -485,9 +484,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);
 }
 
 
@@ -932,7 +937,7 @@ app_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode,
 
 
 /* Perform a VERIFY operation without doing anything lese.  This may
-   be used to initialze a the PIN cache for long lasting other
+   be used to initialize a the PIN cache for long lasting other
    operations.  Its use is highly application dependent. */
 gpg_error_t
 app_check_pin (app_t app, ctrl_t ctrl, const char *keyidstr,
@@ -1028,20 +1033,27 @@ scd_update_reader_status_file (void)
       int sw;
       unsigned int status;
 
-      sw = apdu_get_status (a->slot, 0, &status);
+      lock_app (a, NULL);
       app_next = a->next;
 
-      if (sw == SW_HOST_NO_READER)
-        {
-          /* Most likely the _reader_ has been unplugged.  */
-          status = 0;
-        }
-      else if (sw)
+      if (a->reset_requested)
+        status = 0;
+      else
         {
-          /* Get status failed.  Ignore that.  */
-          if (a->periodical_check_needed)
-            periodical_check_needed = 1;
-          continue;
+          sw = apdu_get_status (a->slot, 0, &status);
+          if (sw == SW_HOST_NO_READER)
+            {
+              /* Most likely the _reader_ has been unplugged.  */
+              status = 0;
+            }
+          else if (sw)
+            {
+              /* Get status failed.  Ignore that.  */
+              if (a->periodical_check_needed)
+                periodical_check_needed = 1;
+              unlock_app (a);
+              continue;
+            }
         }
 
       if (a->card_status != status)
@@ -1060,12 +1072,14 @@ scd_update_reader_status_file (void)
               a->card_status = status;
               if (a->periodical_check_needed)
                 periodical_check_needed = 1;
+              unlock_app (a);
             }
         }
       else
         {
           if (a->periodical_check_needed)
             periodical_check_needed = 1;
+          unlock_app (a);
         }
     }
   npth_mutex_unlock (&app_list_lock);