gpgconf: Fix for --homedir.
[gnupg.git] / scd / command.c
index 343830a..9d978ab 100644 (file)
@@ -42,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
@@ -59,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 {                                                 \
@@ -128,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;
 
@@ -208,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
@@ -307,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);
+                }
+            }
         }
     }
 
@@ -345,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;
         }
     }
@@ -472,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. */
@@ -515,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);
     }
@@ -529,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"
@@ -667,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;
@@ -686,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);
@@ -804,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)
     {
@@ -909,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);
@@ -923,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)
@@ -935,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));
 
@@ -1089,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);
@@ -1103,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)
@@ -1112,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)
@@ -1132,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
@@ -1170,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"
@@ -1472,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
@@ -1773,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)
     {
@@ -1822,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"
@@ -2060,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 (;;)
@@ -2285,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;
         }
@@ -2312,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)
             {
@@ -2331,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
               {
@@ -2354,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",
@@ -2365,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;