Imported Upstream version 2.2.24
[platform/upstream/gpg2.git] / scd / command.c
index 8c7ca20..769113f 100644 (file)
@@ -40,8 +40,8 @@
 #ifdef HAVE_LIBUSB
 #include "ccid-driver.h"
 #endif
-#include "asshelp.h"
-#include "server-help.h"
+#include "../common/asshelp.h"
+#include "../common/server-help.h"
 
 /* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN */
 #define MAXLEN_PIN 100
@@ -79,7 +79,7 @@ struct server_local_s
   assuan_context_t assuan_ctx;
 
 #ifdef HAVE_W32_SYSTEM
-  unsigned long event_signal;   /* Or 0 if not used. */
+  void *event_signal;           /* Or NULL if not used. */
 #else
   int event_signal;             /* Or 0 if not used. */
 #endif
@@ -138,9 +138,10 @@ hex_to_buffer (const char *string, size_t *r_length)
 
 /* Reset the card and free the application context.  With SEND_RESET
    set to true actually send a RESET to the reader; this is the normal
-   way of calling the function.  */
+   way of calling the function.  If KEEP_LOCK is set and the session
+   is locked that lock wil not be released.  */
 static void
-do_reset (ctrl_t ctrl, int send_reset)
+do_reset (ctrl_t ctrl, int send_reset, int keep_lock)
 {
   app_t app = ctrl->app_ctx;
 
@@ -148,7 +149,7 @@ do_reset (ctrl_t ctrl, int send_reset)
     app_reset (app, ctrl, IS_LOCKED (ctrl)? 0: send_reset);
 
   /* If we hold a lock, unlock now. */
-  if (locked_session && ctrl->server_local == locked_session)
+  if (!keep_lock && locked_session && ctrl->server_local == locked_session)
     {
       locked_session = NULL;
       log_info ("implicitly unlocking due to RESET\n");
@@ -160,9 +161,7 @@ reset_notify (assuan_context_t ctx, char *line)
 {
   ctrl_t ctrl = assuan_get_pointer (ctx);
 
-  (void) line;
-
-  do_reset (ctrl, 1);
+  do_reset (ctrl, 1, has_option (line, "--keep-lock"));
   return 0;
 }
 
@@ -178,7 +177,11 @@ option_handler (assuan_context_t ctx, const char *key, const char *value)
 #ifdef HAVE_W32_SYSTEM
       if (!*value)
         return gpg_error (GPG_ERR_ASS_PARAMETER);
-      ctrl->server_local->event_signal = strtoul (value, NULL, 16);
+#ifdef _WIN64
+      ctrl->server_local->event_signal = (void *)strtoull (value, NULL, 16);
+#else
+      ctrl->server_local->event_signal = (void *)strtoul (value, NULL, 16);
+#endif
 #else
       int i = *value? atoi (value) : -1;
       if (i < 0)
@@ -217,6 +220,7 @@ open_card_with_request (ctrl_t ctrl, const char *apptype, const char *serialno)
   gpg_error_t err;
   unsigned char *serialno_bin = NULL;
   size_t serialno_bin_len = 0;
+  app_t app = ctrl->app_ctx;
 
   /* If we are already initialized for one specific application we
      need to check that the client didn't requested a specific
@@ -224,6 +228,10 @@ open_card_with_request (ctrl_t ctrl, const char *apptype, const char *serialno)
   if (apptype && ctrl->app_ctx)
     return check_application_conflict (apptype, ctrl->app_ctx);
 
+  /* Re-scan USB devices.  Release APP, before the scan.  */
+  ctrl->app_ctx = NULL;
+  release_application (app, 0);
+
   if (serialno)
     serialno_bin = hex_to_buffer (serialno, &serialno_bin_len);
 
@@ -282,6 +290,8 @@ cmd_serialno (assuan_context_t ctx, char *line)
   else
     demand = NULL;
 
+  line = skip_options (line);
+
   /* Clear the remove flag so that the open_card is able to reread it.  */
   if (ctrl->server_local->card_removed)
     ctrl->server_local->card_removed = 0;
@@ -324,7 +334,7 @@ static const char hlp_learn[] =
   "or a \"CANCEL\" to force the function to terminate with a Cancel\n"
   "error message.\n"
   "\n"
-  "With the option --keypairinfo only KEYPARIINFO lstatus lines are\n"
+  "With the option --keypairinfo only KEYPARIINFO status lines are\n"
   "returned.\n"
   "\n"
   "The response of this command is a list of status lines formatted as\n"
@@ -337,6 +347,7 @@ static const char hlp_learn[] =
   "    P15     = PKCS-15 structure used\n"
   "    DINSIG  = DIN SIG\n"
   "    OPENPGP = OpenPGP card\n"
+  "    PIV     = PIV card\n"
   "    NKS     = NetKey card\n"
   "\n"
   "are implemented.  These strings are aliases for the AID\n"
@@ -440,7 +451,7 @@ cmd_learn (assuan_context_t ctx, char *line)
               xfree (serial);
               return rc;
             }
-          /* Not canceled, so we have to proceeed.  */
+          /* Not canceled, so we have to proceed.  */
         }
       xfree (serial);
     }
