gpgconf: Fix for --homedir.
[gnupg.git] / scd / command.c
index 932db6c..9d978ab 100644 (file)
@@ -34,6 +34,7 @@
 #include <assuan.h>
 #include <ksba.h>
 #include "app-common.h"
+#include "iso7816.h"
 #include "apdu.h" /* Required for apdu_*_reader (). */
 #include "atr.h"
 #include "exechelp.h"
@@ -41,6 +42,7 @@
 #include "ccid-driver.h"
 #endif
 #include "asshelp.h"
+#include "server-help.h"
 
 /* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN */
 #define MAXLEN_PIN 100
@@ -58,7 +60,7 @@
 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
 
 
-/* Macro to flag a removed card.  ENODEV is also tested to catch teh
+/* Macro to flag a removed card.  ENODEV is also tested to catch the
    case of a removed reader.  */
 #define TEST_CARD_REMOVAL(c,r)                              \
        do {                                                 \
        == locked_session->ctrl_backlink->server_local->vreader_idx))
 
 
-/* Flag indicating that the reader has been disabled.  */
-static int reader_disabled;
-
-
 /* This structure is used to keep track of user readers.  To
    eventually accommodate this structure for RFID cards, where more
    than one card is used per reader, we name it virtual reader.  */
@@ -131,10 +129,6 @@ struct server_local_s
      continue operation. */
   int card_removed;
 
-  /* Flag indicating that the application context needs to be released
-     at the next opportunity.  */
-  int app_ctx_marked_for_release;
-
   /* A disconnect command has been sent.  */
   int disconnect_allowed;
 
@@ -211,64 +205,34 @@ update_card_removed (int vrdr, int value)
     return;
 
   for (sl=session_list; sl; sl = sl->next_session)
-    if (sl->ctrl_backlink
-        && sl->ctrl_backlink->server_local->vreader_idx == vrdr)
-      {
-        sl->card_removed = value;
-      }
-  /* Let the card application layer know about the removal.  */
-  if (value)
-    application_notify_card_reset (vreader_slot (vrdr));
-}
-
-
-/* Check whether the option NAME appears in LINE.  Returns 1 or 0. */
-static int
-has_option (const char *line, const char *name)
-{
-  const char *s;
-  int n = strlen (name);
-
-  s = strstr (line, name);
-  return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n)));
-}
-
-/* Same as has_option but does only test for the name of the option
-   and ignores an argument, i.e. with NAME being "--hash" it would
-   return a pointer for "--hash" as well as for "--hash=foo".  If
-   there is no such option NULL is returned.  The pointer returned
-   points right behind the option name, this may be an equal sign, Nul
-   or a space.  */
-static const char *
-has_option_name (const char *line, const char *name)
-{
-  const char *s;
-  int n = strlen (name);
-
-  s = strstr (line, name);
-  return (s && (s == line || spacep (s-1))
-          && (!s[n] || spacep (s+n) || s[n] == '=')) ? (s+n) : NULL;
-}
+    {
+      ctrl_t ctrl = sl->ctrl_backlink;
 
+      if (ctrl && ctrl->server_local->vreader_idx == vrdr)
+        {
+          sl->card_removed = value;
+          if (value)
+            {
+              struct app_ctx_s *app = ctrl->app_ctx;
+              ctrl->app_ctx = NULL;
+              release_application (app);
+            }
+        }
+    }
 
-/* Skip over options.  It is assumed that leading spaces have been
-   removed (this is the case for lines passed to a handler from
-   assuan).  Blanks after the options are also removed. */
-static char *
-skip_options (char *line)
-{
-  while ( *line == '-' && line[1] == '-' )
+  /* Let the card application layer know about the removal.  */
+  if (value)
     {
-      while (*line && !spacep (line))
-        line++;
-      while (spacep (line))
-        line++;
+      int slot = vreader_slot (vrdr);
+
+      log_debug ("Removal of a card: %d\n", vrdr);
+      apdu_close_reader (slot);
+      application_notify_card_reset (slot);
+      vreader_table[vrdr].slot = -1;
     }
-  return line;
 }
 
 
