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:
parent
5d9eb060b7
commit
7febb4f247
139
scd/app.c
139
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.
|
/* 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);
|
||||||
|
Loading…
Reference in New Issue
Block a user