* gpg.sgml: Clarify new notation delete feature.
[gnupg.git] / g10 / cardglue.c
index c55579f..d850b56 100644 (file)
@@ -1,5 +1,5 @@
 /* cardglue.c - mainly dispatcher for card related functions.
- * Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -15,7 +15,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
@@ -28,7 +29,6 @@
 #include <errno.h>
 #include <stdarg.h>
 #include <assert.h>
-
 #include "options.h"
 #include "packet.h"
 #include "errors.h"
 #include "apdu.h"
 #include "app-common.h"
 
-/* If we build w/o agent support, assuan.h won't be included and thus
-   we need to define a repalcement for the assuan error type. */
-#ifndef ENABLE_AGENT_SUPPORT
-typedef int assuan_error_t;
-#endif
 
 
 struct ctrl_ctx_s 
@@ -80,6 +75,48 @@ static app_t current_app;
 static assuan_error_t learn_status_cb (void *opaque, const char *line);
 
 
+/* To avoid cluttering the code with bunches of ifdefs we use a few
+   dummy functions instead and defines. */
+#ifndef ENABLE_AGENT_SUPPORT
+
+#define ASSUAN_LINELENGTH 100
+
+static assuan_context_t 
+agent_open (int try, const char *orig_codeset)
+{
+  return NULL;
+}
+
+void 
+agent_close (assuan_context_t ctx)
+{
+}
+
+const char *
+assuan_strerror (assuan_error_t err)
+{
+  return "no Assuan support";
+}
+
+assuan_error_t 
+assuan_transact (assuan_context_t ctx,
+                 const char *command,
+                 assuan_error_t (*data_cb)(void *, const void *, size_t),
+                 void *data_cb_arg,
+                 assuan_error_t (*inquire_cb)(void*, const char *),
+                 void *inquire_cb_arg,
+                 assuan_error_t (*status_cb)(void*, const char *),
+                 void *status_cb_arg)
+{
+  return 100; /* ASSUAN_NOT_IMPLEMENTED */
+}
+assuan_error_t 
+assuan_send_data (assuan_context_t ctx, const void *buffer, size_t length)
+{
+  return 100; /* ASSUAN_NOT_IMPLEMENTED */
+}  
+#endif /*!ENABLE_AGENT_SUPPORT*/
+
 \f
 /* Create a serialno/fpr string from the serial number and the secret
    key.  caller must free the returned string.  There is no error
@@ -252,9 +289,6 @@ app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp)
 
 
 
-
-
-
 /* Release the card info structure. */
 void 
 agent_release_card_info (struct agent_card_info_s *info)
@@ -279,7 +313,7 @@ agent_release_card_info (struct agent_card_info_s *info)
 }
 
 
-/* Print an error message for a failed assuan-Transact and return a
+/* Print an error message for a failed assuan_transact and return a
    gpg error code. No error is printed if RC is 0. */
 static gpg_error_t
 test_transact (int rc, const char *command)
@@ -303,7 +337,7 @@ open_card_via_agent (int *scd_available)
   int rc;
 
   *scd_available = 0;
-  ctx = agent_open (1);
+  ctx = agent_open (1, NULL);
   if (!ctx)
     return NULL;
 
@@ -350,6 +384,7 @@ open_card (void)
   int rc;
   app_t app;
   int did_shutdown = 0;
+  int retry_count = 0;
 
   /* First check whether we can contact a gpg-agent and divert all
      operation to it. This is required because gpg as well as the
@@ -377,6 +412,7 @@ open_card (void)
       slot = apdu_open_reader (default_reader_port);
       if (slot == -1)
         {
+          write_status_text (STATUS_CARDCTRL, "5");
           log_error ("card reader not available\n");
           return NULL;
         }
@@ -385,7 +421,10 @@ open_card (void)
   app = xcalloc (1, sizeof *app);
   app->slot = slot;
   rc = app_select_openpgp (app);
-  if (rc && !opt.batch)
+  if (opt.limit_card_insert_tries 
+      && ++retry_count >= opt.limit_card_insert_tries)
+    ;
+  else if (rc && !opt.batch)
     {
       write_status_text (STATUS_CARDCTRL, "1");
       
@@ -480,10 +519,24 @@ format_cacheid (const char *sn)
   return cacheid;
 }
 
+
+/* If RC is not 0, write an appropriate status message. */
+static void
+status_sc_op_failure (int rc)
+{
+  if (rc == G10ERR_CANCELED)
+    write_status_text (STATUS_SC_OP_FAILURE, "1");
+  else if (rc == G10ERR_BAD_PASS)
+    write_status_text (STATUS_SC_OP_FAILURE, "2");
+  else if (rc)
+    write_status (STATUS_SC_OP_FAILURE);
+}  
+
+
 /* Check that the serial number of the current card (as described by
    APP) matches SERIALNO.  If there is no match and we are not in
    batch mode, present a prompt to insert the desired card.  The
-   function return 0 is the present card is okay, -1 if the user
+   function returnd 0 if the present card is okay, -1 if the user
    selected to insert a new card or an error value.  Note that the
    card context will be closed in all cases except for 0 as return
    value and if it was possible to merely shutdown the reader. */