-
 /* Convert the STRING into a newly allocated buffer while translating
    the hex numbers.  Stops at the first invalid character.  Blanks and
    colons are allowed to separate the hex digits.  Returns NULL on
@@ -310,27 +274,36 @@ do_reset (ctrl_t ctrl, int send_reset)
   int vrdr = ctrl->server_local->vreader_idx;
   int slot;
   int err;
+  struct app_ctx_s *app = ctrl->app_ctx;
 
   if (!(vrdr == -1 || (vrdr >= 0 && vrdr < DIM(vreader_table))))
     BUG ();
 
-  /* If there is an active application, release it.  Tell all other
-     sessions using the same application to release the
-     application.  */
-  if (ctrl->app_ctx)
+  /* If there is an active application, release it. */
+  if (app)
     {
-      release_application (ctrl->app_ctx);
       ctrl->app_ctx = NULL;
-      if (send_reset)
+      release_application (app);
+    }
+
+  /* Release the same application which is used by other sessions.  */
+  if (send_reset)
+    {
+      struct server_local_s *sl;
+
+      for (sl=session_list; sl; sl = sl->next_session)
         {
-          struct server_local_s *sl;
+          ctrl_t c = sl->ctrl_backlink;
 
-          for (sl=session_list; sl; sl = sl->next_session)
-            if (sl->ctrl_backlink
-                && sl->ctrl_backlink->server_local->vreader_idx == vrdr)
-              {
-                sl->app_ctx_marked_for_release = 1;
-              }
+          if (c && c != ctrl && c->server_local->vreader_idx == vrdr)
+            {
+              struct app_ctx_s *app0 = c->app_ctx;
+              if (app0)
+                {
+                  c->app_ctx = NULL;
+                  release_application (app0);
+                }
+            }
         }
     }
 
@@ -348,8 +321,8 @@ do_reset (ctrl_t ctrl, int send_reset)
         case SW_HOST_CARD_INACTIVE:
           break;
         default:
-         apdu_close_reader (slot);
-          vreader_table[vrdr].slot = slot = -1;
+          apdu_close_reader (slot);
+          vreader_table[vrdr].slot = -1;
           break;
         }
     }
@@ -444,9 +417,7 @@ get_current_reader (void)
   /* Try to open the reader. */
   if (vr->slot == -1)
     {
-      int no_service_flag;
-
-      vr->slot = apdu_open_reader (opt.reader_port, &no_service_flag);
+      vr->slot = apdu_open_reader (opt.reader_port);
 
       /* If we still don't have a slot, we have no readers.
         Invalidate for now until a reader is attached. */
@@ -454,12 +425,6 @@ get_current_reader (void)
        {
          vr->valid = 0;
        }
-
-      if (no_service_flag)
-        {
-          log_info ("no card services - disabling scdaemon\n");
-          reader_disabled = 1;
-        }
     }
 
   /* Return the vreader index or -1.  */
@@ -474,9 +439,6 @@ open_card (ctrl_t ctrl, const char *apptype)
   gpg_error_t err;
   int vrdr;
 
-  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. */
@@ -486,16 +448,6 @@ open_card (ctrl_t ctrl, const char *apptype)
   if ( IS_LOCKED (ctrl) )
     return gpg_error (GPG_ERR_LOCKED);
 
-  /* If the application has been marked for release do it now.  We
-     can't do it immediately in do_reset because the application may
-     still be in use.  */
-  if (ctrl->server_local->app_ctx_marked_for_release)
-    {
-      ctrl->server_local->app_ctx_marked_for_release = 0;
-      release_application (ctrl->app_ctx);
-      ctrl->app_ctx = NULL;
-    }
-
   /* If we are already initialized for one specific application we
      need to check that the client didn't requested a specific
      application different from the one in use before we continue. */
@@ -512,7 +464,7 @@ open_card (ctrl_t ctrl, const char *apptype)
     vrdr = get_current_reader ();
   ctrl->server_local->vreader_idx = vrdr;
   if (vrdr == -1)