@@ -895,7 +906,7 @@ cmd_getattr (assuan_context_t ctx, char *line)
 static const char hlp_setattr[] =
   "SETATTR <name> <value> \n"
   "\n"
-  "This command is used to store data on a smartcard.  The allowed\n"
+  "This command is used to store data on a smartcard.  The allowed\n"
   "names and values are depend on the currently selected smartcard\n"
   "application.  NAME and VALUE must be percent and '+' escaped.\n"
   "\n"
@@ -949,7 +960,7 @@ static const char hlp_writecert[] =
   "application. The actual certifciate is requested using the inquiry\n"
   "\"CERTDATA\" and needs to be provided in its raw (e.g. DER) form.\n"
   "\n"
-  "In almost all cases a PIN will be requested.  See the related\n"
+  "In almost all cases a PIN will be requested.  See the related\n"
   "writecert function of the actually used application (app-*.c) for\n"
   "details.";
 static gpg_error_t
@@ -1002,7 +1013,7 @@ cmd_writecert (assuan_context_t ctx, char *line)
 static const char hlp_writekey[] =
   "WRITEKEY [--force] <keyid> \n"
   "\n"
-  "This command is used to store a secret key on a smartcard.  The\n"
+  "This command is used to store a secret key on a smartcard.  The\n"
   "allowed keyids depend on the currently selected smartcard\n"
   "application. The actual keydata is requested using the inquiry\n"
   "\"KEYDATA\" and need to be provided without any protection.  With\n"
@@ -1128,7 +1139,8 @@ cmd_genkey (assuan_context_t ctx, char *line)
   keyno = xtrystrdup (keyno);
   if (!keyno)
     return out_of_core ();