@@ -493,7 +546,7 @@ check_card_serialno (app_t app, const char *serialno)
   const char *s;
   int ask = 0;
   int n;
-  
+
   for (s = serialno, n=0; *s != '/' && hexdigitp (s); s++, n++)
     ;
   if (n != 32)
@@ -515,17 +568,20 @@ check_card_serialno (app_t app, const char *serialno)
         did_shutdown = 1;
       else
         card_close ();
-      tty_printf (_("Please remove the current card and "
-                    "insert the one with serial number:\n"
-                    "   %.*s\n"), 32, serialno);
+
+      if (!opt.batch)
+        tty_printf (_("Please remove the current card and "
+                      "insert the one with serial number:\n"
+                      "   %.*s\n"), 32, serialno);
 
       sprintf (buf, "1 %.32s", serialno);
       write_status_text (STATUS_CARDCTRL, buf);
 
-      if ( cpr_get_answer_okay_cancel ("cardctrl.change_card.okay",
-                          _("Hit return when ready "
-                            "or enter 'c' to cancel: "),
-                                       1) )
+      if ( !opt.batch
+           && cpr_get_answer_okay_cancel ("cardctrl.change_card.okay",
+                                          _("Hit return when ready "
+                                            "or enter 'c' to cancel: "),
+                                          1) )
         {
           card_close ();
           return -1;
@@ -540,40 +596,6 @@ check_card_serialno (app_t app, const char *serialno)
 }
 
 
-
-/* Return a new malloced string by unescaping the string S.  Escaping
-   is percent escaping and '+'/space mapping.  A binary nul will
-   silently be replaced by a 0xFF.  Function returns NULL to indicate
-   an out of memory status. */
-static char *
-unescape_status_string (const unsigned char *s)
-{
-  char *buffer, *d;
-
-  buffer = d = xmalloc (strlen (s)+1);
-  while (*s)
-    {
-      if (*s == '%' && s[1] && s[2])
-        { 
-          s++;
-          *d = xtoi_2 (s);
-          if (!*d)
-            *d = '\xff';
-          d++;
-          s += 2;
-        }
-      else if (*s == '+')
-        {
-          *d++ = ' ';
-          s++;
-        }
-      else
-        *d++ = *s++;
-    }
-  *d = 0; 
-  return buffer;
-}
-
 /* Take a 20 byte hexencoded string and put it into the the provided
    20 byte buffer FPR in binary format. */
 static int
