Fixed a bunch of little bugs as reported by Fabian Keil.
[gnupg.git] / g10 / card-util.c
index 1d26366..26349d6 100644 (file)
 #define CONTROL_D ('D' - 'A' + 1)
 
 
+static void
+write_sc_op_status (gpg_error_t err)
+{
+  switch (gpg_err_code (err))
+    {
+    case 0:
+      write_status (STATUS_SC_OP_SUCCESS);
+      break;
+#if GNUPG_MAJOR_VERSION != 1
+    case GPG_ERR_CANCELED:
+      write_status_text (STATUS_SC_OP_FAILURE, "1");
+      break;
+    case GPG_ERR_BAD_PIN:
+      write_status_text (STATUS_SC_OP_FAILURE, "2");
+      break;
+    default:
+      write_status (STATUS_SC_OP_FAILURE);
+      break;
+#endif /* GNUPG_MAJOR_VERSION != 1 */
+    }
+}
+
+
 /* Change the PIN of a an OpenPGP card.  This is an interactive
    function. */
 void
@@ -86,25 +109,21 @@ change_pin (int unblock_v2, int allow_admin)
       else
         {
           rc = agent_scd_change_pin (2, info.serialno);
+          write_sc_op_status (rc);
           if (rc)
             tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
           else
-            {
-              write_status (STATUS_SC_OP_SUCCESS);
-              tty_printf ("PIN changed.\n");
-            }
+            tty_printf ("PIN changed.\n");
         }
     }
   else if (!allow_admin)
     {
       rc = agent_scd_change_pin (1, info.serialno);
+      write_sc_op_status (rc);
       if (rc)
        tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
       else
-        {
-          write_status (STATUS_SC_OP_SUCCESS);
-          tty_printf ("PIN changed.\n");
-        }
+        tty_printf ("PIN changed.\n");
     }
   else
     for (;;)
@@ -129,50 +148,42 @@ change_pin (int unblock_v2, int allow_admin)
          {
             /* Change PIN.  */
            rc = agent_scd_change_pin (1, info.serialno);
+            write_sc_op_status (rc);
            if (rc)
              tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
            else
-              {
-                write_status (STATUS_SC_OP_SUCCESS);
-                tty_printf ("PIN changed.\n");
-              }
+              tty_printf ("PIN changed.\n");
          }
        else if (*answer == '2')
          {
             /* Unblock PIN.  */
            rc = agent_scd_change_pin (101, info.serialno);
+            write_sc_op_status (rc);
            if (rc)
              tty_printf ("Error unblocking the PIN: %s\n", gpg_strerror (rc));
            else
-              {
-                write_status (STATUS_SC_OP_SUCCESS);
-                tty_printf ("PIN unblocked and new PIN set.\n");
-              }
+              tty_printf ("PIN unblocked and new PIN set.\n");
           }
        else if (*answer == '3')
          {
             /* Change Admin PIN.  */
            rc = agent_scd_change_pin (3, info.serialno);
+            write_sc_op_status (rc);
            if (rc)
              tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
            else
-              {
-                write_status (STATUS_SC_OP_SUCCESS);
-                tty_printf ("PIN changed.\n");
-              }
+              tty_printf ("PIN changed.\n");
          }
        else if (*answer == '4')
          {
             /* Set a new Reset Code.  */
            rc = agent_scd_change_pin (102, info.serialno);
+            write_sc_op_status (rc);
            if (rc)
              tty_printf ("Error setting the Reset Code: %s\n", 
                           gpg_strerror (rc));
            else
-              {
-                write_status (STATUS_SC_OP_SUCCESS);
-                tty_printf ("Reset Code set.\n");
-              }
+              tty_printf ("Reset Code set.\n");
          }
        else if (*answer == 'q' || *answer == 'Q')
          {
@@ -193,7 +204,9 @@ get_manufacturer (unsigned int no)
     case 0x0002: return "Prism";
     case 0x0003: return "OpenFortress";
     case 0x0004: return "Wewid AB";
+    case 0x0005: return "ZeitControl";
 
+    case 0x002A: return "Magrathea";
       /* 0x00000 and 0xFFFF are defined as test cards per spec,
          0xFFF00 to 0xFFFE are assigned for use with randomly created
          serial numbers.  */
@@ -443,6 +456,10 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen)
       fputs (":\n", fp);
 
       fprintf (fp, "forcepin:%d:::\n", !info.chv1_cached);