-  rc = app_genkey (ctrl->app_ctx, ctrl, keyno, force? 1:0,
+  rc = app_genkey (ctrl->app_ctx, ctrl, keyno, NULL,
+                   force? APP_GENKEY_FLAG_FORCE : 0,
                    timestamp, pin_cb, ctx);
   xfree (keyno);
 
@@ -1182,12 +1194,13 @@ cmd_random (assuan_context_t ctx, char *line)
 
 \f
 static const char hlp_passwd[] =
-  "PASSWD [--reset] [--nullpin] <chvno>\n"
+  "PASSWD [--reset] [--nullpin] [--clear] <chvno>\n"
   "\n"
   "Change the PIN or, if --reset is given, reset the retry counter of\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.";
+  "used for TCOS cards to set the initial PIN.  The option --clear clears\n"
+  "the security status associated with the PIN so that the PIN needs to\n"
+  "be presented again. The format of CHVNO depends on the card application.";
 static gpg_error_t
 cmd_passwd (assuan_context_t ctx, char *line)
 {
@@ -1200,6 +1213,8 @@ cmd_passwd (assuan_context_t ctx, char *line)
     flags |= APP_CHANGE_FLAG_RESET;
   if (has_option (line, "--nullpin"))
     flags |= APP_CHANGE_FLAG_NULLPIN;
+  if (has_option (line, "--clear"))
+    flags |= APP_CHANGE_FLAG_CLEAR;
 
   line = skip_options (line);
 
@@ -1210,6 +1225,11 @@ cmd_passwd (assuan_context_t ctx, char *line)
     line++;
   *line = 0;
 
+  /* Do not allow other flags aside of --clear. */
+  if ((flags & APP_CHANGE_FLAG_CLEAR) && (flags & ~APP_CHANGE_FLAG_CLEAR))
+    return set_error (GPG_ERR_UNSUPPORTED_OPERATION,
+                      "--clear used with other options");
+
   if ((rc = open_card (ctrl)))
     return rc;
 
@@ -1243,7 +1263,7 @@ static const char hlp_checkpin[] =
   "   entry system, only the regular CHV will get blocked and not the\n"
   "   dangerous CHV3.  IDSTR is the usual card's serial number in hex\n"
   "   notation; an optional fingerprint part will get ignored.  There\n"
-  "   is however a special mode if the IDSTR is sffixed with the\n"
+  "   is however a special mode if the IDSTR is suffixed with the\n"
   "   literal string \"[CHV3]\": In this case the Admin PIN is checked\n"
   "   if and only if the retry counter is still at 3.\n"
   "\n"
@@ -1321,9 +1341,10 @@ cmd_lock (assuan_context_t ctx, char *line)
       npth_sleep (1); /* Better implement an event mechanism. However,
                          for card operations this should be
                          sufficient. */
-      /* FIXME: Need to check that the connection is still alive.
-         This can be done by issuing status messages. */
-      goto retry;
+      /* Send a progress so that we can detect a connection loss.  */
+      rc = send_status_printf (ctrl, "PROGRESS", "scd_locked . 0 0");
+      if (!rc)
+        goto retry;
     }
 #endif /*USE_NPTH*/
 
@@ -1367,30 +1388,26 @@ static const char hlp_getinfo[] =
   "Multi purpose command to return certain information.  \n"
   "Supported values of WHAT are:\n"
   "\n"
-  "version     - Return the version of the program.\n"
-  "pid         - Return the process id of the server.\n"
-  "\n"
-  "socket_name - Return the name of the socket.\n"
-  "\n"
-  "status - Return the status of the current reader (in the future, may\n"
-  "also return the status of all readers).  The status is a list of\n"
-  "one-character flags.  The following flags are currently defined:\n"
-  "  'u'  Usable card present.  This is the normal state during operation.\n"
-  "  'r'  Card removed.  A reset is necessary.\n"
-  "These flags are exclusive.\n"
-  "\n"
-  "reader_list - Return a list of detected card readers.  Does\n"
-  "              currently only work with the internal CCID driver.\n"
-  "\n"
-  "deny_admin  - Returns OK if admin commands are not allowed or\n"
-  "              GPG_ERR_GENERAL if admin commands are allowed.\n"
-  "\n"
-  "app_list    - Return a list of supported applications.  One\n"
-  "              application per line, fields delimited by colons,\n"
-  "              first field is the name.\n"
-  "\n"
-  "card_list   - Return a list of serial numbers of active cards,\n"
-  "              using a status response.";
+  "  version     - Return the version of the program.\n"
+  "  pid         - Return the process id of the server.\n"
+  "  socket_name - Return the name of the socket.\n"
+  "  connections - Return number of active connections.\n"
+  "  status      - Return the status of the current reader (in the future,\n"
+  "                may also return the status of all readers).  The status\n"
+  "                is a list of one-character flags.  The following flags\n"
+  "                are currently defined:\n"
+  "                  'u'  Usable card present.\n"
+  "                  'r'  Card removed.  A reset is necessary.\n"
+  "                These flags are exclusive.\n"
+  "  reader_list - Return a list of detected card readers.  Does\n"
+  "                currently only work with the internal CCID driver.\n"
+  "  deny_admin  - Returns OK if admin commands are not allowed or\n"
+  "                GPG_ERR_GENERAL if admin commands are allowed.\n"
+  "  app_list    - Return a list of supported applications.  One\n"
+  "                application per line, fields delimited by colons,\n"
+  "                first field is the name.\n"
+  "  card_list   - Return a list of serial numbers of active cards,\n"
+  "                using a status response.";
 static gpg_error_t
 cmd_getinfo (assuan_context_t ctx, char *line)
 {
@@ -1417,6 +1434,13 @@ cmd_getinfo (assuan_context_t ctx, char *line)
       else
         rc = gpg_error (GPG_ERR_NO_DATA);
     }
+  else if (!strcmp (line, "connections"))
+    {
+      char numbuf[20];
+
+      snprintf (numbuf, sizeof numbuf, "%d", get_active_connection_count ());
+      rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
+    }
   else if (!strcmp (line, "status"))
     {
       ctrl_t ctrl = assuan_get_pointer (ctx);
@@ -1487,7 +1511,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)
     {
@@ -1628,7 +1652,7 @@ cmd_apdu (assuan_context_t ctx, char *line)
 
       rc = apdu_send_direct (app->slot, exlen,
                              apdu, apdulen, handle_more,
-                             &result, &resultlen);
+                             NULL, &result, &resultlen);
       if (rc)
         log_error ("apdu_send_direct failed: %s\n", gpg_strerror (rc));
       else
@@ -1792,7 +1816,7 @@ scd_command_handler (ctrl_t ctrl, int fd)
     }
 
   /* Cleanup.  We don't send an explicit reset to the card.  */