@@ -633,12 +655,12 @@ learn_status_cb (void *opaque, const char *line)
   else if (keywordlen == 9 && !memcmp (keyword, "DISP-NAME", keywordlen))
     {
       xfree (parm->disp_name);
-      parm->disp_name = unescape_status_string (line);
+      parm->disp_name = unescape_percent_string (line);
     }
   else if (keywordlen == 9 && !memcmp (keyword, "DISP-LANG", keywordlen))
     {
       xfree (parm->disp_lang);
-      parm->disp_lang = unescape_status_string (line);
+      parm->disp_lang = unescape_percent_string (line);
     }
   else if (keywordlen == 8 && !memcmp (keyword, "DISP-SEX", keywordlen))
     {
@@ -647,12 +669,12 @@ learn_status_cb (void *opaque, const char *line)
   else if (keywordlen == 10 && !memcmp (keyword, "PUBKEY-URL", keywordlen))
     {
       xfree (parm->pubkey_url);
-      parm->pubkey_url = unescape_status_string (line);
+      parm->pubkey_url = unescape_percent_string (line);
     }
   else if (keywordlen == 10 && !memcmp (keyword, "LOGIN-DATA", keywordlen))
     {
       xfree (parm->login_data);
-      parm->login_data = unescape_status_string (line);
+      parm->login_data = unescape_percent_string (line);
     }
   else if (keywordlen == 11 && !memcmp (keyword, "SIG-COUNTER", keywordlen))
     {
@@ -662,7 +684,7 @@ learn_status_cb (void *opaque, const char *line)
     {
       char *p, *buf;
 
-      buf = p = unescape_status_string (line);
+      buf = p = unescape_percent_string (line);
       if (buf)
         {
           while (spacep (p))
@@ -739,7 +761,7 @@ learn_status_cb (void *opaque, const char *line)
       int no = keyword[11] - '1';
       assert (no >= 0 && no <= 3);
       xfree (parm->private_do[no]);
-      parm->private_do[no] = unescape_status_string (line);
+      parm->private_do[no] = unescape_percent_string (line);
     }
  
   return 0;
@@ -874,8 +896,18 @@ pin_cb (void *opaque, const char *info, char **retstr)
 
  again:
   if (is_status_enabled())
-    write_status_text (STATUS_NEED_PASSPHRASE_PIN,
-                       isadmin? "OPENPGP 3" : "OPENPGP 1");
+    {
+      if (parm && parm->sn && *parm->sn)
+        {
+          char *buf = xmalloc ( 10 + strlen (parm->sn) + 1);
+          strcpy (stpcpy (buf, isadmin? "OPENPGP 3 ":"OPENPGP 1 "), parm->sn);
+          write_status_text (STATUS_NEED_PASSPHRASE_PIN, buf);
+          xfree (buf);
+        }
+      else  
+        write_status_text (STATUS_NEED_PASSPHRASE_PIN,
+                           isadmin? "OPENPGP 3" : "OPENPGP 1");
+    }
 
   value = ask_passphrase (info, again_text,
                           newpin && isadmin? "passphrase.adminpin.new.ask" :
@@ -892,7 +924,7 @@ pin_cb (void *opaque, const char *info, char **retstr)
   cacheid = NULL;
   again_text = NULL;
   if (!value && canceled)
-    return -1;
+    return G10ERR_CANCELED;
   else if (!value)
     return G10ERR_GENERAL;
 
@@ -900,16 +932,17 @@ pin_cb (void *opaque, const char *info, char **retstr)
     {
       char *value2;
 
-      value2 = ask_passphrase (info, NULL, NULL,
+      value2 = ask_passphrase (info, NULL,
                                "passphrase.pin.repeat", 
                                _("Repeat this PIN: "),
-                              &canceled);
-      if (!value && canceled)
+                               NULL,
+                               &canceled);
+      if (!value2 && canceled)
         {
           xfree (value);
-          return -1;
+          return G10ERR_CANCELED;
         }
-      else if (!value)
+      else if (!value2)
         {
           xfree (value);
           return G10ERR_GENERAL;
@@ -934,10 +967,15 @@ pin_cb (void *opaque, const char *info, char **retstr)
 /* Send a SETATTR command to the SCdaemon. */
 int 
 agent_scd_setattr (const char *name,
-                   const unsigned char *value, size_t valuelen)
+                   const unsigned char *value, size_t valuelen,
+                   const char *serialno)
 {
   app_t app;
   int rc;
+  struct pincb_parm_s parm;
+
+  memset (&parm, 0, sizeof parm);
+  parm.sn = serialno;
 
   app = current_app? current_app : open_card ();
   if (!app)
@@ -975,11 +1013,10 @@ agent_scd_setattr (const char *name,
     }
   else
     {
-      rc = app->fnc.setattr (app, name, pin_cb, NULL, value, valuelen);
+      rc = app->fnc.setattr (app, name, pin_cb, &parm, value, valuelen);
     }
 
-  if (rc)
-    write_status (STATUS_SC_OP_FAILURE);
+  status_sc_op_failure (rc);
   return rc;
 }
 
@@ -997,11 +1034,17 @@ inq_writekey_parms (void *opaque, const char *keyword)
 
 /* Send a WRITEKEY command to the SCdaemon. */
 int 
-agent_scd_writekey (int keyno, const unsigned char *keydata, size_t keydatalen)
+agent_scd_writekey (int keyno, const char *serialno,
+                    const unsigned char *keydata, size_t keydatalen)
 {
   app_t app;
   int rc;
   char line[ASSUAN_LINELENGTH];
+  struct pincb_parm_s parm;
+
+  memset (&parm, 0, sizeof parm);
+  parm.sn = serialno;
+
   app = current_app? current_app : open_card ();
   if (!app)
     return gpg_error (GPG_ERR_CARD);
@@ -1026,12 +1069,11 @@ agent_scd_writekey (int keyno, const unsigned char *keydata, size_t keydatalen)
       snprintf (line, DIM(line)-1, "OPENPGP.%d", keyno);
       line[DIM(line)-1] = 0;
       rc = app->fnc.writekey (app, NULL, line, 0x0001,
-                              pin_cb, NULL,
+                              pin_cb, &parm,
                               keydata, keydatalen);
     }
 
-  if (rc)
-    write_status (STATUS_SC_OP_FAILURE);
+  status_sc_op_failure (rc);
   return rc;
 }
 
@@ -1091,12 +1133,17 @@ genkey_status_cb (void *opaque, const char *line)
 
 /* Send a GENKEY command to the SCdaemon. */
 int 
-agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force)
+agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force,
+                  const char *serialno)
 {
   app_t app;
   char line[ASSUAN_LINELENGTH];
   struct ctrl_ctx_s ctrl;
   int rc;
+  struct pincb_parm_s parm;
+
+  memset (&parm, 0, sizeof parm);
+  parm.sn = serialno;
 
   app = current_app? current_app : open_card ();
   if (!app)
@@ -1121,11 +1168,10 @@ agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force)
       ctrl.status_cb_arg = info;
       rc = app->fnc.genkey (app, &ctrl, line,
                             force? 1:0,
-                            pin_cb, NULL);
+                            pin_cb, &parm);
     }
 
