* Makefile.am (DISTCHECK_CONFIGURE_FLAGS): New.
[gnupg.git] / g10 / app-openpgp.c
index 096e66e..d6cbe88 100644 (file)
@@ -16,6 +16,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
+ *
+ * $Id$
  */
 
 #include <config.h>
 #include "errors.h"
 #include "memory.h"
 #include "util.h"
-#include "i18n.h"
 #include "cardglue.h"
 #else /* GNUPG_MAJOR_VERSION != 1 */
 #include "scdaemon.h"
 #endif /* GNUPG_MAJOR_VERSION != 1 */
 
+#include "i18n.h"
 #include "iso7816.h"
 #include "app-common.h"
 #include "tlv.h"
@@ -52,27 +54,33 @@ static struct {
   int binary;
   int dont_cache;
   int flush_on_error;
+  int get_immediate_in_v11; /* Enable a hack to bypass the cache of
+                               this data object if it is used in 1.1
+                               and later versions of the card.  This
+                               does not work with composite DO and is
+                               currently only useful for the CHV
+                               status bytes. */
   char *desc;
 } data_objects[] = {
-  { 0x005E, 0,    0, 1, 0, 0, "Login Data" },
-  { 0x5F50, 0,    0, 0, 0, 0, "URL" },
-  { 0x0065, 1,    0, 1, 0, 0, "Cardholder Related Data"},
-  { 0x005B, 0, 0x65, 0, 0, 0, "Name" },
-  { 0x5F2D, 0, 0x65, 0, 0, 0, "Language preferences" },
-  { 0x5F35, 0, 0x65, 0, 0, 0, "Sex" },
-  { 0x006E, 1,    0, 1, 0, 0, "Application Related Data" },
-  { 0x004F, 0, 0x6E, 1, 0, 0, "AID" },
-  { 0x0073, 1,    0, 1, 0, 0, "Discretionary Data Objects" },
-  { 0x0047, 0, 0x6E, 1, 0, 0, "Card Capabilities" },
-  { 0x00C0, 0, 0x6E, 1, 0, 0, "Extended Card Capabilities" },
-  { 0x00C1, 0, 0x6E, 1, 0, 0, "Algorithm Attributes Signature" },
-  { 0x00C2, 0, 0x6E, 1, 0, 0, "Algorithm Attributes Decryption" },
-  { 0x00C3, 0, 0x6E, 1, 0, 0, "Algorithm Attributes Authentication" },
-  { 0x00C4, 0, 0x6E, 1, 0, 1, "CHV Status Bytes" },
-  { 0x00C5, 0, 0x6E, 1, 0, 0, "Fingerprints" },
-  { 0x00C6, 0, 0x6E, 1, 0, 0, "CA Fingerprints" },
-  { 0x007A, 1,    0, 1, 0, 0, "Security Support Template" },
-  { 0x0093, 0, 0x7A, 1, 1, 0, "Digital Signature Counter" },
+  { 0x005E, 0,    0, 1, 0, 0, 0, "Login Data" },
+  { 0x5F50, 0,    0, 0, 0, 0, 0, "URL" },
+  { 0x0065, 1,    0, 1, 0, 0, 0, "Cardholder Related Data"},
+  { 0x005B, 0, 0x65, 0, 0, 0, 0, "Name" },
+  { 0x5F2D, 0, 0x65, 0, 0, 0, 0, "Language preferences" },
+  { 0x5F35, 0, 0x65, 0, 0, 0, 0, "Sex" },
+  { 0x006E, 1,    0, 1, 0, 0, 0, "Application Related Data" },
+  { 0x004F, 0, 0x6E, 1, 0, 0, 0, "AID" },
+  { 0x0073, 1,    0, 1, 0, 0, 0, "Discretionary Data Objects" },
+  { 0x0047, 0, 0x6E, 1, 1, 0, 0, "Card Capabilities" },
+  { 0x00C0, 0, 0x6E, 1, 1, 0, 0, "Extended Card Capabilities" },
+  { 0x00C1, 0, 0x6E, 1, 1, 0, 0, "Algorithm Attributes Signature" },
+  { 0x00C2, 0, 0x6E, 1, 1, 0, 0, "Algorithm Attributes Decryption" },
+  { 0x00C3, 0, 0x6E, 1, 1, 0, 0, "Algorithm Attributes Authentication" },
+  { 0x00C4, 0, 0x6E, 1, 0, 1, 1, "CHV Status Bytes" },
+  { 0x00C5, 0, 0x6E, 1, 0, 0, 0, "Fingerprints" },
+  { 0x00C6, 0, 0x6E, 1, 0, 0, 0, "CA Fingerprints" },
+  { 0x007A, 1,    0, 1, 0, 0, 0, "Security Support Template" },
+  { 0x0093, 0, 0x7A, 1, 1, 0, 0, "Digital Signature Counter" },
   { 0 }
 };
 
