scd: Make SERIALNO --all work correctly and use it.

* scd/app.c (maybe_switch_app): Factor reselect code out to ...
(run_reselect): new.
(app_write_learn_status): Tweak diagnostics.
(app_do_with_keygrip): Run reselect if a card has more than one
switchable application.

* agent/call-scd.c (agent_card_serialno): Ditto.
* tools/card-call-scd.c (start_agent): Use option --all with SERIALNO.
(scd_serialno): Ditto.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2020-01-13 12:08:23 +01:00
parent 15028627a1
commit 0e48aa0849
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
5 changed files with 94 additions and 57 deletions

View File

@ -854,7 +854,7 @@ agent_card_serialno (ctrl_t ctrl, char **r_serialno, const char *demand)
return rc;
if (!demand)
strcpy (line, "SERIALNO");
strcpy (line, "SERIALNO --all");
else
snprintf (line, DIM(line), "SERIALNO --demand=%s", demand);

View File

@ -764,13 +764,13 @@ get_dispserialno (app_t app, int failmode)
/* The verify command can be used to retrieve the security status of
* the card. Given the PIN name (e.g. "PIV.80" for thge application
* the card. Given the PIN name (e.g. "PIV.80" for the application
* pin, a status is returned:
*
* -1 = Error retrieving the data,
* -2 = No such PIN,
* -3 = PIN blocked,
* -5 = Verify still valid,
* -5 = Verified and still valid,
* n >= 0 = Number of verification attempts left.
*/
static int

139
scd/app.c
View File

@ -300,7 +300,7 @@ is_app_allowed (const char *name)
* to switch to the requested application.
* Other code - Switching is not possible.
*
* If SERIALNO_BIN is not NULL a coflict is onl asserted if the
* If SERIALNO_BIN is not NULL a conflict is only asserted if the
* serialno of the card matches.
*/
gpg_error_t
@ -1032,7 +1032,7 @@ card_unref_locked (card_t card)
FF 02 00 = Serial number from Yubikey config
FF 7F 00 = No serialno.
All other serial number not starting with FF are used as they are.
All other serial numbers not starting with FF are used as they are.
*/
gpg_error_t
app_munge_serialno (card_t card)
@ -1094,6 +1094,46 @@ app_get_serialno (app_t app)
}
/* Helper to run the reselect function. */
static gpg_error_t
run_reselect (ctrl_t ctrl, card_t c, app_t a, app_t a_prev)
{
gpg_error_t err;
if (!a->fnc.reselect)
{
log_info ("slot %d, app %s: re-select not implemented\n",
c->slot, xstrapptype (a));
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
}
/* Give the current app a chance to save some state before another
* app is selected. We ignore errors here because that state saving
* (e.g. putting PINs into a cache) is a convenience feature and not
* required to always work. */
if (a_prev && a_prev->fnc.prep_reselect)
{
err = a_prev->fnc.prep_reselect (a_prev, ctrl);
if (err)
log_error ("slot %d, app %s: preparing re-select from %s failed: %s\n",
c->slot, xstrapptype (a),
xstrapptype (a_prev), gpg_strerror (err));
}
err = a->fnc.reselect (a, ctrl);
if (err)
{
log_error ("slot %d, app %s: error re-selecting: %s\n",
c->slot, xstrapptype (a), gpg_strerror (err));
return err;
}
if (DBG_APP)
log_debug ("slot %d, app %s: re-selected\n", c->slot, xstrapptype (a));
return 0;
}
/* Check that the card has been initialized and whether we need to
* switch to another application on the same card. Switching means
* that the new active app will be moved to the head of the list at
@ -1162,33 +1202,9 @@ maybe_switch_app (ctrl_t ctrl, card_t card, const char *keyref)
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));
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
}
/* Give the current app a chance to save some state before another
* app is selected. We ignore errors here because that state saving
* (e.g. putting PINs into a cache) is a convenience feature and not
* required to always work. */
if (app_prev && app_prev->fnc.prep_reselect)
{
err = app_prev->fnc.prep_reselect (app_prev, ctrl);
if (err)
log_info ("card %d: preparing re-select failed for '%s': %s\n",
card->slot, xstrapptype (app_prev), gpg_strerror (err));
err = 0;
}
err = app->fnc.reselect (app, ctrl);
err = run_reselect (ctrl, card, app, app_prev);
if (err)
{
log_error ("card %d: error re-selecting '%s': %s\n",
card->slot, xstrapptype (app), gpg_strerror (err));
return err;
}
return err;
/* Swap APP with the head of the app list if needed. Note that APP
* is not the head of the list. */
@ -1200,9 +1216,9 @@ maybe_switch_app (ctrl_t ctrl, card_t card, const char *keyref)
}
if (opt.verbose)
log_info ("card %d: %s '%s'\n",
card->slot, app_prev? "switched to":"re-selected",
xstrapptype (app));
log_info ("slot %d, app %s: %s\n",
card->slot, xstrapptype (app),
app_prev? "switched":"re-selected");
ctrl->current_apptype = app->apptype;
@ -1271,8 +1287,10 @@ app_write_learn_status (card_t card, ctrl_t ctrl, unsigned int flags)
{
tmperr = last_app->fnc.prep_reselect (last_app, ctrl);
if (tmperr)
log_info ("card %d: preparing re-select failed for '%s'"
": %s\n", card->slot, xstrapptype (last_app),
log_info ("slot %d, app %s:"
" preparing re-select from %s failed: %s\n",
card->slot, xstrapptype (app),
xstrapptype (last_app),
gpg_strerror (tmperr));
}
any_reselect = 1;
@ -1290,9 +1308,10 @@ app_write_learn_status (card_t card, ctrl_t ctrl, unsigned int flags)
{
tmperr = last_app->fnc.prep_reselect (last_app, ctrl);
if (tmperr)
log_info ("card %d: preparing re-select failed for '%s'"
": %s\n", card->slot, xstrapptype (last_app),
gpg_strerror (tmperr));
log_info ("slot %d, app %s:"
" preparing re-select from %s failed: %s\n",
card->slot, xstrapptype (app),
xstrapptype (last_app), gpg_strerror (tmperr));
}
err2 = app->fnc.reselect (app, ctrl);
if (err2)
@ -2027,8 +2046,9 @@ app_do_with_keygrip (ctrl_t ctrl, int action, const char *keygrip_str,
int capability)
{
int locked = 0;
gpg_error_t err;
card_t c;
app_t a;
app_t a, a_prev;
npth_mutex_lock (&card_list_lock);
@ -2040,26 +2060,43 @@ app_do_with_keygrip (ctrl_t ctrl, int action, const char *keygrip_str,
goto leave_the_loop;
}
locked = 1;
a_prev = NULL;
for (a = c->app; a; a = a->next)
if (a->fnc.with_keygrip)
{
if (DBG_APP)
log_debug ("slot %d app %s: calling with_keygrip(action=%d)\n",
c->slot, xstrapptype (a), action);
if (!a->fnc.with_keygrip (a, ctrl, action, keygrip_str, capability))
goto leave_the_loop;
}
{
if (!a->fnc.with_keygrip)
continue;
/* Note that we need to do a re-select even for the current
* app because the last selected application (e.g. after
* init) might be a different one and we do not run
* maybe_switch_app here. Of course we we do this only iff
* we have an additional app. */
if (c->app->next)
{
if (run_reselect (ctrl, c, a, a_prev))
continue;
}
a_prev = a;
if (DBG_APP)
log_debug ("slot %d, app %s: calling with_keygrip(%s)\n",
c->slot, xstrapptype (a),
action == KEYGRIP_ACTION_SEND_DATA? "send_data":
action == KEYGRIP_ACTION_WRITE_STATUS? "write_data":
action == KEYGRIP_ACTION_LOOKUP? "lookup":"?");
if (!a->fnc.with_keygrip (a, ctrl, action, keygrip_str, capability))
goto leave_the_loop; /* ACTION_LOOKUP succeeded. */
}
/* Select the first app again. */
if (c->app->next)
run_reselect (ctrl, c, c->app, a_prev);
unlock_card (c);
locked = 0;
}
leave_the_loop:
/* FIXME: Add app switching logic. The above code assumes that the
* actions can be performend without switching. This needs to be
* checked. */
/* Force switching of the app if the selected one is not the current
* one. Changing the current apptype is sufficient to do this. */
if (c && c->app && c->app->apptype != a->apptype)

View File

@ -282,7 +282,7 @@ static const char hlp_serialno[] =
"selected and an error is returned if no such card available.\n"
"\n"
"If --all is given, all possible other applications of the card are\n"
"also selected to prepare for \"LEARN --force --multi\".\n"
"also selected to prepare for things like \"LEARN --force --multi\".\n"
"\n"
"If APPTYPE is given, an application of that type is selected and an\n"
"error is returned if the application is not supported or available.\n"

View File

@ -360,7 +360,7 @@ start_agent (unsigned int flags)
err = warn_version_mismatch (agent_ctx, SCDAEMON_NAME, 2);
if (!err)
err = assuan_transact (agent_ctx, "SCD SERIALNO",
err = assuan_transact (agent_ctx, "SCD SERIALNO --all",
NULL, NULL, NULL, NULL,
learn_status_cb, &info);
if (err && !(flags & START_AGENT_SUPPRESS_ERRORS))
@ -1284,7 +1284,7 @@ scd_serialno (char **r_serialno, const char *demand)
return err;
if (!demand)
strcpy (line, "SCD SERIALNO");
strcpy (line, "SCD SERIALNO --all");
else
snprintf (line, DIM(line), "SCD SERIALNO --demand=%s", demand);