-  if (rc)
-    write_status (STATUS_SC_OP_FAILURE);
+  status_sc_op_failure (rc);
   return rc;
 }
 
@@ -1207,7 +1253,7 @@ agent_scd_pksign (const char *serialno, int hashalgo,
 
   if (rc)
     {
-      write_status (STATUS_SC_OP_FAILURE);
+      status_sc_op_failure (rc);
       if (!app->assuan_ctx)
         agent_clear_pin_cache (serialno);
     }
@@ -1281,21 +1327,26 @@ agent_scd_pkdecrypt (const char *serialno,
 
   if (rc)
     {
-      write_status (STATUS_SC_OP_FAILURE);
+      status_sc_op_failure (rc);
       if (!app->assuan_ctx)
         agent_clear_pin_cache (serialno);
     }
   return rc;
 }
 
-/* Change the PIN of an OpenPGP card or reset the retry counter. */
+/* Change the PIN of an OpenPGP card or reset the retry
+   counter. SERIALNO may be NULL or a hex string finally passed to the
+   passphrase callback. */
 int 
-agent_scd_change_pin (int chvno)
+agent_scd_change_pin (int chvno, const char *serialno)
 {
   app_t app;
-  char chvnostr[20];
   int reset = 0;
   int rc;
+  struct pincb_parm_s parm;
+
+  memset (&parm, 0, sizeof parm);
+  parm.sn = serialno;
 
   reset = (chvno >= 100);
   chvno %= 100;
@@ -1306,17 +1357,25 @@ agent_scd_change_pin (int chvno)
 
   if (app->assuan_ctx)
     {
-      rc = gpg_error (GPG_ERR_CARD);
+      char line[ASSUAN_LINELENGTH];
+
+      snprintf (line, DIM(line)-1, "SCD PASSWD%s %d",
+                reset? " --reset":"", chvno);
+      line[DIM(line)-1] = 0;
+      rc = test_transact (assuan_transact (app->assuan_ctx, line,
+                                           NULL, NULL, NULL, NULL, NULL, NULL),
+                          "SCD PASSWD");
     }
   else
     {
+      char chvnostr[50];
+
       sprintf (chvnostr, "%d", chvno);
       rc = app->fnc.change_pin (app, NULL, chvnostr, reset,
-                                pin_cb, NULL);
+                                pin_cb, &parm);
     }
 
-  if (rc)
-    write_status (STATUS_SC_OP_FAILURE);
+  status_sc_op_failure (rc);
   return rc;
 }
 
@@ -1328,6 +1387,10 @@ agent_scd_checkpin (const char *serialnobuf)
 {
   app_t app;
   int rc;
+  struct pincb_parm_s parm;
+
+  memset (&parm, 0, sizeof parm);
+  parm.sn = serialnobuf;
 
   app = current_app? current_app : open_card ();
   if (!app)
@@ -1335,15 +1398,21 @@ agent_scd_checkpin (const char *serialnobuf)
 
   if (app->assuan_ctx)
     {
-      rc = gpg_error (GPG_ERR_CARD);
+      char line[ASSUAN_LINELENGTH];
+
+      if (15 + strlen (serialnobuf) > DIM(line)-1)
+        return gpg_error (GPG_ERR_CARD);
+      stpcpy (stpcpy (line, "SCD CHECKPIN "), serialnobuf); 
+      rc = test_transact (assuan_transact (app->assuan_ctx, line,
+                                           NULL, NULL, NULL, NULL, NULL, NULL),
+                          "SCD CHECKPIN");
     }
   else
     {
-      rc = app->fnc.check_pin (app, serialnobuf, pin_cb, NULL);
+      rc = app->fnc.check_pin (app, serialnobuf, pin_cb, &parm);
     }
 
-  if (rc)
-    write_status (STATUS_SC_OP_FAILURE);
+  status_sc_op_failure (rc);
   return rc;
 }