Make use of the *_NAME etc macros.
[gnupg.git] / scd / command.c
index afd5ef2..05b50b9 100644 (file)
 #include <ctype.h>
 #include <unistd.h>
 #include <signal.h>
-#ifdef USE_GNU_PTH
-# include <pth.h>
+#ifdef USE_NPTH
+# include <npth.h>
 #endif
 
 #include "scdaemon.h"
 #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"
 #ifdef HAVE_LIBUSB
 #include "ccid-driver.h"
 #endif
-
+#include "asshelp.h"
 
 /* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN */
 #define MAXLEN_PIN 100
@@ -48,6 +49,9 @@
 /* Maximum allowed size of key data as used in inquiries. */
 #define MAXLEN_KEYDATA 4096
 
+/* Maximum allowed total data size for SETDATA.  */
+#define MAXLEN_SETDATA 4096
+
 /* Maximum allowed size of certificate data as used in inquiries. */
 #define MAXLEN_CERTDATA 16384
 
        == 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.  */
@@ -156,7 +156,7 @@ static struct server_local_s *locked_session;
 
 /* While doing a reset we need to make sure that the ticker does not
    call scd_update_reader_status_file while we are using it. */
-static pth_mutex_t status_file_update_lock;
+static npth_mutex_t status_file_update_lock;
 
 \f
 /*-- Local prototypes --*/
@@ -173,10 +173,12 @@ void
 initialize_module_command (void)
 {
   static int initialized;
+  int err;
 
   if (!initialized)
     {
-      if (pth_mutex_init (&status_file_update_lock))
+      err = npth_mutex_init (&status_file_update_lock, NULL);
+      if (!err)
         initialized = 1;
     }
 }
@@ -304,6 +306,7 @@ do_reset (ctrl_t ctrl, int send_reset)
 {
   int vrdr = ctrl->server_local->vreader_idx;
   int slot;
+  int err;
 
   if (!(vrdr == -1 || (vrdr >= 0 && vrdr < DIM(vreader_table))))
     BUG ();
@@ -360,7 +363,8 @@ do_reset (ctrl_t ctrl, int send_reset)
      try to update the file.  Calling update_reader_status_file is
      required to get hold of the new status of the card in the vreader
      table.  */
-  if (!pth_mutex_acquire (&status_file_update_lock, 0, NULL))
+  err = npth_mutex_lock (&status_file_update_lock);
+  if (err)
     {
       log_error ("failed to acquire status_file_update lock\n");
       ctrl->server_local->vreader_idx = -1;
@@ -368,8 +372,10 @@ do_reset (ctrl_t ctrl, int send_reset)
     }
   update_reader_status_file (0);  /* Update slot status table.  */
   update_card_removed (vrdr, 0);  /* Clear card_removed flag.  */
-  if (!pth_mutex_release (&status_file_update_lock))
-    log_error ("failed to release status_file_update lock\n");
+  err = npth_mutex_unlock (&status_file_update_lock);
+  if (err)
+    log_error ("failed to release status_file_update lock: %s\n",
+              strerror (err));
 
   /* Do this last, so that the update_card_removed above does its job.  */
   ctrl->server_local->vreader_idx = -1;
@@ -435,9 +441,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. */
@@ -445,12 +449,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.  */
@@ -465,9 +463,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. */
@@ -503,7 +498,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
@@ -555,14 +550,13 @@ cmd_serialno (assuan_context_t ctx, char *line)
 {
   ctrl_t ctrl = assuan_get_pointer (ctx);
   int rc = 0;
-  char *serial_and_stamp;
   char *serial;
   time_t stamp;
   int retries = 0;
 
   /* 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);
@@ -581,15 +575,10 @@ cmd_serialno (assuan_context_t ctx, char *line)
   if (rc)
     return rc;
 
-  rc = estream_asprintf (&serial_and_stamp, "%s %lu",
-                         serial, (unsigned long)stamp);
+  rc = print_assuan_status (ctx, "SERIALNO", "%s %lu",
+                            serial, (unsigned long)stamp);
   xfree (serial);
-  if (rc < 0)
-    return out_of_core ();
-  rc = 0;
-  assuan_write_status (ctx, "SERIALNO", serial_and_stamp);
-  xfree (serial_and_stamp);
-  return 0;
+  return rc;
 }
 
 
@@ -678,32 +667,32 @@ cmd_learn (assuan_context_t ctx, char *line)
      knows about this card */
   if (!only_keypairinfo)
     {
-      char *serial_and_stamp;
       char *serial;
       time_t stamp;
 
       rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp);
       if (rc)
         return rc;
-      rc = estream_asprintf (&serial_and_stamp, "%s %lu",
-                             serial, (unsigned long)stamp);
-      xfree (serial);
+
+      rc = print_assuan_status (ctx, "SERIALNO", "%s %lu",
+                                serial, (unsigned long)stamp);
       if (rc < 0)
-        return out_of_core ();
-      rc = 0;
-      assuan_write_status (ctx, "SERIALNO", serial_and_stamp);
+        {
+          xfree (serial);
+          return out_of_core ();
+        }
 
       if (!has_option (line, "--force"))
         {
           char *command;
 
-          rc = estream_asprintf (&command, "KNOWNCARDP %s", serial_and_stamp);
+          rc = estream_asprintf (&command, "KNOWNCARDP %s %lu",
+                                 serial, (unsigned long)stamp);
           if (rc < 0)
             {
-              xfree (serial_and_stamp);
+              xfree (serial);
               return out_of_core ();
             }
-          rc = 0;
           rc = assuan_inquire (ctx, command, NULL, NULL, 0);
           xfree (command);
           if (rc)
@@ -711,12 +700,12 @@ cmd_learn (assuan_context_t ctx, char *line)
               if (gpg_err_code (rc) != GPG_ERR_ASS_CANCELED)
                 log_error ("inquire KNOWNCARDP failed: %s\n",
                            gpg_strerror (rc));
-              xfree (serial_and_stamp);
+              xfree (serial);
               return rc;
             }
           /* Not canceled, so we have to proceeed.  */
         }
-      xfree (serial_and_stamp);
+      xfree (serial);
     }
 
   /* Let the application print out its collection of useful status
@@ -848,17 +837,24 @@ cmd_readkey (assuan_context_t ctx, char *line)
 
 \f
 static const char hlp_setdata[] =
-  "SETDATA <hexstring> \n"
+  "SETDATA [--append] <hexstring>\n"
   "\n"
-  "The client should use this command to tell us the data he want to sign.";
+  "The client should use this command to tell us the data he want to sign.\n"
+  "With the option --append, the data is appended to the data set by a\n"
+  "previous SETDATA command.";
 static gpg_error_t
 cmd_setdata (assuan_context_t ctx, char *line)
 {
   ctrl_t ctrl = assuan_get_pointer (ctx);
-  int n;
+  int append;
+  int n, i, off;
   char *p;
   unsigned char *buf;
 
+  append = (ctrl->in_data.value && has_option (line, "--append"));
+
+  line = skip_options (line);
+
   if (locked_session && locked_session != ctrl->server_local)
     return gpg_error (GPG_ERR_LOCKED);
 
@@ -872,15 +868,31 @@ cmd_setdata (assuan_context_t ctx, char *line)
   if ((n&1))
     return set_error (GPG_ERR_ASS_PARAMETER, "odd number of digits");
   n /= 2;
-  buf = xtrymalloc (n);
+  if (append)
+    {
+      if (ctrl->in_data.valuelen + n > MAXLEN_SETDATA)
+        return set_error (GPG_ERR_TOO_LARGE,
+                          "limit on total size of data reached");
+      buf = xtrymalloc (ctrl->in_data.valuelen + n);
+    }
+  else
+    buf = xtrymalloc (n);
   if (!buf)
     return out_of_core ();
 
+  if (append)
+    {
+      memcpy (buf, ctrl->in_data.value, ctrl->in_data.valuelen);
+      off = ctrl->in_data.valuelen;
+    }
+  else
+    off = 0;
+  for (p=line, i=0; i < n; p += 2, i++)
+    buf[off+i] = xtoi_2 (p);
+
   xfree (ctrl->in_data.value);
   ctrl->in_data.value = buf;
-  ctrl->in_data.valuelen = n;
-  for (p=line, n=0; n < ctrl->in_data.valuelen; p += 2, n++)
-    buf[n] = xtoi_2 (p);
+  ctrl->in_data.valuelen = off+n;
   return 0;
 }
 
@@ -897,13 +909,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 = estream_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);
@@ -911,8 +923,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)
@@ -1077,6 +1089,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);
@@ -1091,7 +1104,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)
@@ -1100,6 +1113,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)
@@ -1597,18 +1617,18 @@ cmd_lock (assuan_context_t ctx, char *line)
   else
     locked_session = ctrl->server_local;
 
-#ifdef USE_GNU_PTH
+#ifdef USE_NPTH
   if (rc && has_option (line, "--wait"))
     {
       rc = 0;
-      pth_sleep (1); /* Better implement an event mechanism. However,
-                        for card operations this should be
-                        sufficient. */
+      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;
     }