+      for (i=0; i < DIM (info.key_attr); i++)
+        if (info.key_attr[0].algo)
+          fprintf (fp, "keyattr:%d:%d:%u:\n", i+1,
+                   info.key_attr[i].algo, info.key_attr[i].nbits);
       fprintf (fp, "maxpinlen:%d:%d:%d:\n",
                    info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]);
       fprintf (fp, "pinretry:%d:%d:%d:\n",
@@ -518,6 +535,16 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen)
         }
       tty_fprintf (fp,    "Signature PIN ....: %s\n",
                    info.chv1_cached? _("not forced"): _("forced"));
+      if (info.key_attr[0].algo)
+        {
+          tty_fprintf (fp,    "Key attributes ...:");
+          for (i=0; i < DIM (info.key_attr); i++)
+            tty_fprintf (fp, " %u%c",
+                         info.key_attr[i].nbits,
+                         info.key_attr[i].algo == 1? 'R':
+                         info.key_attr[i].algo == 17? 'D': '?');
+          tty_fprintf (fp, "\n");
+        }
       tty_fprintf (fp,    "Max. PIN lengths .: %d %d %d\n",
                    info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]);
       tty_fprintf (fp,    "PIN retry counter : %d %d %d\n",
@@ -678,6 +705,7 @@ change_url (void)
   if (rc)
     log_error ("error setting URL: %s\n", gpg_strerror (rc));
   xfree (url);
+  write_sc_op_status (rc);
   return rc;
 }
 
@@ -687,7 +715,6 @@ change_url (void)
 static int
 fetch_url(void)
 {
-#if GNUPG_MAJOR_VERSION == 1
   int rc;
   struct agent_card_info_s info;
 
@@ -727,14 +754,11 @@ fetch_url(void)
     }
 
   return rc;
-#else
-  return 0;
-#endif
 }
 
 
 /* Read data from file FNAME up to MAXLEN characters.  On error return
-   -1 and store NULl at R_BUFFER; on success return the number of
+   -1 and store NULL at R_BUFFER; on success return the number of
    bytes read and store the address of a newly allocated buffer at
    R_BUFFER. */
 static int
@@ -785,6 +809,39 @@ get_data_from_file (const char *fname, size_t maxlen, char **r_buffer)
 }
 
 
+/* Write LENGTH bytes from BUFFER to file FNAME.  Return 0 on
+   success.  */
+static int
+put_data_to_file (const char *fname, const void *buffer, size_t length)
+{
+  FILE *fp;
+  
+  fp = fopen (fname, "wb");
+#if GNUPG_MAJOR_VERSION == 1
+  if (fp && is_secured_file (fileno (fp)))
+    {
+      fclose (fp);
+      fp = NULL;
+      errno = EPERM;
+    }
+#endif
+  if (!fp)
+    {
+      tty_printf (_("can't create `%s': %s\n"), fname, strerror (errno));
+      return -1;
+    }
+          
+  if (length && fwrite (buffer, length, 1, fp) != 1)
+    {
+      tty_printf (_("error writing `%s': %s\n"), fname, strerror (errno));
+      fclose (fp);
+      return -1;
+    }
+  fclose (fp);
+  return 0;
+}
+
+
 static int
 change_login (const char *args)
 {
@@ -823,6 +880,7 @@ change_login (const char *args)
   if (rc)
     log_error ("error setting login data: %s\n", gpg_strerror (rc));
   xfree (data);
+  write_sc_op_status (rc);
   return rc;
 }
 
@@ -868,6 +926,7 @@ change_private_do (const char *args, int nr)
   if (rc)
     log_error ("error setting private DO: %s\n", gpg_strerror (rc));
   xfree (data);
+  write_sc_op_status (rc);
   return rc;
 }
 
@@ -897,6 +956,38 @@ change_cert (const char *args)
   if (rc)
     log_error ("error writing certificate to card: %s\n", gpg_strerror (rc));
   xfree (data);
