mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +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:
parent
5d9eb060b7
commit
7febb4f247
119
scd/app.c
119
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,16 +1112,50 @@ 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 (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);
|
||||
|
||||
@ -1106,17 +1168,26 @@ maybe_switch_app (ctrl_t ctrl, card_t card)
|
||||
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. */
|
||||
|
||||
/* 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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user