summaryrefslogtreecommitdiffstats
path: root/scd
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2019-09-05 14:05:05 +0200
committerWerner Koch <wk@gnupg.org>2019-09-05 14:07:27 +0200
commit7febb4f2476742936b829424ad23df662b37f4b4 (patch)
treeffde77421e86db994114ad4fe160369781a24f9e /scd
parentscd:openpgp: Avoid PIN caching issues after re-select. (diff)
downloadgnupg2-7febb4f2476742936b829424ad23df662b37f4b4.tar.xz
gnupg2-7febb4f2476742936b829424ad23df662b37f4b4.zip
scd: Implement auto-switching between Yubikey apps.
* scd/app.c (apptype_from_keyref): New. (maybe_switch_app): Add arg 'keyref' and use this also for switching. Change all callers to pass a keyref if needed. -- A drawback of this auto-switching is that the PIN cache of the cards are cleared. That could be mitigated by having our own cache but we always tried to avoid that. Signed-off-by: Werner Koch <wk@gnupg.org>
Diffstat (limited to 'scd')
-rw-r--r--scd/app.c139
1 files changed, 105 insertions, 34 deletions
diff --git a/scd/app.c b/scd/app.c
index 76ae43053..8acd15ff3 100644
--- a/scd/app.c
+++ b/scd/app.c
@@ -121,6 +121,31 @@ apptype_from_name (const char *name)
}
+/* Return the apptype for KEYREF. This is the first part of the
+ * KEYREF up to the dot. */
+static apptype_t
+apptype_from_keyref (const char *keyref)
+{
+ int i;
+ unsigned int n;
+ const char *s;
+
+ if (!keyref)
+ return APPTYPE_NONE;
+ s = strchr (keyref, '.');
+ if (!s || s == keyref || !s[1])
+ return APPTYPE_NONE; /* Not a valid keyref. */
+ n = s - keyref;
+
+ for (i=0; app_priority_list[i].apptype; i++)
+ if (strlen (app_priority_list[i].name) == n
+ && !ascii_strncasecmp (app_priority_list[i].name, keyref, n))
+ return app_priority_list[i].apptype;
+
+ return APPTYPE_NONE;
+}
+
+
/* 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.
@@ -243,6 +268,7 @@ app_dump_state (void)
{
log_info ("app_dump_state: card=%p slot=%d type=%s\n",
c, c->slot, strcardtype (c->cardtype));
+ /* FIXME The use of log_info risks a race! */
for (a=c->app; a; a = a->next)
log_info ("app_dump_state: app=%p type='%s'\n",
a, strapptype (a->apptype));
@@ -1070,10 +1096,12 @@ app_get_serialno (app_t app)
* that the new active app will be moved to the head of the list at
* CARD->app. Thus function must be called with the card lock held. */
static gpg_error_t
-maybe_switch_app (ctrl_t ctrl, card_t card)
+maybe_switch_app (ctrl_t ctrl, card_t card, const char *keyref)
{
gpg_error_t err;
- app_t app, app_prev;
+ app_t app;
+ app_t app_prev = NULL;
+ apptype_t apptype;
if (!card->ref_count || !card->app)
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
@@ -1084,39 +1112,82 @@ maybe_switch_app (ctrl_t ctrl, card_t card)
ctrl->current_apptype = card->app->apptype;
return 0;
}
- log_debug ("card=%p want=%s card->app=%s\n",
- card, strapptype (ctrl->current_apptype),
- strapptype (card->app->apptype));
- app_dump_state ();
- if (ctrl->current_apptype == card->app->apptype)
- return 0; /* No need to switch. */
- app_prev = card->app;
- for (app = app_prev->next; app; app_prev = app, app = app->next)
- if (app->apptype == ctrl->current_apptype)
- break;
+ if (DBG_APP)
+ log_debug ("slot %d: have=%s want=%s keyref=%s\n",
+ card->slot, strapptype (card->app->apptype),
+ strapptype (ctrl->current_apptype),
+ keyref? keyref:"[none]");
+
+ app = NULL;
+ if (keyref)
+ {
+ /* Switch based on the requested KEYREF. */
+ apptype = apptype_from_keyref (keyref);
+ if (apptype)
+ {
+ for (app = card->app; app; app_prev = app, app = app->next)
+ if (app->apptype == apptype)
+ break;
+ if (!app_prev && ctrl->current_apptype == card->app->apptype)
+ return 0; /* Already the first app - no need to switch. */
+ }
+ else if (strlen (keyref) == 40)
+ {
+ /* This looks like a keygrip. Iterate over all apps to find
+ * the corresponding app. */
+ for (app = card->app; app; app_prev = app, app = app->next)
+ if (app->fnc.with_keygrip
+ && !app->fnc.with_keygrip (app, ctrl,
+ KEYGRIP_ACTION_LOOKUP, keyref))
+ break;
+ if (!app_prev && ctrl->current_apptype == card->app->apptype)
+ return 0; /* Already the first app - no need to switch. */
+ }
+ }
+
+ if (!app)
+ {
+ /* Switch based on the current application of this connection or
+ * if a keyref based switch didn't worked. */
+ if (ctrl->current_apptype == card->app->apptype)
+ return 0; /* No need to switch. */
+ app_prev = card->app;
+ for (app = app_prev->next; app; app_prev = app, app = app->next)
+ if (app->apptype == ctrl->current_apptype)
+ break;
+ }
if (!app)
return gpg_error (GPG_ERR_WRONG_CARD);
if (!app->fnc.reselect)
{
log_error ("oops: reselect function missing for '%s'\n",
- strapptype(app->apptype));
+ strapptype (app->apptype));
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
}
err = app->fnc.reselect (app, ctrl);
if (err)
{
- log_error ("error re-selecting '%s': %s\n",
- strapptype(app->apptype), gpg_strerror (err));
+ log_error ("card %d: error re-selecting '%s': %s\n",
+ card->slot, xstrapptype (app), gpg_strerror (err));
return err;
}
- /* Swap APP with the head of the app list. Note that APP is not the
- * head of the list. */
- app_prev->next = app->next;
- app->next = card->app;
- card->app = app;
+
+ /* Swap APP with the head of the app list if needed. Note that APP
+ * is not the head of the list. */
+ if (app_prev)
+ {
+ app_prev->next = app->next;
+ app->next = card->app;
+ card->app = app;
+ }
+
+ if (opt.verbose)
+ log_info ("card %d: %s '%s'\n",
+ card->slot, app_prev? "switched to":"re-selected",
+ xstrapptype (app));
+
ctrl->current_apptype = app->apptype;
- log_info ("switched to '%s'\n", strapptype (app->apptype));
return 0;
}
@@ -1162,7 +1233,7 @@ app_write_learn_status (card_t card, ctrl_t ctrl, unsigned int flags)
/* Always make sure that the current app for this connection has
* been selected and is at the top of the list. */
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, NULL)))
;
else if (!card->app->fnc.learn_status)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
@@ -1220,7 +1291,7 @@ app_readcert (card_t card, ctrl_t ctrl, const char *certid,
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, certid)))
;
else if (!card->app->fnc.readcert)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
@@ -1262,7 +1333,7 @@ app_readkey (card_t card, ctrl_t ctrl, const char *keyid, unsigned int flags,
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, keyid)))
;
else if (!card->app->fnc.readkey)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
@@ -1291,7 +1362,7 @@ app_getattr (card_t card, ctrl_t ctrl, const char *name)
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, NULL)))
;
else if (name && !strcmp (name, "CARDTYPE"))
{
@@ -1344,7 +1415,7 @@ app_setattr (card_t card, ctrl_t ctrl, const char *name,
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, NULL)))
;
else if (!card->app->fnc.setattr)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
@@ -1380,7 +1451,7 @@ app_sign (card_t card, ctrl_t ctrl, const char *keyidstr, int hashalgo,
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, keyidstr)))
;
else if (!card->app->fnc.sign)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
@@ -1421,7 +1492,7 @@ app_auth (card_t card, ctrl_t ctrl, const char *keyidstr,
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, keyidstr)))
;
else if (!card->app->fnc.auth)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
@@ -1464,7 +1535,7 @@ app_decipher (card_t card, ctrl_t ctrl, const char *keyidstr,
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, keyidstr)))
;
else if (!card->app->fnc.decipher)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
@@ -1503,7 +1574,7 @@ app_writecert (card_t card, ctrl_t ctrl,
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, certidstr)))
;
else if (!card->app->fnc.writecert)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
@@ -1539,7 +1610,7 @@ app_writekey (card_t card, ctrl_t ctrl,
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, keyidstr)))
;
else if (!card->app->fnc.writekey)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
@@ -1574,7 +1645,7 @@ app_genkey (card_t card, ctrl_t ctrl, const char *keynostr,
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, keynostr)))
;
else if (!card->app->fnc.genkey)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
@@ -1634,7 +1705,7 @@ app_change_pin (card_t card, ctrl_t ctrl, const char *chvnostr,
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, NULL)))
;
else if (!card->app->fnc.change_pin)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
@@ -1670,7 +1741,7 @@ app_check_pin (card_t card, ctrl_t ctrl, const char *keyidstr,
if (err)
return err;
- if ((err = maybe_switch_app (ctrl, card)))
+ if ((err = maybe_switch_app (ctrl, card, NULL)))
;
else if (!card->app->fnc.check_pin)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);