+  write_sc_op_status (rc);
+  return rc;
+}
+
+
+static int
+read_cert (const char *args)
+{
+  const char *fname;
+  void *buffer;
+  size_t length;
+  int rc;
+
+  if (args && *args == '>')  /* Write it to a file */
+    {
+      for (args++; spacep (args); args++)
+        ;
+      fname = args;
+    }
+  else
+    {
+      tty_printf ("usage error: redirectrion to file required\n");
+      return -1;
+    }
+
+  rc = agent_scd_readcert ("OPENPGP.3", &buffer, &length);
+  if (rc)
+    log_error ("error reading certificate from card: %s\n", gpg_strerror (rc));
+  else
+    rc = put_data_to_file (fname, buffer, length);
+  xfree (buffer);
+  write_sc_op_status (rc);
   return rc;
 }
 
@@ -934,6 +1025,7 @@ change_lang (void)
   if (rc)
     log_error ("error setting lang: %s\n", gpg_strerror (rc));
   xfree (data);
+  write_sc_op_status (rc);
   return rc;
 }
 
@@ -969,6 +1061,7 @@ change_sex (void)
   if (rc)
     log_error ("error setting sex: %s\n", gpg_strerror (rc));
   xfree (data);
+  write_sc_op_status (rc);
   return rc;
 }
 
@@ -1013,6 +1106,7 @@ change_cafpr (int fprno)
                           fprno==3?"CA-FPR-3":"x", fpr, 20, NULL );
   if (rc)
     log_error ("error setting cafpr: %s\n", gpg_strerror (rc));
+  write_sc_op_status (rc);
   return rc;
 }
 
@@ -1038,6 +1132,7 @@ toggle_forcesig (void)
   rc = agent_scd_setattr ("CHV-STATUS-1", newstate? "\x01":"", 1, NULL);
   if (rc)
     log_error ("error toggling signature PIN flag: %s\n", gpg_strerror (rc));
+  write_sc_op_status (rc);
 }
 
 
@@ -1077,7 +1172,7 @@ check_pin_for_key_operation (struct agent_card_info_s *info, int *forced_chv1)
 
   *forced_chv1 = !info->chv1_cached;
   if (*forced_chv1)
-    { /* Switch of the forced mode so that during key generation we
+    { /* Switch off the forced mode so that during key generation we
          don't get bothered with PIN queries for each
          self-signature. */
       rc = agent_scd_setattr ("CHV-STATUS-1", "\x01", 1, info->serialno);
@@ -1095,8 +1190,11 @@ check_pin_for_key_operation (struct agent_card_info_s *info, int *forced_chv1)
          binding signature. */
       rc = agent_scd_checkpin (info->serialno);
       if (rc)
-        log_error ("error checking the PIN: %s\n", gpg_strerror (rc));
-    }
+        {
+          log_error ("error checking the PIN: %s\n", gpg_strerror (rc));
+          write_sc_op_status (rc);
+        }
+  }
   return rc;
 }
 
@@ -1408,7 +1506,7 @@ enum cmdids
     cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY,
     cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR,
     cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
-    cmdUNBLOCK,
+    cmdREADCERT, cmdUNBLOCK,
     cmdINVCMD
   };
 
@@ -1442,6 +1540,7 @@ static struct
     { "unblock" , cmdUNBLOCK,0, N_("unblock the PIN using a Reset Code") },
     /* Note, that we do not announce these command yet. */
     { "privatedo", cmdPRIVATEDO, 0, NULL },
+    { "readcert", cmdREADCERT, 0, NULL },
     { "writecert", cmdWRITECERT, 1, NULL },
     { NULL, cmdINVCMD, 0, NULL } 
   };
@@ -1501,7 +1600,7 @@ card_edit (strlist_t commands)
   int have_commands = !!commands;
   int redisplay = 1;
   char *answer = NULL;
-  int did_checkpin = 0, allow_admin=0;
+  int allow_admin=0;
   char serialnobuf[50];
 
 
@@ -1696,6 +1795,13 @@ card_edit (strlist_t commands)
             change_cert (arg_rest);
           break;
 
+        case cmdREADCERT:
+          if ( arg_number != 3 )
+            tty_printf ("usage: readcert 3 > FILE\n");
+          else
+            read_cert (arg_rest);
+          break;
+
         case cmdFORCESIG:
           toggle_forcesig ();
           break;
@@ -1706,12 +1812,10 @@ card_edit (strlist_t commands)
 
         case cmdPASSWD:
           change_pin (0, allow_admin);
-          did_checkpin = 0; /* Need to reset it of course. */
           break;
 
         case cmdUNBLOCK:
           change_pin (1, allow_admin);
-          did_checkpin = 0; /* Need to reset it of course. */
           break;
 
         case cmdQUIT: