gpg: Also detect a plaintext packet before an encrypted packet.
[gnupg.git] / scd / app.c
index fa05475..f3f1205 100644 (file)
--- a/scd/app.c
+++ b/scd/app.c
 #include <npth.h>
 
 #include "scdaemon.h"
 #include <npth.h>
 
 #include "scdaemon.h"
-#include "exechelp.h"
+#include "../common/exechelp.h"
 #include "app-common.h"
 #include "iso7816.h"
 #include "apdu.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;
 
 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);
 }
 
   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)
    we have support for it though.  */
 static int
 is_app_allowed (const char *name)
@@ -136,45 +136,38 @@ 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
 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)
     {
 
   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);
 
       if (sw)
         err = gpg_error (GPG_ERR_CARD_RESET);
 
-      /* Release the same application which is used by other sessions.  */
-      send_client_notifications (app);
+      app->reset_requested = 1;
+      unlock_app (app);
+
+      scd_kick_the_loop ();
+      gnupg_sleep (1);
     }
   else
     {
       ctrl->app_ctx = NULL;
     }
   else
     {
       ctrl->app_ctx = NULL;
-      release_application_internal (app);
+      release_application (app, 0);
     }
 
     }
 
-  unlock_app (app);
   return err;
 }
 
 static gpg_error_t
   return err;
 }
 
 static gpg_error_t
-app_new_register (int slot, ctrl_t ctrl, const char *name)
+app_new_register (int slot, ctrl_t ctrl, const char *name,
+                  int periodical_check_needed)
 {
   gpg_error_t err = 0;
   app_t app = NULL;
 {
   gpg_error_t err = 0;
   app_t app = NULL;
@@ -192,6 +185,7 @@ app_new_register (int slot, ctrl_t ctrl, const char *name)
     }
 
   app->slot = slot;
     }
 
   app->slot = slot;
+  app->card_status = (unsigned int)-1;
 
   if (npth_mutex_init (&app->lock, NULL))
     {
 
   if (npth_mutex_init (&app->lock, NULL))
     {
@@ -214,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)
     {
      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)
       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)
       if (!err)
         err = iso7816_read_binary (slot, 0, 0, &result, &resultlen);
       if (!err)
@@ -302,7 +296,7 @@ app_new_register (int slot, ctrl_t ctrl, const char *name)
       return err;
     }
 
       return err;
     }
 
-  app->require_get_status = 1;   /* For token, this can be 0.  */
+  app->periodical_check_needed = periodical_check_needed;
 
   npth_mutex_lock (&app_list_lock);
   app->next = app_top;
 
   npth_mutex_lock (&app_list_lock);
   app->next = app_top;
@@ -317,63 +311,91 @@ app_new_register (int slot, ctrl_t ctrl, const char *name)
    and return a context.  Returns an error code and stores NULL at
    R_APP if no application was found or no card is present. */
 gpg_error_t
    and return a context.  Returns 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, const char *name, app_t *r_app, int scan)