@@ -86,6 +94,18 @@ struct cache_s {
 
 struct app_local_s {
   struct cache_s *cache;
+  struct 
+  {
+    unsigned int get_challenge:1;
+    unsigned int key_import:1;
+    unsigned int change_force_chv:1;
+    unsigned int private_dos:1;
+  } extcap;
+  struct
+  {
+    unsigned int no_sync:1;   /* Do not sync CHV1 and CHV2 */
+    unsigned int def_chv2:1;  /* Use 123456 for CHV2.  */
+  } flags;
 };
 
 
@@ -127,25 +147,23 @@ get_cached_data (app_t app, int tag,
   *result = NULL;
   *resultlen = 0;
 
-  if (app->app_local)
-    {
-      for (c=app->app_local->cache; c; c = c->next)
-        if (c->tag == tag)
+  for (c=app->app_local->cache; c; c = c->next)
+    if (c->tag == tag)
+      {
+        if(c->length)
           {
-           if(c->length)
-             {
-               p = xtrymalloc (c->length);
-               if (!p)
-                 return gpg_error (gpg_err_code_from_errno (errno));
-               memcpy (p, c->data, c->length);
-               *result = p;
-             }
-
-           *resultlen = c->length;
-
-           return 0;
+            p = xtrymalloc (c->length);
+            if (!p)
+              return gpg_error (gpg_err_code_from_errno (errno));
+            memcpy (p, c->data, c->length);
+            *result = p;
           }
-    }
+
+        *resultlen = c->length;
+
+        return 0;
+      }
   
   err = iso7816_get_data (app->slot, tag, &p, &len);
   if (err)
@@ -163,24 +181,18 @@ get_cached_data (app_t app, int tag,
       }
 
   /* No, cache it. */
-  if (!app->app_local)
-    app->app_local = xtrycalloc (1, sizeof *app->app_local);
 
-  /* Note that we can safely ignore out of core errors. */
-  if (app->app_local)
+  for (c=app->app_local->cache; c; c = c->next)
+    assert (c->tag != tag);
+  
+  c = xtrymalloc (sizeof *c + len);
+  if (c)
     {
-      for (c=app->app_local->cache; c; c = c->next)
-        assert (c->tag != tag);
-      
-      c = xtrymalloc (sizeof *c + len);
-      if (c)
-        {
-          memcpy (c->data, p, len);
-          c->length = len;
-          c->tag = tag;
-          c->next = app->app_local->cache;
-          app->app_local->cache = c;
-        }
+      memcpy (c->data, p, len);
+      c->length = len;
+      c->tag = tag;
+      c->next = app->app_local->cache;
+      app->app_local->cache = c;
     }
 
   return 0;
@@ -206,7 +218,9 @@ flush_cache_item (app_t app, int tag)
         xfree (c);
 
         for (c=app->app_local->cache; c ; c = c->next)
-          assert (c->tag != tag); /* Oops: duplicated entry. */
+          {
+            assert (c->tag != tag); /* Oops: duplicated entry. */
+          }
         return;
       }
 
@@ -266,6 +280,15 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes)
   for (i=0; data_objects[i].tag && data_objects[i].tag != tag; i++)
     ;
 