-#endif /*USE_GNU_PTH*/
+#endif /*USE_NPTH*/
 
   if (rc)
     log_error ("cmd_lock failed: %s\n", gpg_strerror (rc));
@@ -1936,15 +1956,8 @@ cmd_killscd (assuan_context_t ctx, char *line)
   (void)line;
 
   ctrl->server_local->stopme = 1;
-#ifdef ASSUAN_FORCE_CLOSE
   assuan_set_flag (ctx, ASSUAN_FORCE_CLOSE, 1);
   return 0;
-#else
-  /* Actually returning an EOF does not anymore work with modern
-     Libassuan versions.  However we keep that non working code until
-     we make a Libassuan with the force close flag a requirement.  */
-  return gpg_error (GPG_ERR_EOF);
-#endif
 }
 
 
@@ -2103,7 +2116,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;
 
@@ -2263,11 +2276,6 @@ update_reader_status_file (int set_card_removed_flag)
   int idx;
   unsigned int status, changed;
 
-  /* Make sure that a reader has been opened.  Like get_current_reader,
-     this part of the code assumes that there is only one reader.  */
-  if (!vreader_table[0].valid)
-    (void)get_current_reader ();
-
   /* Note, that we only try to get the status, because it does not
      make sense to wait here for a operation to complete.  If we are
      busy working with a card, delays in the status file update should
@@ -2357,7 +2365,7 @@ update_reader_status_file (int set_card_removed_flag)
                 fname = make_filename (opt.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",
+                  log_error ("failed to run event handler '%s': %s\n",
                              fname, gpg_strerror (err));
                 xfree (fname);
                 xfree (envstr);
@@ -2365,10 +2373,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;
@@ -2402,9 +2408,13 @@ update_reader_status_file (int set_card_removed_flag)
 void
 scd_update_reader_status_file (void)
 {
-  if (!pth_mutex_acquire (&status_file_update_lock, 1, NULL))
+  int err;
+  err = npth_mutex_lock (&status_file_update_lock);
+  if (err)
     return; /* locked - give up. */
   update_reader_status_file (1);
-  if (!pth_mutex_release (&status_file_update_lock))
-    log_error ("failed to release status_file_update lock\n");
+  err = npth_mutex_unlock (&status_file_update_lock);
+  if (err)
+    log_error ("failed to release status_file_update lock: %s\n",
+              strerror (err));
 }