gpg: Do not bail on an invalid packet in the local keyring.
[gnupg.git] / scd / app.c
index 219cee6..4fe60cb 100644 (file)
--- a/scd/app.c
+++ b/scd/app.c
 
 static npth_mutex_t app_list_lock;
 static app_t app_top;
+
+
+/* The list of application names and there select function.  Of no
+ * specfic application is selected the first available application on
+ * a card is selected.  */
+struct app_priority_list_s
+{
+  char const *name;
+  gpg_error_t (*select_func)(app_t);
+};
+
+static struct app_priority_list_s app_priority_list[] =
+  {{ "openpgp",   app_select_openpgp   },
+   { "piv",       app_select_piv       },
+   { "nks",       app_select_nks       },
+   { "p15",       app_select_p15       },
+   { "geldkarte", app_select_geldkarte },
+   { "dinsig",    app_select_dinsig    },
+   { "sc-hsm",    app_select_sc_hsm    },
+   { NULL,        NULL                 }
+  };
+
+
+
+
 \f
+/* Initialization function to change the default app_priority_list.
+ * LIST is a list of comma or space separated strings with application
+ * names.  Unknown names will only result in warning message.
+ * Application not mentioned in LIST are used in their original order
+ * after the given once.  */
+void
+app_update_priority_list (const char *arg)
+{
+  struct app_priority_list_s save;
+  char **names;
+  int i, j, idx;
+
+  names = strtokenize (arg, ", ");
+  if (!names)
+    log_fatal ("strtokenize failed: %s\n",
+               gpg_strerror (gpg_error_from_syserror ()));
+
+  idx = 0;
+  for (i=0; names[i]; i++)
+    {
+      ascii_strlwr (names[i]);
+      for (j=0; j < i; j++)
+        if (!strcmp (names[j], names[i]))
+          break;
+      if (j < i)
+        {
+          log_info ("warning: duplicate application '%s' in priority list\n",
+                    names[i]);
+          continue;
+        }
+
+      for (j=idx; app_priority_list[j].name; j++)
+        if (!strcmp (names[i], app_priority_list[j].name))
+          break;
+      if (!app_priority_list[j].name)
+        {
+          log_info ("warning: unknown application '%s' in priority list\n",
+                    names[i]);
+          continue;
+        }
+      save = app_priority_list[idx];
+      app_priority_list[idx] = app_priority_list[j];
+      app_priority_list[j] = save;
+      idx++;
+    }
+  log_assert (idx < DIM (app_priority_list));
+
+  xfree (names);
+  for (i=0; app_priority_list[i].name; i++)
+    log_info ("app priority %d: %s\n", i, app_priority_list[i].name);
+}
+
+
 static void
 print_progress_line (void *opaque, const char *what, int pc, int cur, int tot)
 {
@@ -121,6 +199,9 @@ check_conflict (app_t app, const char *name)
   if (!app || !name || (app->apptype && !ascii_strcasecmp (app->apptype, name)))
     return 0;
 
+  if (app->apptype && !strcmp (app->apptype, "UNDEFINED"))
+    return 0;
+
   log_info ("application '%s' in use - can't switch\n",
             app->apptype? app->apptype : "<null>");
 
@@ -176,6 +257,7 @@ app_new_register (int slot, ctrl_t ctrl, const char *name,
   unsigned char *result = NULL;
   size_t resultlen;
   int want_undefined;
+  int i;
 
   /* Need to allocate a new one.  */
   app = xtrycalloc (1, sizeof *app);
@@ -218,9 +300,12 @@ app_new_register (int slot, ctrl_t ctrl, const char *name,
            * config.  */
           static char const yk_aid[] =
             { 0xA0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17 }; /*MGR*/
+          static char const otp_aid[] =
+            { 0xA0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01 }; /*OTP*/
           unsigned char *buf;
           size_t buflen;
-          const unsigned char *s0, *s1;
+          const unsigned char *s0;
+          unsigned char formfactor;
           size_t n;
 
           if (!iso7816_select_application (slot, yk_aid, sizeof yk_aid,
@@ -228,6 +313,7 @@ app_new_register (int slot, ctrl_t ctrl, const char *name,
               && !iso7816_apdu_direct (slot, "\x00\x1d\x00\x00\x00", 5, 0,
                                        NULL, &buf, &buflen))
             {
+              app->cardtype = "yubikey";
               if (opt.verbose)
                 {
                   log_info ("Yubico: config=");
@@ -239,27 +325,44 @@ app_new_register (int slot, ctrl_t ctrl, const char *name,
               if (buflen > 1)
                 {
                   s0 = find_tlv (buf+1, buflen-1, 0x04, &n);  /* Form factor */
-                  if (s0 && n == 1)
+                  formfactor = (s0 && n == 1)? *s0 : 0;
+
+                  s0 = find_tlv (buf+1, buflen-1, 0x02, &n);  /* Serial */
+                  if (s0 && n >= 4)
                     {
-                      s1 = find_tlv (buf+1, buflen-1, 0x02, &n);  /* Serial */
-                      if (s1 && n >= 4)
+                      app->serialno = xtrymalloc (3 + 1 + n);
+                      if (app->serialno)
                         {
-                          app->serialno = xtrymalloc (3 + 1 + n);
-                          if (app->serialno)
-                            {
-                              app->serialnolen = 3 + 1 + n;
-                              app->serialno[0] = 0xff;
-                              app->serialno[1] = 0x02;
-                              app->serialno[2] = 0x0;
-                              app->serialno[3] = *s0;
-                              memcpy (app->serialno + 4, s1, n);
-                              /* Note that we do not clear the error
-                               * so that no further serial number
-                               * testing is done.  After all we just
-                               * set the serial number.  */
-                            }
+                          app->serialnolen = 3 + 1 + n;
+                          app->serialno[0] = 0xff;
+                          app->serialno[1] = 0x02;
+                          app->serialno[2] = 0x0;
+                          app->serialno[3] = formfactor;
+                          memcpy (app->serialno + 4, s0, n);
+                          /* Note that we do not clear the error
+                           * so that no further serial number
+                           * testing is done.  After all we just
+                           * set the serial number.  */
                         }
                     }
+
+                  s0 = find_tlv (buf+1, buflen-1, 0x05, &n);  /* version */
+                  if (s0 && n == 3)
+                    app->cardversion = ((s0[0]<<16)|(s0[1]<<8)|s0[2]);
+                  else if (!s0)
+                    {
+                      /* No version - this is not a Yubikey 5.  We now
+                       * switch to the OTP app and take the first
+                       * three bytes of the reponse as version
+                       * number.  */
+                      xfree (buf);
+                      buf = NULL;
+                      if (!iso7816_select_application_ext (slot,
+                                                       otp_aid, sizeof otp_aid,
+                                                       1, &buf, &buflen)
+                          && buflen > 3)
+                        app->cardversion = ((buf[0]<<16)|(buf[1]<<8)|buf[2]);
+                    }
                 }
               xfree (buf);
             }
@@ -323,26 +426,18 @@ app_new_register (int slot, ctrl_t ctrl, const char *name,
         goto leave;
 
       /* Set a default error so that we run through the application
-       * selecion chain.  */
+       * selection chain.  */
       err = gpg_error (GPG_ERR_NOT_FOUND);
     }
 
-  if (err && is_app_allowed ("openpgp")
-          && (!name || !strcmp (name, "openpgp")))
-    err = app_select_openpgp (app);
-  if (err && is_app_allowed ("piv") && (!name || !strcmp (name, "piv")))
-    err = app_select_piv (app);
-  if (err && is_app_allowed ("nks") && (!name || !strcmp (name, "nks")))
-    err = app_select_nks (app);
-  if (err && is_app_allowed ("p15") && (!name || !strcmp (name, "p15")))
-    err = app_select_p15 (app);
-  if (err && is_app_allowed ("geldkarte")
-      && (!name || !strcmp (name, "geldkarte")))
-    err = app_select_geldkarte (app);
-  if (err && is_app_allowed ("dinsig") && (!name || !strcmp (name, "dinsig")))
-    err = app_select_dinsig (app);
-  if (err && is_app_allowed ("sc-hsm") && (!name || !strcmp (name, "sc-hsm")))
-    err = app_select_sc_hsm (app);
+  /* Find the first available app if NAME is NULL or the one matching
+   * NAME but only if that application is also enabled.  */
+  for (i=0; err && app_priority_list[i].name; i++)
+    {
+      if (is_app_allowed (app_priority_list[i].name)
+          && (!name || !strcmp (name, app_priority_list[i].name)))
+        err = app_priority_list[i].select_func (app);
+    }
   if (err && name && gpg_err_code (err) != GPG_ERR_OBJ_TERM_STATE)
     err = gpg_error (GPG_ERR_NOT_SUPPORTED);
 
@@ -469,32 +564,21 @@ select_application (ctrl_t ctrl, const char *name, app_t *r_app,
 char *
 get_supported_applications (void)
 {
-  const char *list[] = {
-    "openpgp",
-    "piv",
-    "nks",
-    "p15",
-    "geldkarte",
-    "dinsig",
-    "sc-hsm",
-    /* Note: "undefined" is not listed here because it needs special
-       treatment by the client.  */
-    NULL
-  };
   int idx;
   size_t nbytes;
   char *buffer, *p;
+  const char *s;
 
-  for (nbytes=1, idx=0; list[idx]; idx++)
-    nbytes += strlen (list[idx]) + 1 + 1;
+  for (nbytes=1, idx=0; (s=app_priority_list[idx].name); idx++)
+    nbytes += strlen (s) + 1 + 1;
 
   buffer = xtrymalloc (nbytes);
   if (!buffer)
     return NULL;
 
-  for (p=buffer, idx=0; list[idx]; idx++)
-    if (is_app_allowed (list[idx]))
-      p = stpcpy (stpcpy (p, list[idx]), ":\n");
+  for (p=buffer, idx=0; (s=app_priority_list[idx].name); idx++)
+    if (is_app_allowed (s))
+      p = stpcpy (stpcpy (p, s), ":\n");
   *p = 0;
 
   return buffer;
@@ -628,7 +712,7 @@ app_get_serialno (app_t app)
 }
 
 
-/* Write out the application specifig status lines for the LEARN
+/* Write out the application specific status lines for the LEARN
    command. */
 gpg_error_t
 app_write_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
@@ -640,9 +724,19 @@ app_write_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
   if (!app->fnc.learn_status)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
 
-  /* We do not send APPTYPE if only keypairinfo is requested.  */
-  if (app->apptype && !(flags & 1))
-    send_status_direct (ctrl, "APPTYPE", app->apptype);
+  /* We do not send CARD and APPTYPE if only keypairinfo is requested.  */
+  if (!(flags &1))
+    {
+      if (app->cardtype)
+        send_status_direct (ctrl, "CARDTYPE", app->cardtype);
+      if (app->cardversion)
+        send_status_printf (ctrl, "CARDVERSION", "%X", app->cardversion);
+      if (app->apptype)
+        send_status_direct (ctrl, "APPTYPE", app->apptype);
+      if (app->appversion)
+        send_status_printf (ctrl, "APPVERSION", "%X", app->appversion);
+    }
+
   err = lock_app (app, ctrl);
   if (err)
     return err;
@@ -678,14 +772,15 @@ app_readcert (app_t app, ctrl_t ctrl, const char *certid,
 
 
 /* Read the key with ID KEYID.  On success a canonical encoded
-   S-expression with the public key will get stored at PK and its
-   length (for assertions) at PKLEN; the caller must release that
-   buffer. On error NULL will be stored at PK and PKLEN and an error
-   code returned.
-
-   This function might not be supported by all applications.  */
+ * S-expression with the public key will get stored at PK and its
+ * length (for assertions) at PKLEN; the caller must release that
+ * buffer. On error NULL will be stored at PK and PKLEN and an error
+ * code returned.  If the key is not required NULL may be passed for
+ * PK; this makse send if the APP_READKEY_FLAG_INFO has also been set.
+ *
+ * This function might not be supported by all applications.  */
 gpg_error_t
-app_readkey (app_t app, ctrl_t ctrl, int advanced, const char *keyid,
+app_readkey (app_t app, ctrl_t ctrl, const char *keyid, unsigned int flags,
              unsigned char **pk, size_t *pklen)
 {
   gpg_error_t err;
@@ -695,7 +790,7 @@ app_readkey (app_t app, ctrl_t ctrl, int advanced, const char *keyid,
   if (pklen)
     *pklen = 0;
 
-  if (!app || !keyid || !pk || !pklen)
+  if (!app || !keyid)
     return gpg_error (GPG_ERR_INV_VALUE);
   if (!app->ref_count)
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
@@ -704,7 +799,7 @@ app_readkey (app_t app, ctrl_t ctrl, int advanced, const char *keyid,
   err = lock_app (app, ctrl);
   if (err)
     return err;
-  err= app->fnc.readkey (app, advanced, keyid, pk, pklen);
+  err= app->fnc.readkey (app, ctrl, keyid, flags, pk, pklen);
   unlock_app (app);
   return err;
 }
@@ -721,6 +816,11 @@ app_getattr (app_t app, ctrl_t ctrl, const char *name)
   if (!app->ref_count)
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
 
+  if (app->cardtype && name && !strcmp (name, "CARDTYPE"))
+    {
+      send_status_direct (ctrl, "CARDTYPE", app->cardtype);
+      return 0;
+    }
   if (app->apptype && name && !strcmp (name, "APPTYPE"))
     {
       send_status_direct (ctrl, "APPTYPE", app->apptype);
@@ -744,7 +844,7 @@ app_getattr (app_t app, ctrl_t ctrl, const char *name)
   err = lock_app (app, ctrl);
   if (err)
     return err;
-  err =  app->fnc.getattr (app, ctrl, name);
+  err = app->fnc.getattr (app, ctrl, name);
   unlock_app (app);
   return err;
 }
@@ -930,8 +1030,8 @@ app_writekey (app_t app, ctrl_t ctrl,
 
 /* Perform a SETATTR operation.  */
 gpg_error_t
-app_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
-            time_t createtime,
+app_genkey (app_t app, ctrl_t ctrl, const char *keynostr,
+            const char *keytype, unsigned int flags, time_t createtime,
             gpg_error_t (*pincb)(void*, const char *, char **),
             void *pincb_arg)
 {
@@ -946,7 +1046,7 @@ app_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
   err = lock_app (app, ctrl);
   if (err)
     return err;
-  err = app->fnc.genkey (app, ctrl, keynostr, flags,
+  err = app->fnc.genkey (app, ctrl, keynostr, keytype, flags,
                          createtime, pincb, pincb_arg);
   unlock_app (app);
   if (opt.verbose)
@@ -979,7 +1079,8 @@ app_get_challenge (app_t app, ctrl_t ctrl, size_t nbytes, unsigned char *buffer)
 
 /* Perform a CHANGE REFERENCE DATA or RESET RETRY COUNTER operation.  */
 gpg_error_t
-app_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode,
+app_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
+                unsigned int flags,
                 gpg_error_t (*pincb)(void*, const char *, char **),
                 void *pincb_arg)
 {
@@ -994,8 +1095,7 @@ app_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode,
   err = lock_app (app, ctrl);
   if (err)
     return err;
-  err = app->fnc.change_pin (app, ctrl, chvnostr, reset_mode,
-                             pincb, pincb_arg);
+  err = app->fnc.change_pin (app, ctrl, chvnostr, flags, pincb, pincb_arg);
   unlock_app (app);
   if (opt.verbose)
     log_info ("operation change_pin result: %s\n", gpg_strerror (err));
@@ -1190,3 +1290,24 @@ app_send_card_list (ctrl_t ctrl)
     }
   npth_mutex_unlock (&app_list_lock);
 }
+
+/* Execute an action for each app.  ACTION can be one of:
+     KEYGRIP_ACTION_SEND_DATA: send data if KEYGRIP_STR matches
+     KEYGRIP_ACTION_WRITE_STATUS: write status if KEYGRIP_STR matches
+     KEYGRIP_ACTION_LOOKUP: Return matching APP
+ */
+app_t
+app_do_with_keygrip (ctrl_t ctrl, int action, const char *keygrip_str)
+{
+  app_t a;
+
+  npth_mutex_lock (&app_list_lock);
+
+  for (a = app_top; a; a = a->next)
+    if (a->fnc.with_keygrip
+        && !a->fnc.with_keygrip (a, ctrl, action, keygrip_str))
+      break;
+
+  npth_mutex_unlock (&app_list_lock);
+  return a;
+}