-    err = gpg_error (reader_disabled? GPG_ERR_NOT_OPERATIONAL: GPG_ERR_CARD);
+    err = gpg_error (GPG_ERR_CARD);
   else
     {
       /* Fixme: We should move the apdu_connect call to
@@ -529,8 +481,8 @@ open_card (ctrl_t ctrl, const char *apptype)
           else if (sw == SW_HOST_CARD_INACTIVE)
             err = gpg_error (GPG_ERR_CARD_RESET);
           else
-            err = gpg_error (GPG_ERR_CARD);
-       }
+            err = gpg_error (GPG_ERR_ENODEV);
+        }
       else
         err = select_application (ctrl, slot, apptype, &ctrl->app_ctx);
     }
@@ -543,14 +495,14 @@ open_card (ctrl_t ctrl, const char *apptype)
 static const char hlp_serialno[] =
   "SERIALNO [<apptype>]\n"
   "\n"
-  "Return the serial number of the card using a status reponse.  This\n"
+  "Return the serial number of the card using a status response.  This\n"
   "function should be used to check for the presence of a card.\n"
   "\n"
   "If APPTYPE is given, an application of that type is selected and an\n"
   "error is returned if the application is not supported or available.\n"
   "The default is to auto-select the application using a hardwired\n"
   "preference system.  Note, that a future extension to this function\n"
-  "may allow to specify a list and order of applications to try.\n"
+  "may enable specifying a list and order of applications to try.\n"
   "\n"
   "This function is special in that it can be used to reset the card.\n"
   "Most other functions will return an error when a card change has\n"
@@ -570,7 +522,7 @@ cmd_serialno (assuan_context_t ctx, char *line)
 
   /* Clear the remove flag so that the open_card is able to reread it.  */
  retry:
-  if (!reader_disabled && ctrl->server_local->card_removed)
+  if (ctrl->server_local->card_removed)
     {
       if ( IS_LOCKED (ctrl) )
         return gpg_error (GPG_ERR_LOCKED);
@@ -681,9 +633,18 @@ cmd_learn (assuan_context_t ctx, char *line)
      knows about this card */
   if (!only_keypairinfo)
     {
+      int slot;
+      const char *reader;
       char *serial;
       time_t stamp;
 
+      slot = vreader_slot (ctrl->server_local->vreader_idx);
+      reader = apdu_get_reader_name (slot);
+      if (!reader)
+        return out_of_core ();
+      send_status_direct (ctrl, "READER", reader);
+      /* No need to free the string of READER.  */
+
       rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp);
       if (rc)
         return rc;
@@ -700,8 +661,8 @@ cmd_learn (assuan_context_t ctx, char *line)
         {
           char *command;
 
-          rc = estream_asprintf (&command, "KNOWNCARDP %s %lu",
-                                 serial, (unsigned long)stamp);
+          rc = gpgrt_asprintf (&command, "KNOWNCARDP %s %lu",
+                               serial, (unsigned long)stamp);
           if (rc < 0)
             {
               xfree (serial);
@@ -818,10 +779,8 @@ cmd_readkey (assuan_context_t ctx, char *line)
 
   rc = ksba_cert_new (&kc);
   if (rc)
-    {
-      xfree (cert);
-      goto leave;
-    }
+    goto leave;
+
   rc = ksba_cert_init_from_mem (kc, cert, ncert);
   if (rc)
     {
@@ -923,13 +882,13 @@ pin_cb (void *opaque, const char *info, char **retstr)
 
   if (!retstr)
     {
-      /* We prompt for keypad entry.  To make sure that the popup has
+      /* We prompt for pinpad entry.  To make sure that the popup has
          been show we use an inquire and not just a status message.
          We ignore any value returned.  */
       if (info)
         {
-          log_debug ("prompting for keypad entry '%s'\n", info);
-          rc = estream_asprintf (&command, "POPUPKEYPADPROMPT %s", info);
+          log_debug ("prompting for pinpad entry '%s'\n", info);
+          rc = gpgrt_asprintf (&command, "POPUPPINPADPROMPT %s", info);
           if (rc < 0)
             return gpg_error (gpg_err_code_from_errno (errno));
           rc = assuan_inquire (ctx, command, &value, &valuelen, MAXLEN_PIN);
@@ -937,8 +896,8 @@ pin_cb (void *opaque, const char *info, char **retstr)
         }
       else
         {
-          log_debug ("dismiss keypad entry prompt\n");
-          rc = assuan_inquire (ctx, "DISMISSKEYPADPROMPT",
+          log_debug ("dismiss pinpad entry prompt\n");
+          rc = assuan_inquire (ctx, "DISMISSPINPADPROMPT",
                                &value, &valuelen, MAXLEN_PIN);
         }
       if (!rc)
@@ -949,7 +908,7 @@ pin_cb (void *opaque, const char *info, char **retstr)
   *retstr = NULL;
   log_debug ("asking for PIN '%s'\n", info);
 
-  rc = estream_asprintf (&command, "NEEDPIN %s", info);
+  rc = gpgrt_asprintf (&command, "NEEDPIN %s", info);
   if (rc < 0)
     return gpg_error (gpg_err_code_from_errno (errno));
 
@@ -1103,6 +1062,7 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line)
   unsigned char *outdata;
   size_t outdatalen;
   char *keyidstr;
+  unsigned int infoflags;
 
   if ( IS_LOCKED (ctrl) )
     return gpg_error (GPG_ERR_LOCKED);
@@ -1117,7 +1077,7 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line)
                      keyidstr,
                      pin_cb, ctx,
                      ctrl->in_data.value, ctrl->in_data.valuelen,
-                     &outdata, &outdatalen);
+                     &outdata, &outdatalen, &infoflags);
 
   xfree (keyidstr);
   if (rc)
@@ -1126,6 +1086,13 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line)
     }
   else
     {
+      /* If the card driver told us that there is no padding, send a
+         status line.  If there is a padding it is assumed that the
+         caller knows what padding is used.  It would have been better
+         to always send that information but for backward
+         compatibility we can't do that.  */
+      if ((infoflags & APP_DECIPHER_INFO_NOPAD))
+        send_status_direct (ctrl, "PADDING", "0");
       rc = assuan_send_data (ctx, outdata, outdatalen);
       xfree (outdata);
       if (rc)
@@ -1146,7 +1113,7 @@ static const char hlp_getattr[] =
   "returned through status message, see the LEARN command for details.\n"
   "\n"
   "However, the current implementation assumes that Name is not escaped;\n"
-  "this works as long as noone uses arbitrary escaping. \n"
+  "this works as long as no one uses arbitrary escaping. \n"
   "\n"
   "Note, that this function may even be used on a locked card.";
 static gpg_error_t
@@ -1184,7 +1151,7 @@ static const char hlp_setattr[] =
   "application.  NAME and VALUE must be percent and '+' escaped.\n"
   "\n"
   "However, the current implementation assumes that NAME is not\n"
-  "escaped; this works as long as noone uses arbitrary escaping.\n"
+  "escaped; this works as long as no one uses arbitrary escaping.\n"
   "\n"
   "A PIN will be requested for most NAMEs.  See the corresponding\n"
   "setattr function of the actually used application (app-*.c) for\n"
@@ -1486,7 +1453,7 @@ static const char hlp_passwd[] =
   "PASSWD [--reset] [--nullpin] <chvno>\n"
   "\n"
   "Change the PIN or, if --reset is given, reset the retry counter of\n"
-  "the card holder verfication vector CHVNO.  The option --nullpin is\n"
+  "the card holder verification vector CHVNO.  The option --nullpin is\n"
   "used for TCOS cards to set the initial PIN.  The format of CHVNO\n"
   "depends on the card application.";
 static gpg_error_t
@@ -1787,13 +1754,14 @@ static gpg_error_t
 cmd_restart (assuan_context_t ctx, char *line)
 {
   ctrl_t ctrl = assuan_get_pointer (ctx);
+  struct app_ctx_s *app = ctrl->app_ctx;
 
   (void)line;
 
-  if (ctrl->app_ctx)
+  if (app)
     {
-      release_application (ctrl->app_ctx);
       ctrl->app_ctx = NULL;
+      release_application (app);
     }
   if (locked_session && ctrl->server_local == locked_session)
     {
@@ -1836,7 +1804,7 @@ static const char hlp_apdu[] =
   "  S CARD-ATR 3BFA1300FF813180450031C173C00100009000B1\n"
   "\n"
   "Using the option --more handles the card status word MORE_DATA\n"
-  "(61xx) and concatenates all reponses to one block.\n"
+  "(61xx) and concatenates all responses to one block.\n"
   "\n"
   "Using the option \"--exlen\" the returned APDU may use extended\n"
   "length up to N bytes.  If N is not given a default value is used\n"
@@ -2074,14 +2042,10 @@ scd_command_handler (ctrl_t ctrl, int fd)
   session_list = ctrl->server_local;
   ctrl->server_local->ctrl_backlink = ctrl;
   ctrl->server_local->assuan_ctx = ctx;
-  ctrl->server_local->vreader_idx = -1;
 
   /* We open the reader right at startup so that the ticker is able to
      update the status file. */
-  if (ctrl->server_local->vreader_idx == -1)
-    {
-      ctrl->server_local->vreader_idx = get_current_reader ();
-    }
+  ctrl->server_local->vreader_idx = get_current_reader ();
 
   /* Command processing loop. */
   for (;;)
@@ -2122,7 +2086,7 @@ scd_command_handler (ctrl_t ctrl, int fd)
           BUG ();
       sl->next_session = ctrl->server_local->next_session;
     }
-  stopme = ctrl->server_local->stopme || reader_disabled;
+  stopme = ctrl->server_local->stopme;
   xfree (ctrl->server_local);
   ctrl->server_local = NULL;
 
@@ -2299,9 +2263,7 @@ update_reader_status_file (int set_card_removed_flag)
       if (sw_apdu == SW_HOST_NO_READER)
         {
           /* Most likely the _reader_ has been unplugged.  */
-          application_notify_card_reset (vr->slot);
-         apdu_close_reader (vr->slot);
-          vr->slot = -1;
+          apdu_close_reader (vr->slot);
           status = 0;
           changed = vr->changed;
         }
@@ -2326,7 +2288,7 @@ update_reader_status_file (int set_card_removed_flag)
             depends on how client sessions will associate the reader
             status with their session.  */
           snprintf (templ, sizeof templ, "reader_%d.status", vr->slot);
-          fname = make_filename (opt.homedir, templ, NULL );
+          fname = make_filename (gnupg_homedir (), templ, NULL );
           fp = fopen (fname, "w");
           if (fp)
             {
@@ -2345,8 +2307,8 @@ update_reader_status_file (int set_card_removed_flag)
             char *homestr, *envstr;
             gpg_error_t err;
 
-            homestr = make_filename (opt.homedir, NULL);
-            if (estream_asprintf (&envstr, "GNUPGHOME=%s", homestr) < 0)
+            homestr = make_filename (gnupg_homedir (), NULL);
+            if (gpgrt_asprintf (&envstr, "GNUPGHOME=%s", homestr) < 0)
               log_error ("out of core while building environment\n");
             else
               {
@@ -2368,7 +2330,7 @@ update_reader_status_file (int set_card_removed_flag)
                            (status & 2)? "PRESENT": "NOCARD");
                 args[8] = NULL;
 
-                fname = make_filename (opt.homedir, "scd-event", NULL);
+                fname = make_filename (gnupg_homedir (), "scd-event", NULL);
                 err = gnupg_spawn_process_detached (fname, args, envs);
                 if (err && gpg_err_code (err) != GPG_ERR_ENOENT)
                   log_error ("failed to run event handler '%s': %s\n",
@@ -2379,10 +2341,8 @@ update_reader_status_file (int set_card_removed_flag)
             xfree (homestr);
           }
 
-          /* Set the card removed flag for all current sessions.  We
-             will set this on any card change because a reset or
-             SERIALNO request must be done in any case.  */
-          if (vr->any && set_card_removed_flag)
+          /* Set the card removed flag for all current sessions.  */
+          if (vr->any && vr->status == 0 && set_card_removed_flag)
             update_card_removed (idx, 1);
 
           vr->any = 1;