+  if (app->card_version > 0x0100 && data_objects[i].get_immediate_in_v11)
+    {
+      if( iso7816_get_data (app->slot, tag, &buffer, &buflen))
+        return NULL;
+      *result = buffer;
+      *nbytes = buflen;
+      return buffer;
+    }
+
   value = NULL;
   rc = -1;
   if (data_objects[i].tag && data_objects[i].get_from)
@@ -393,6 +416,75 @@ count_bits (const unsigned char *a, size_t len)
   return n;
 }
 
+/* GnuPG makes special use of the login-data DO, this fucntion parses
+   the login data to store the flags for later use.  It may be called
+   at any time and should be called after changing the login-data DO.
+
+   Everything up to a LF is considered a mailbox or account name.  If
+   the first LF is follewed by DC4 (0x14) control sequence are
+   expected up to the next LF.  Control sequences are separated by FS
+   (0x28) and consist of key=value pairs.  There is one key defined:
+
+    F=<flags>
+
+    Were FLAGS is a plain hexadecimal number representing flag values.
+    The lsb is here the rightmost bit.  Defined flags bits are:
+
+      Bit 0 = CHV1 and CHV2 are not syncronized
+      Bit 1 = CHV2 has been been set to the default PIN of "123456"
+              (this implies that bit 0 is also set).
+
+*/
+static void
+parse_login_data (app_t app)
+{
+  unsigned char *buffer, *p;
+  size_t buflen, len;
+  void *relptr;
+
+  /* Set defaults.  */
+  app->app_local->flags.no_sync = 0;
+  app->app_local->flags.def_chv2 = 0;
+
+  /* Read the DO.  */
+  relptr = get_one_do (app, 0x005E, &buffer, &buflen);
+  if (!relptr)
+    return; /* Ooops. */
+  for (; buflen; buflen--, buffer++)
+    if (*buffer == '\n')
+      break;
+  if (buflen < 2 || buffer[1] != '\x14')
+    return; /* No control sequences.  */
+  buflen--;
+  buffer++;
+  do
+    {
+      buflen--;
+      buffer++;
+      if (buflen > 1 && *buffer == 'F' && buffer[1] == '=')
+        {
+          /* Flags control sequence found.  */
+          int lastdig = 0;
+
+          /* For now we are only interested in the last digit, so skip
+             any leading digits but bail out on invalid characters. */
+          for (p=buffer+2, len = buflen-2; len && hexdigitp (p); p++, len--)
+            lastdig = xtoi_1 (p);
+          if (len && !(*p == '\n' || *p == '\x18'))
+            goto next;  /* Invalid characters in field.  */
+          app->app_local->flags.no_sync = !!(lastdig & 1);
+          app->app_local->flags.def_chv2 = (lastdig & 3) == 3;
+        }
+    next:
+      for (; buflen && *buffer != '\x18'; buflen--, buffer++)
+        if (*buffer == '\n')
+          buflen = 1; 
+    }
+  while (buflen);
+
+  xfree (relptr);
+}
+
 /* Note, that FPR must be at least 20 bytes. */
 static int 
 store_fpr (int slot, int keynumber, u32 timestamp,
@@ -439,14 +531,29 @@ store_fpr (int slot, int keynumber, u32 timestamp,
   rc = iso7816_put_data (slot, (card_version > 0x0007? 0xC7 : 0xC6)
                                + keynumber, fpr, 20);
   if (rc)
-    log_error ("failed to store the fingerprint: %s\n",gpg_strerror (rc));
+    log_error (_("failed to store the fingerprint: %s\n"),gpg_strerror (rc));
+
+  if (!rc && card_version > 0x0100)
+    {
+      unsigned char buf[4];
+
+      buf[0] = timestamp >> 24;
+      buf[1] = timestamp >> 16;
+      buf[2] = timestamp >>  8;
+      buf[3] = timestamp;
+
+      rc = iso7816_put_data (slot, 0xCE + keynumber, buf, 4);
+      if (rc)
+        log_error (_("failed to store the creation date: %s\n"),
+                   gpg_strerror (rc));
+    }
 
   return rc;
 }
 
        
 static void
-send_fpr_if_not_null (CTRL ctrl, const char *keyword,
+send_fpr_if_not_null (ctrl_t ctrl, const char *keyword,
                       int number, const unsigned char *fpr)
 {                      
   int i;
@@ -469,7 +576,7 @@ send_fpr_if_not_null (CTRL ctrl, const char *keyword,
 }
 
 static void
-send_key_data (CTRL ctrl, const char *name, 
+send_key_data (ctrl_t ctrl, const char *name, 
                const unsigned char *a, size_t alen)
 {
   char *p, *buf = xmalloc (alen*2+1);
@@ -487,7 +594,7 @@ send_key_data (CTRL ctrl, const char *name,
 /* Implement the GETATTR command.  This is similar to the LEARN
    command but returns just one value via the status interface. */
 static int 
-do_getattr (APP app, CTRL ctrl, const char *name)
+do_getattr (app_t app, ctrl_t ctrl, const char *name)
 {
   static struct {
     const char *name;
@@ -505,6 +612,7 @@ do_getattr (APP app, CTRL ctrl, const char *name)
     { "SIG-COUNTER",  0x0093, 2 },
     { "SERIALNO",     0x004F, -1 },
     { "AID",          0x004F },
+    { "EXTCAP",       0x0000, -2 },
     { NULL, 0 }
   };
   int idx, i;
@@ -539,6 +647,18 @@ do_getattr (APP app, CTRL ctrl, const char *name)
         }
       return 0;
     }
+  if (table[idx].special == -2)
+    {
+      char tmp[50];
+
+      sprintf (tmp, "gc=%d ki=%d fc=%d pd=%d", 
+               app->app_local->extcap.get_challenge,
+               app->app_local->extcap.key_import,
+               app->app_local->extcap.change_force_chv,
+               app->app_local->extcap.private_dos);
+      send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
+      return 0;
+    }
 
   relptr = get_one_do (app, table[idx].tag, &value, &valuelen);
   if (relptr)
@@ -576,8 +696,9 @@ do_getattr (APP app, CTRL ctrl, const char *name)
 
 
 static int
-do_learn_status (APP app, CTRL ctrl)
+do_learn_status (app_t app, ctrl_t ctrl)
 {
+  do_getattr (app, ctrl, "EXTCAP");
   do_getattr (app, ctrl, "DISP-NAME");
   do_getattr (app, ctrl, "DISP-LANG");
   do_getattr (app, ctrl, "DISP-SEX");
@@ -608,13 +729,14 @@ verify_chv2 (app_t app,
       rc = pincb (pincb_arg, "PIN", &pinvalue); 
       if (rc)
         {
-          log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
+          log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc));
           return rc;
         }
 
       if (strlen (pinvalue) < 6)
         {
-          log_error ("prassphrase (CHV2) is too short; minimum length is 6\n");
+          log_error (_("PIN for CHV%d is too short;"
+                       " minimum length is %d\n"), 2, 6);
           xfree (pinvalue);
           return gpg_error (GPG_ERR_BAD_PIN);
         }
@@ -622,7 +744,7 @@ verify_chv2 (app_t app,
       rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue));
       if (rc)
         {
-          log_error ("verify CHV2 failed: %s\n", gpg_strerror (rc));
+          log_error (_("verify CHV%d failed: %s\n"), 2, gpg_strerror (rc));
           xfree (pinvalue);
           flush_cache_after_error (app);
           return rc;
@@ -636,7 +758,7 @@ verify_chv2 (app_t app,
             rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED);
           if (rc)
             {
-              log_error ("verify CHV1 failed: %s\n", gpg_strerror (rc));
+              log_error (_("verify CHV%d failed: %s\n"), 1, gpg_strerror (rc));
               xfree (pinvalue);
               flush_cache_after_error (app);
               return rc;
@@ -650,32 +772,60 @@ verify_chv2 (app_t app,
 
 /* Verify CHV3 if required. */
 static int
-verify_chv3 (APP app,
+verify_chv3 (app_t app,
              int (*pincb)(void*, const char *, char **),
              void *pincb_arg)
 {
   int rc = 0;
 
+#if GNUPG_MAJOR_VERSION != 1
   if (!opt.allow_admin)
     {
-      log_info ("access to admin commands is not configured\n");
+      log_info (_("access to admin commands is not configured\n"));
       return gpg_error (GPG_ERR_EACCES);
     }
+#endif
       
   if (!app->did_chv3) 
     {
       char *pinvalue;
+      void *relptr;
+      unsigned char *value;
+      size_t valuelen;
+      int reread_chv_status;
+      
+
+      relptr = get_one_do (app, 0x00C4, &value, &valuelen);
+      if (!relptr || valuelen < 7)
+        {
+          log_error (_("error retrieving CHV status from card\n"));
+          xfree (relptr);
+          return gpg_error (GPG_ERR_CARD);
+        }
+      if (value[6] == 0)
+        {
+          log_info (_("card is permanently locked!\n"));
+          xfree (relptr);
+          return gpg_error (GPG_ERR_BAD_PIN);
+        }
+
+      reread_chv_status = (value[6] < 3);
+
+      log_info(_("%d Admin PIN attempts remaining before card"
+                 " is permanently locked\n"), value[6]);
+      xfree (relptr);
 
-      rc = pincb (pincb_arg, "Admin PIN", &pinvalue); 
+      rc = pincb (pincb_arg, _("Admin PIN"), &pinvalue); 
       if (rc)
         {
-          log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
+          log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc));
           return rc;
         }
 
       if (strlen (pinvalue) < 6)
         {
-          log_error ("prassphrase (CHV3) is too short; minimum length is 6\n");
+          log_error (_("prassphrase (CHV%d) is too short;"
+                       " minimum length is %d\n"), 3, 6);
           xfree (pinvalue);
           return gpg_error (GPG_ERR_BAD_PIN);
         }
@@ -684,11 +834,18 @@ verify_chv3 (APP app,
       xfree (pinvalue);
       if (rc)
         {
-          log_error ("verify CHV3 failed: %s\n", gpg_strerror (rc));
+          log_error (_("verify CHV%d failed: %s\n"), 3, gpg_strerror (rc));
           flush_cache_after_error (app);
           return rc;
         }
       app->did_chv3 = 1;
+      /* If the PIN has been entered wrongly before, we need to flush
+         the cached value so that the next read correctly reflects the
+         resetted retry counter.  Note that version 1.1 of the specs
+         allow direct reading of that DO, so that we could actually
+         flush it in all cases. */
+      if (reread_chv_status)
+        flush_cache_item (app, 0x00C4);
     }
   return rc;
 }
@@ -697,7 +854,7 @@ verify_chv3 (APP app,
 /* Handle the SETATTR operation. All arguments are already basically
    checked. */
 static int 
-do_setattr (APP app, const char *name,
+do_setattr (app_t app, const char *name,
             int (*pincb)(void*, const char *, char **),
             void *pincb_arg,
             const unsigned char *value, size_t valuelen)
@@ -710,7 +867,7 @@ do_setattr (APP app, const char *name,
     int special;
   } table[] = {
     { "DISP-NAME",    0x005B },
-    { "LOGIN-DATA",   0x005E },
+    { "LOGIN-DATA",   0x005E, 2 },
     { "DISP-LANG",    0x5F2D },
     { "DISP-SEX",     0x5F35 },
     { "PUBKEY-URL",   0x5F50 },
@@ -741,6 +898,8 @@ do_setattr (APP app, const char *name,
 
   if (table[idx].special == 1)
     app->force_chv1 = (valuelen && *value == 0);
+  else if (table[idx].special == 2)
+    parse_login_data (app);
 
   return rc;
 }
@@ -748,7 +907,7 @@ do_setattr (APP app, const char *name,
 
 /* Handle the PASSWD command. */
 static int 
-do_change_pin (APP app, CTRL ctrl,  const char *chvnostr, int reset_mode,
+do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr, int reset_mode,
                int (*pincb)(void*, const char *, char **),
                void *pincb_arg)
 {
@@ -835,7 +994,7 @@ do_change_pin (APP app, CTRL ctrl,  const char *chvnostr, int reset_mode,
 
 /* Handle the GENKEY command. */
 static int 
-do_genkey (APP app, CTRL ctrl,  const char *keynostr, unsigned int flags,
+do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
           int (*pincb)(void*, const char *, char **),
           void *pincb_arg)
 {
@@ -977,7 +1136,7 @@ convert_sig_counter_value (const unsigned char *value, size_t valuelen)
 }
 
 static unsigned long
-get_sig_counter (APP app)
+get_sig_counter (app_t app)
 {
   void *relptr;
   unsigned char *value;
@@ -993,7 +1152,7 @@ get_sig_counter (APP app)
 }
 
 static int
-compare_fingerprint (APP app, int keyno, unsigned char *sha1fpr)
+compare_fingerprint (app_t app, int keyno, unsigned char *sha1fpr)
 {
   const unsigned char *fpr;
   unsigned char *buffer;
@@ -1033,7 +1192,7 @@ compare_fingerprint (APP app, int keyno, unsigned char *sha1fpr)
      known to gpg was not updated.  If there is no fingerprint we
      assume that this is okay. */
 static int
-check_against_given_fingerprint (APP app, const char *fpr, int keyno)
+check_against_given_fingerprint (app_t app, const char *fpr, int keyno)
 {
   unsigned char tmp[20];
   const char *s;
@@ -1064,7 +1223,7 @@ check_against_given_fingerprint (APP app, const char *fpr, int keyno)
    not match the one required for the requested action (e.g. the
    serial number does not match). */
 static int 
-do_sign (APP app, const char *keyidstr, int hashalgo,
+do_sign (app_t app, const char *keyidstr, int hashalgo,
          int (*pincb)(void*, const char *, char **),
          void *pincb_arg,
          const void *indata, size_t indatalen,
@@ -1139,20 +1298,21 @@ do_sign (APP app, const char *keyidstr, int hashalgo,
 
       {
         char *prompt;
-        if (asprintf (&prompt, "PIN [sigs done: %lu]", sigcount) < 0)
+        if (asprintf (&prompt, _("PIN [sigs done: %lu]"), sigcount) < 0)
           return gpg_error_from_errno (errno);
         rc = pincb (pincb_arg, prompt, &pinvalue); 
         free (prompt);
       }
       if (rc)
         {
-          log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
+          log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc));
           return rc;
         }
 
       if (strlen (pinvalue) < 6)
         {
-          log_error ("prassphrase (CHV1) is too short; minimum length is 6\n");
+          log_error (_("PIN for CHV%d is too short;"
+                       " minimum length is %d\n"), 1, 6);
           xfree (pinvalue);
           return gpg_error (GPG_ERR_BAD_PIN);
         }
@@ -1160,7 +1320,7 @@ do_sign (APP app, const char *keyidstr, int hashalgo,
       rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue));
       if (rc)
         {
-          log_error ("verify CHV1 failed\n");
+          log_error (_("verify CHV%d failed: %s\n"), 1, gpg_strerror (rc));
           xfree (pinvalue);
           flush_cache_after_error (app);
           return rc;
@@ -1174,7 +1334,7 @@ do_sign (APP app, const char *keyidstr, int hashalgo,
             rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED);
           if (rc)
             {
-              log_error ("verify CHV2 failed\n");
+              log_error (_("verify CHV%d failed: %s\n"), 2, gpg_strerror (rc));
               xfree (pinvalue);
               flush_cache_after_error (app);
               return rc;
@@ -1198,7 +1358,7 @@ do_sign (APP app, const char *keyidstr, int hashalgo,
    not match the one required for the requested action (e.g. the
    serial number does not match). */
 static int 
-do_auth (APP app, const char *keyidstr,
+do_auth (app_t app, const char *keyidstr,
          int (*pincb)(void*, const char *, char **),
          void *pincb_arg,
          const void *indata, size_t indatalen,
@@ -1257,7 +1417,7 @@ do_auth (APP app, const char *keyidstr,
 
 
 static int 
-do_decipher (APP app, const char *keyidstr,
+do_decipher (app_t app, const char *keyidstr,
              int (pincb)(void*, const char *, char **),
              void *pincb_arg,
              const void *indata, size_t indatalen,
@@ -1319,7 +1479,7 @@ do_decipher (APP app, const char *keyidstr,
    dangerous CHV3.  KEYIDSTR is the usual card's serial number; an
    optional fingerprint part will be ignored. */
 static int 
-do_check_pin (APP app, const char *keyidstr,
+do_check_pin (app_t app, const char *keyidstr,
               int (pincb)(void*, const char *, char **),
               void *pincb_arg)
 {
@@ -1366,7 +1526,7 @@ do_check_pin (APP app, const char *keyidstr,
 /* Select the OpenPGP application on the card in SLOT.  This function
    must be used before any other OpenPGP application functions. */
 int
-app_select_openpgp (APP app)
+app_select_openpgp (app_t app)
 {
   static char const aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 };
   int slot = app->slot;
@@ -1378,11 +1538,14 @@ app_select_openpgp (APP app)
   rc = iso7816_select_application (slot, aid, sizeof aid);
   if (!rc)
     {
+      unsigned int manufacturer;
+
       app->apptype = "OPENPGP";
 
       app->did_chv1 = 0;
       app->did_chv2 = 0;
       app->did_chv3 = 0;
+      app->app_local = NULL;
 
       /* The OpenPGP card returns the serial number as part of the
          AID; because we prefer to use OpenPGP serial numbers, we
@@ -1394,26 +1557,58 @@ app_select_openpgp (APP app)
         goto leave;
       if (opt.verbose)
         {
-          log_info ("got AID: ");
+          log_info ("AID: ");
           log_printhex ("", buffer, buflen);
         }
 
       app->card_version = buffer[6] << 8;
       app->card_version |= buffer[7];
+      manufacturer = (buffer[8]<<8 | buffer[9]);
+
       xfree (app->serialno);
       app->serialno = buffer;
       app->serialnolen = buflen;
       buffer = NULL;
+      app->app_local = xtrycalloc (1, sizeof *app->app_local);
+      if (!app->app_local)
+        {
+          rc = gpg_error (gpg_err_code_from_errno (errno));
+          goto leave;
+        }
 
       relptr = get_one_do (app, 0x00C4, &buffer, &buflen);
       if (!relptr)
         {
-          log_error ("can't access CHV Status Bytes - invalid OpenPGP card?\n");
+          log_error (_("can't access %s - invalid OpenPGP card?\n"),
+                     "CHV Status Bytes");
           goto leave;
         }
       app->force_chv1 = (buflen && *buffer == 0);
       xfree (relptr);
-        
+
+      relptr = get_one_do (app, 0x00C0, &buffer, &buflen);
+      if (!relptr)
+        {
+          log_error (_("can't access %s - invalid OpenPGP card?\n"),
+                     "Extended Capability Flags" );
+          goto leave;
+        }
+      if (buflen)
+        {
+          app->app_local->extcap.get_challenge    = !!(*buffer & 0x40);
+          app->app_local->extcap.key_import       = !!(*buffer & 0x20);
+          app->app_local->extcap.change_force_chv = !!(*buffer & 0x10);
+          app->app_local->extcap.private_dos      = !!(*buffer & 0x08);
+        }
+      xfree (relptr);
+      
+      /* Some of the first cards accidently don't set the
+         CHANGE_FORCE_CHV bit but allow it anyway. */
+      if (app->card_version <= 0x0100 && manufacturer == 1)
+        app->app_local->extcap.change_force_chv = 1;
+
+      parse_login_data (app);
+
       if (opt.verbose > 1)
         dump_all_do (slot);
 
@@ -1431,6 +1626,8 @@ app_select_openpgp (APP app)
    }
 
 leave:
+  if (rc)
+    do_deinit (app);
   return rc;
 }
 
@@ -1442,7 +1639,7 @@ leave:
    buffers or NULL if the data object is not available.  All returned
    values are sanitized. */
 int
-app_openpgp_cardinfo (APP app,
+app_openpgp_cardinfo (app_t app,
                       char **serialno,
                       char **disp_name,
                       char **pubkey_url,
@@ -1463,7 +1660,8 @@ app_openpgp_cardinfo (APP app,
       rc = app_get_serial_and_stamp (app, serialno, &dummy);
       if (rc)
         {
-          log_error ("error getting serial number: %s\n", gpg_strerror (rc));
+          log_error (_("error getting serial number: %s\n"),
+                     gpg_strerror (rc));
           return rc;
         }
     }
@@ -1523,7 +1721,7 @@ app_openpgp_cardinfo (APP app,
 
 
 /* This function is currently only used by the sc-copykeys program to
-   store a key on the smartcard.  APP ist the application handle,
+   store a key on the smartcard.  app_t ist the application handle,
    KEYNO is the number of the key and PINCB, PINCB_ARG are used to ask
    for the SO PIN.  TEMPLATE and TEMPLATE_LEN describe a buffer with
    the key template to store. CREATED_AT is the timestamp used to
@@ -1531,7 +1729,7 @@ app_openpgp_cardinfo (APP app,
    RSA public exponent. This function silently overwrites an existing
    key.*/
 int 
-app_openpgp_storekey (APP app, int keyno,
+app_openpgp_storekey (app_t app, int keyno,
                       unsigned char *template, size_t template_len,
                       time_t created_at,
                       const unsigned char *m, size_t mlen,
@@ -1557,7 +1755,7 @@ app_openpgp_storekey (APP app, int keyno,
                          template, template_len);
   if (rc)
     {
-      log_error ("failed to store the key: rc=%s\n", gpg_strerror (rc));
+      log_error (_("failed to store the key: %s\n"), gpg_strerror (rc));
       rc = gpg_error (GPG_ERR_CARD);
       goto leave;
     }
@@ -1576,7 +1774,7 @@ app_openpgp_storekey (APP app, int keyno,
 /* Utility function for external tools: Read the public RSA key at
    KEYNO and return modulus and exponent in (M,MLEN) and (E,ELEN). */
 int 
-app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
+app_openpgp_readkey (app_t app, int keyno, unsigned char **m, size_t *mlen,
                      unsigned char **e, size_t *elen)
 {
   int rc;
@@ -1599,14 +1797,14 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
   if (rc)
     {
       rc = gpg_error (GPG_ERR_CARD);
-      log_error ("reading key failed\n");
+      log_error (_("reading the key failed\n"));
       goto leave;
     }
 
   keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
   if (!keydata)
     {
-      log_error ("response does not contain the public key data\n");
+      log_error (_("response does not contain the public key data\n"));
       rc = gpg_error (GPG_ERR_CARD);
       goto leave;
     }
@@ -1614,7 +1812,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
   a = find_tlv (keydata, keydatalen, 0x0081, &alen);
   if (!a)
     {
-      log_error ("response does not contain the RSA modulus\n");
+      log_error (_("response does not contain the RSA modulus\n"));
       rc = gpg_error (GPG_ERR_CARD);
       goto leave;
     }
@@ -1625,7 +1823,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
   a = find_tlv (keydata, keydatalen, 0x0082, &alen);
   if (!a)
     {
-      log_error ("response does not contain the RSA public exponent\n");
+      log_error (_("response does not contain the RSA public exponent\n"));
       rc = gpg_error (GPG_ERR_CARD);
       goto leave;
     }