+select_application (ctrl_t ctrl, const char *name, app_t *r_app,
+                    int scan, const unsigned char *serialno_bin,
+                    size_t serialno_bin_len)
 {
 {
-  gpg_error_t err;
-  app_t app;
-  int slot;
+  gpg_error_t err = 0;
+  app_t a, a_prev = NULL;
 
   *r_app = NULL;
 
 
   *r_app = NULL;
 
-  if (scan
-      /* FIXME: Here, we can change code to support multiple readers.
-         For now, we only open a single reader.
-      */
-      && !app_top)
+  if (scan || !app_top)
     {
     {
-      slot = apdu_open_reader (opt.reader_port);
-      if (slot >= 0)
+      struct dev_list *l;
+      int new_app = 0;
+
+      /* Scan the devices to find new device(s).  */
+      err = apdu_dev_list_start (opt.reader_port, &l);
+      if (err)
+        return err;
+
+      while (1)
         {
         {
-          int sw = apdu_connect (slot);
+          int slot;
+          int periodical_check_needed_this;
 
 
-          if (sw == SW_HOST_CARD_INACTIVE)
+          slot = apdu_open_reader (l, !app_top);
+          if (slot < 0)
+            break;
+
+          periodical_check_needed_this = apdu_connect (slot);
+          if (periodical_check_needed_this < 0)
             {
             {
-              /* Try again.  */
-              sw = apdu_reset (slot);
+              /* We close a reader with no card.  */
+              err = gpg_error (GPG_ERR_ENODEV);
             }
             }
-
-          if (!sw || sw == SW_HOST_ALREADY_CONNECTED)
-            err = 0;
           else
           else
-            err = gpg_error (GPG_ERR_ENODEV);
+            {
+              err = app_new_register (slot, ctrl, name,
+                                      periodical_check_needed_this);
+              new_app++;
+            }
+
+          if (err)
+            apdu_close_reader (slot);
         }
         }
-      else
-        err = gpg_error (GPG_ERR_ENODEV);
 
 
-      if (!err)
-        err = app_new_register (slot, ctrl, name);
-      else
-        apdu_close_reader (slot);
+      apdu_dev_list_finish (l);
+
+      /* If new device(s), kick the scdaemon loop.  */
+      if (new_app)
+        scd_kick_the_loop ();
     }
     }
-  else
-    err = 0;
 
 
-  if (!err)
-    app = app_top;
-  else
-    app = NULL;
+  npth_mutex_lock (&app_list_lock);
+  for (a = app_top; a; a = a->next)
+    {
+      lock_app (a, ctrl);
+      if (serialno_bin == NULL)
+        break;
+      if (a->serialnolen == serialno_bin_len
+          && !memcmp (a->serialno, serialno_bin, a->serialnolen))
+        break;
+      unlock_app (a);
+      a_prev = a;
+    }
 
 
-  if (app)
+  if (a)
     {
     {
-      lock_app (app, ctrl);
-      err = check_conflict (app, name);
+      err = check_conflict (a, name);
       if (!err)
         {
       if (!err)
         {
-          app->ref_count++;
-          *r_app = app;
-        }
-      unlock_app (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
+    err = gpg_error (GPG_ERR_ENODEV);
+
+  npth_mutex_unlock (&app_list_lock);
 
   return err;
 }
 
   return err;
 }
@@ -441,6 +463,8 @@ deallocate_app (app_t app)
     }
 
   xfree (app->serialno);
     }
 
   xfree (app->serialno);
+
+  unlock_app (app);
   xfree (app);
 }
 
   xfree (app);
 }
 
@@ -450,7 +474,7 @@ deallocate_app (app_t app)
    actually deferring the deallocation to allow for a later reuse by
    a new connection. */
 void
    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;
 {
   if (!app)
     return;
@@ -460,9 +484,15 @@ release_application (app_t app)
      is using the card - this way the PIN cache and other cached data
      are preserved.  */
 
      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);
 }
 
 
 }
 
 
@@ -511,33 +541,23 @@ app_munge_serialno (app_t app)
 
 
 
 
 
 
-/* Retrieve the serial number and the time of the last update of the
-   card.  The serial number is returned as a malloced string (hex
-   encoded) in SERIAL and the time of update is returned in STAMP.  If
-   no update time is available the returned value is 0.  Caller must
-   free SERIAL unless the function returns an error.  If STAMP is not
-   of interest, NULL may be passed. */
-gpg_error_t
-app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp)
+/* Retrieve the serial number of the card.  The serial number is
+   returned as a malloced string (hex encoded) in SERIAL.  Caller must
+   free SERIAL unless the function returns an error.  */
+char *
+app_get_serialno (app_t app)
 {
 {
-  char *buf;
-
-  if (!app || !serial)
-    return gpg_error (GPG_ERR_INV_VALUE);
+  char *serial;
 
 
-  *serial = NULL;
-  if (stamp)
-    *stamp = 0; /* not available */
+  if (!app)
+    return NULL;
 
   if (!app->serialnolen)
 
   if (!app->serialnolen)
-    buf = xtrystrdup ("FF7F00");
+    serial = xtrystrdup ("FF7F00");
   else
   else
-    buf = bin2hex (app->serialno, app->serialnolen, NULL);
-  if (!buf)
-    return gpg_error_from_syserror ();
+    serial = bin2hex (app->serialno, app->serialnolen, NULL);
 
 
-  *serial = buf;
-  return 0;
+  return serial;
 }
 
 
 }
 
 
@@ -550,15 +570,12 @@ app_write_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
 
   if (!app)
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (!app)
     return gpg_error (GPG_ERR_INV_VALUE);
-  if (!app->ref_count)
-    return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
   if (!app->fnc.learn_status)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
 
   /* We do not send APPTYPE if only keypairinfo is requested.  */
   if (app->apptype && !(flags & 1))
   if (!app->fnc.learn_status)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
 
   /* We do not send APPTYPE if only keypairinfo is requested.  */
   if (app->apptype && !(flags & 1))
-    send_status_info (ctrl, "APPTYPE",
-                      app->apptype, strlen (app->apptype), NULL, 0);
+    send_status_direct (ctrl, "APPTYPE", app->apptype);
   err = lock_app (app, ctrl);
   if (err)
     return err;
   err = lock_app (app, ctrl);
   if (err)
     return err;
@@ -639,20 +656,18 @@ app_getattr (app_t app, ctrl_t ctrl, const char *name)
 
   if (app->apptype && name && !strcmp (name, "APPTYPE"))
     {
 
   if (app->apptype && name && !strcmp (name, "APPTYPE"))
     {
-      send_status_info (ctrl, "APPTYPE",
-                        app->apptype, strlen (app->apptype), NULL, 0);
+      send_status_direct (ctrl, "APPTYPE", app->apptype);
       return 0;
     }
   if (name && !strcmp (name, "SERIALNO"))
     {
       char *serial;
       return 0;
     }
   if (name && !strcmp (name, "SERIALNO"))
     {
       char *serial;
-      time_t stamp;
-      int rc;
 
 
-      rc = app_get_serial_and_stamp (app, &serial, &stamp);
-      if (rc)
-        return rc;
-      send_status_info (ctrl, "SERIALNO", serial, strlen (serial), NULL, 0);
+      serial = app_get_serialno (app);
+      if (!serial)
+        return gpg_error (GPG_ERR_INV_VALUE);
+
+      send_status_direct (ctrl, "SERIALNO", serial);
       xfree (serial);
       return 0;
     }
       xfree (serial);
       return 0;
     }
@@ -922,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
 
 
 /* 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,
    operations.  Its use is highly application dependent. */
 gpg_error_t
 app_check_pin (app_t app, ctrl_t ctrl, const char *keyidstr,
@@ -1006,21 +1021,26 @@ report_change (int slot, int old_status, int cur_status)
   xfree (homestr);
 }
 
   xfree (homestr);
 }
 
-void
+int
 scd_update_reader_status_file (void)
 {
   app_t a, app_next;
 scd_update_reader_status_file (void)
 {
   app_t a, app_next;
+  int periodical_check_needed = 0;
 
   npth_mutex_lock (&app_list_lock);
   for (a = app_top; a; a = app_next)
     {
 
   npth_mutex_lock (&app_list_lock);
   for (a = app_top; a; a = app_next)
     {
+      int sw;
+      unsigned int status;
+
+      lock_app (a, NULL);
       app_next = a->next;
       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);
           sw = apdu_get_status (a->slot, 0, &status);
-
           if (sw == SW_HOST_NO_READER)
             {
               /* Most likely the _reader_ has been unplugged.  */
           if (sw == SW_HOST_NO_READER)
             {
               /* Most likely the _reader_ has been unplugged.  */
@@ -1029,26 +1049,42 @@ scd_update_reader_status_file (void)
           else if (sw)
             {
               /* Get status failed.  Ignore that.  */
           else if (sw)
             {
               /* Get status failed.  Ignore that.  */
+              if (a->periodical_check_needed)
+                periodical_check_needed = 1;
+              unlock_app (a);
               continue;
             }
               continue;
             }
+        }
 
 
-          if (a->card_status != status)
+      if (a->card_status != status)
+        {
+          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
             {
             {
-              report_change (a->slot, a->card_status, status);
-              send_client_notifications (a);
-
-              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;
+              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);
     }
   npth_mutex_unlock (&app_list_lock);
+
+  return periodical_check_needed;
 }
 
 /* This function must be called once to initialize this module.  This
 }
 
 /* This function must be called once to initialize this module.  This
@@ -1069,3 +1105,21 @@ initialize_module_command (void)
 
   return apdu_init ();
 }
 
   return apdu_init ();
 }
+
+void
+app_send_card_list (ctrl_t ctrl)
+{
+  app_t a;
+  char buf[65];
+
+  npth_mutex_lock (&app_list_lock);
+  for (a = app_top; a; a = a->next)
+    {
+      if (DIM (buf) < 2 * a->serialnolen + 1)
+        continue;
+
+      bin2hex (a->serialno, a->serialnolen, buf);
+      send_status_direct (ctrl, "SERIALNO", buf);
+    }
+  npth_mutex_unlock (&app_list_lock);
+}