-  do_reset (ctrl, 0);
+  do_reset (ctrl, 0, 0);
 
   /* Release the server object.  */
   if (session_list == ctrl->server_local)
@@ -1840,7 +1864,8 @@ send_status_info (ctrl_t ctrl, const char *keyword, ...)
 
   p = buf;
   n = 0;
-  while ( (value = va_arg (arg_ptr, const unsigned char *)) )
+  while ( (value = va_arg (arg_ptr, const unsigned char *))
+           && n < DIM (buf)-2 )
     {
       valuelen = va_arg (arg_ptr, size_t);
       if (!valuelen)
@@ -1857,6 +1882,7 @@ send_status_info (ctrl_t ctrl, const char *keyword, ...)
             {
               sprintf (p, "%%%02X", *value);
               p += 3;
+              n += 2;
             }
           else if (*value == ' ')
             *p++ = '+';
@@ -1884,6 +1910,54 @@ send_status_direct (ctrl_t ctrl, const char *keyword, const char *args)
 }
 
 
+/* This status functions expects a printf style format string.  No
+ * filtering of the data is done instead the orintf formatted data is
+ * send using assuan_send_status. */
+gpg_error_t
+send_status_printf (ctrl_t ctrl, const char *keyword, const char *format, ...)
+{
+  gpg_error_t err;
+  va_list arg_ptr;
+  assuan_context_t ctx;
+
+  if (!ctrl || !ctrl->server_local || !(ctx = ctrl->server_local->assuan_ctx))
+    return 0;
+
+  va_start (arg_ptr, format);
+  err = vprint_assuan_status (ctx, keyword, format, arg_ptr);
+  va_end (arg_ptr);
+  return err;
+}
+
+
+void
+popup_prompt (void *opaque, int on)
+{
+  ctrl_t ctrl = opaque;
+
+  if (ctrl)
+    {
+      assuan_context_t ctx = ctrl->server_local->assuan_ctx;
+
+      if (ctx)
+        {
+          const char *cmd;
+          gpg_error_t err;
+          unsigned char *value;
+          size_t valuelen;
+
+          if (on)
+            cmd = "POPUPPINPADPROMPT --ack";
+          else
+            cmd = "DISMISSPINPADPROMPT";
+          err = assuan_inquire (ctx, cmd, &value, &valuelen, 100);
+          if (!err)
+            xfree (value);
+        }
+    }
+}
+
+
 /* Helper to send the clients a status change notification.  */
 void
 send_client_notifications (app_t app, int removal)
@@ -1914,7 +1988,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)
@@ -1923,20 +1997,20 @@ send_client_notifications (app_t app, int removal)
         pid = assuan_get_pid (sl->assuan_ctx);
 
 #ifdef HAVE_W32_SYSTEM
-        handle = (void *)sl->event_signal;
+        handle = sl->event_signal;
         for (kidx=0; kidx < killidx; kidx++)
           if (killed[kidx].pid == pid
               && killed[kidx].handle == handle)
             break;
         if (kidx < killidx)
-          log_info ("event %lx (%p) already triggered for client %d\n",
+          log_info ("event %p (%p) already triggered for client %d\n",
                     sl->event_signal, handle, (int)pid);
         else
           {
-            log_info ("triggering event %lx (%p) for client %d\n",
+            log_info ("triggering event %p (%p) for client %d\n",
                       sl->event_signal, handle, (int)pid);
             if (!SetEvent (handle))
-              log_error ("SetEvent(%lx) failed: %s\n",
+              log_error ("SetEvent(%p) failed: %s\n",
                          sl->event_signal, w32_strerror (-1));
             if (killidx < DIM (killed))
               {