1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-11-10 21:38:50 +01:00

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>
This commit is contained in:
Werner Koch 2019-09-05 14:05:05 +02:00
parent 5d9eb060b7
commit 7febb4f247
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B

139
scd/app.c
View File

@ -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. /* Initialization function to change the default app_priority_list.
* LIST is a list of comma or space separated strings with application * LIST is a list of comma or space separated strings with application
* names. Unknown names will only result in warning message. * 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", log_info ("app_dump_state: card=%p slot=%d type=%s\n",
c, c->slot, strcardtype (c->cardtype)); c, c->slot, strcardtype (c->cardtype));
/* FIXME The use of log_info risks a race! */
for (a=c->app; a; a = a->next) for (a=c->app; a; a = a->next)
log_info ("app_dump_state: app=%p type='%s'\n", log_info ("app_dump_state: app=%p type='%s'\n",
a, strapptype (a->apptype)); 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 * 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. */ * CARD->app. Thus function must be called with the card lock held. */
static gpg_error_t 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; 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) if (!card->ref_count || !card->app)
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); 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; ctrl->current_apptype = card->app->apptype;
return 0; return 0;
} }
log_debug ("card=%p want=%s card->app=%s\n", if (DBG_APP)
card, strapptype (ctrl->current_apptype), log_debug ("slot %d: have=%s want=%s keyref=%s\n",
strapptype (card->app->apptype)); card->slot, strapptype (card->app->apptype),
app_dump_state (); strapptype (ctrl->current_apptype),
if (ctrl->current_apptype == card->app->apptype) keyref? keyref:"[none]");
return 0; /* No need to switch. */
app_prev = card->app; app = NULL;
for (app = app_prev->next; app; app_prev = app, app = app->next) if (keyref)
if (app->apptype == ctrl->current_apptype) {
break; /* 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) if (!app)
return gpg_error (GPG_ERR_WRONG_CARD); return gpg_error (GPG_ERR_WRONG_CARD);
if (!app->fnc.reselect) if (!app->fnc.reselect)
{ {
log_error ("oops: reselect function missing for '%s'\n", log_error ("oops: reselect function missing for '%s'\n",
strapptype(app->apptype)); strapptype (app->apptype));
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
} }
err = app->fnc.reselect (app, ctrl); err = app->fnc.reselect (app, ctrl);
if (err) if (err)
{ {
log_error ("error re-selecting '%s': %s\n", log_error ("card %d: error re-selecting '%s': %s\n",
strapptype(app->apptype), gpg_strerror (err)); card->slot, xstrapptype (app), gpg_strerror (err));
return err; return err;
} }
/* Swap APP with the head of the app list. Note that APP is not the
* head of the list. */ /* Swap APP with the head of the app list if needed. Note that APP
app_prev->next = app->next; * is not the head of the list. */
app->next = card->app; if (app_prev)
card->app = app; {
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; ctrl->current_apptype = app->apptype;
log_info ("switched to '%s'\n", strapptype (app->apptype));
return 0; 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 /* Always make sure that the current app for this connection has
* been selected and is at the top of the list. */ * 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) else if (!card->app->fnc.learn_status)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
@ -1220,7 +1291,7 @@ app_readcert (card_t card, ctrl_t ctrl, const char *certid,
if (err) if (err)
return err; return err;
if ((err = maybe_switch_app (ctrl, card))) if ((err = maybe_switch_app (ctrl, card, certid)))
; ;
else if (!card->app->fnc.readcert) else if (!card->app->fnc.readcert)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); 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) if (err)
return err; return err;
if ((err = maybe_switch_app (ctrl, card))) if ((err = maybe_switch_app (ctrl, card, keyid)))
; ;
else if (!card->app->fnc.readkey) else if (!card->app->fnc.readkey)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
@ -1291,7 +1362,7 @@ app_getattr (card_t card, ctrl_t ctrl, const char *name)
if (err) if (err)
return err; return err;
if ((err = maybe_switch_app (ctrl, card))) if ((err = maybe_switch_app (ctrl, card, NULL)))
; ;
else if (name && !strcmp (name, "CARDTYPE")) else if (name && !strcmp (name, "CARDTYPE"))
{ {
@ -1344,7 +1415,7 @@ app_setattr (card_t card, ctrl_t ctrl, const char *name,
if (err) if (err)
return err; return err;
if ((err = maybe_switch_app (ctrl, card))) if ((err = maybe_switch_app (ctrl, card, NULL)))
; ;
else if (!card->app->fnc.setattr) else if (!card->app->fnc.setattr)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); 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) if (err)
return err; return err;
if ((err = maybe_switch_app (ctrl, card))) if ((err = maybe_switch_app (ctrl, card, keyidstr)))
; ;
else if (!card->app->fnc.sign) else if (!card->app->fnc.sign)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
@ -1421,7 +1492,7 @@ app_auth (card_t card, ctrl_t ctrl, const char *keyidstr,
if (err) if (err)
return err; return err;
if ((err = maybe_switch_app (ctrl, card))) if ((err = maybe_switch_app (ctrl, card, keyidstr)))
; ;
else if (!card->app->fnc.auth) else if (!card->app->fnc.auth)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
@ -1464,7 +1535,7 @@ app_decipher (card_t card, ctrl_t ctrl, const char *keyidstr,
if (err) if (err)
return err; return err;
if ((err = maybe_switch_app (ctrl, card))) if ((err = maybe_switch_app (ctrl, card, keyidstr)))
; ;
else if (!card->app->fnc.decipher) else if (!card->app->fnc.decipher)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
@ -1503,7 +1574,7 @@ app_writecert (card_t card, ctrl_t ctrl,
if (err) if (err)
return err; return err;
if ((err = maybe_switch_app (ctrl, card))) if ((err = maybe_switch_app (ctrl, card, certidstr)))
; ;
else if (!card->app->fnc.writecert) else if (!card->app->fnc.writecert)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
@ -1539,7 +1610,7 @@ app_writekey (card_t card, ctrl_t ctrl,
if (err) if (err)
return err; return err;
if ((err = maybe_switch_app (ctrl, card))) if ((err = maybe_switch_app (ctrl, card, keyidstr)))
; ;
else if (!card->app->fnc.writekey) else if (!card->app->fnc.writekey)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
@ -1574,7 +1645,7 @@ app_genkey (card_t card, ctrl_t ctrl, const char *keynostr,
if (err) if (err)
return err; return err;
if ((err = maybe_switch_app (ctrl, card))) if ((err = maybe_switch_app (ctrl, card, keynostr)))
; ;
else if (!card->app->fnc.genkey) else if (!card->app->fnc.genkey)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); 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) if (err)
return 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) else if (!card->app->fnc.change_pin)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); 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) if (err)
return 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) else if (!card->app->fnc.check_pin)
err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);