scd: New commands SWITCHCARD and SWITCHAPP.

* scd/app.c: Include membuf.h.
(app_switch_current_card): New.
(send_card_and_app_list): Factor code out to ...
(send_serialno_and_app_status): new.
(app_send_card_list): New.
(app_send_active_apps): New.
(app_switch_active_app): New.
* scd/command.c (cmd_switchcard): New.
(cmd_switchapp): New.
(register_commands): Register new commands.
(cmd_getinfo): New sub-commands "active_apps" and "all_active_apps".
--

These new commands allow to switch between known cards and are in
particular useful for the gpg-card tool.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2020-01-16 19:42:16 +01:00
parent dd61164410
commit 718555874e
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
3 changed files with 262 additions and 13 deletions

View File

@ -229,6 +229,7 @@ const char *strapptype (apptype_t t);
void app_update_priority_list (const char *arg);
gpg_error_t app_send_card_list (ctrl_t ctrl);
gpg_error_t app_send_active_apps (card_t card, ctrl_t ctrl);
char *card_get_serialno (card_t card);
char *app_get_serialno (app_t app);
@ -242,6 +243,13 @@ gpg_error_t select_application (ctrl_t ctrl, const char *name, card_t *r_app,
int scan, const unsigned char *serialno_bin,
size_t serialno_bin_len);
gpg_error_t select_additional_application (ctrl_t ctrl, const char *name);
gpg_error_t app_switch_current_card (ctrl_t ctrl,
const unsigned char *serialno,
size_t serialnolen);
gpg_error_t app_switch_active_app (card_t card, ctrl_t ctrl,
const char *appname);
char *get_supported_applications (void);
card_t card_ref (card_t card);

177
scd/app.c
View File

@ -29,11 +29,14 @@
#include "iso7816.h"
#include "apdu.h"
#include "../common/tlv.h"
#include "../common/membuf.h"
/* Forward declaration of internal function. */
static gpg_error_t
select_additional_application_internal (card_t card, apptype_t req_apptype);
static gpg_error_t
send_serialno_and_app_status (card_t card, int with_apps, ctrl_t ctrl);
/* Lock to protect the list of cards and its associated
* applications. */
@ -730,6 +733,56 @@ select_application (ctrl_t ctrl, const char *name, card_t *r_card,
}
/* Switch the current card for the session CTRL and print a SERIALNO
* status line on success. (SERIALNO, SERIALNOLEN) is the binary s/n
* of the card to switch to. */
gpg_error_t
app_switch_current_card (ctrl_t ctrl,
const unsigned char *serialno, size_t serialnolen)
{
gpg_error_t err;
card_t card, cardtmp;
npth_mutex_lock (&card_list_lock);
if (!ctrl->card_ctx)
{
err = gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
goto leave;
}
if (serialno && serialnolen)
{
for (card = card_top; card; card = card->next)
{
if (card->serialnolen == serialnolen
&& !memcmp (card->serialno, serialno, card->serialnolen))
break;
}
if (!card)
{
err = gpg_error (GPG_ERR_NOT_FOUND);
goto leave;
}
/* Note: We do not use card_ref here because we only swap the
* context of the current session and there is no chance of a
* context switch. This also works if the card stays the same. */
cardtmp = ctrl->card_ctx;
ctrl->card_ctx = card;
card->ref_count++;
card_unref_locked (cardtmp);
}
/* Print the status line. */
err = send_serialno_and_app_status (ctrl->card_ctx, 0, ctrl);
leave:
npth_mutex_unlock (&card_list_lock);
return err;
}
static gpg_error_t
select_additional_application_internal (card_t card, apptype_t req_apptype)
{
@ -1975,13 +2028,58 @@ compare_card_list_items (const void *arg_a, const void *arg_b)
}
/* Send status lines with the serialno of all inserted cards. */
gpg_error_t
app_send_card_list (ctrl_t ctrl)
/* Helper for send_card_and_app_list and app_switch_active_app. */
static gpg_error_t
send_serialno_and_app_status (card_t card, int with_apps, ctrl_t ctrl)
{
gpg_error_t err;
app_t a;
char buf[65];
char *p;
membuf_t mb;
if (DIM (buf) < 2 * card->serialnolen + 1)
return 0; /* Oops. */
bin2hex (card->serialno, card->serialnolen, buf);
if (with_apps)
{
/* Note that in case the additional applications have not yet been
* added to the card context (which is commonly done by means of
* "SERIALNO --all", we do that here. */
err = select_all_additional_applications_internal (card);
if (err)
return err;
init_membuf (&mb, 256);
put_membuf_str (&mb, buf);
for (a = card->app; a; a = a->next)
{
if (!a->fnc.with_keygrip)
continue;
put_membuf (&mb, " ", 1);
put_membuf_str (&mb, xstrapptype (a));
}
put_membuf (&mb, "", 1);
p = get_membuf (&mb, NULL);
if (!p)
return gpg_error_from_syserror ();
send_status_direct (ctrl, "SERIALNO", p);
xfree (p);
}
else
send_status_direct (ctrl, "SERIALNO", buf);
return 0;
}
/* Common code for app_send_card_list and app_send_active_apps. */
static gpg_error_t
send_card_and_app_list (ctrl_t ctrl, card_t wantcard, int with_apps)
{
gpg_error_t err;
card_t c;
char buf[65];
card_t *cardlist = NULL;
int n, ncardlist;
@ -2000,11 +2098,11 @@ app_send_card_list (ctrl_t ctrl)
for (n=0; n < ncardlist; n++)
{
if (DIM (buf) < 2 * cardlist[n]->serialnolen + 1)
if (wantcard && wantcard != cardlist[n])
continue;
bin2hex (cardlist[n]->serialno, cardlist[n]->serialnolen, buf);
send_status_direct (ctrl, "SERIALNO", buf);
err = send_serialno_and_app_status (cardlist[n], with_apps, ctrl);
if (err)
goto leave;
}
err = 0;
@ -2016,6 +2114,69 @@ app_send_card_list (ctrl_t ctrl)
}
/* Send status lines with the serialno of all inserted cards. */
gpg_error_t
app_send_card_list (ctrl_t ctrl)
{
return send_card_and_app_list (ctrl, NULL, 0);
}
/* Send status lines with the serialno and appname of the current card
* or of all cards if CARD is NULL. */
gpg_error_t
app_send_active_apps (card_t card, ctrl_t ctrl)
{
return send_card_and_app_list (ctrl, card, 1);
}
/* Switch to APPNAME and print a respective status line with that app
* listed first. If APPNAME is NULL or the empty string no switching
* is done but the status line is printed anyway. */
gpg_error_t
app_switch_active_app (card_t card, ctrl_t ctrl, const char *appname)
{
gpg_error_t err;
apptype_t apptype;
if (!card)
return gpg_error (GPG_ERR_INV_VALUE);
err = lock_card (card, ctrl);
if (err)
return err;
/* Note that in case the additional applications have not yet been
* added to the card context (which is commonly done by means of
* "SERIALNO --all", we do that here. */
err = select_all_additional_applications_internal (card);
if (err)
goto leave;
if (appname && *appname)
{
apptype = apptype_from_name (appname);
if (!apptype)
{
err = gpg_error (GPG_ERR_NOT_FOUND);
goto leave;
}
ctrl->current_apptype = apptype;
err = maybe_switch_app (ctrl, card, NULL);
if (err)
goto leave;
}
/* Print the status line. */
err = send_serialno_and_app_status (card, 1, ctrl);
leave:
unlock_card (card);
return err;
}
/* Execute an action for each app. ACTION can be one of:
*
* - KEYGRIP_ACTION_SEND_DATA

View File

@ -287,8 +287,7 @@ static const char hlp_serialno[] =
"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"
"The default is to auto-select the application using a hardwired\n"
"preference system. Note, that a future extension to this function\n"
"may enable specifying a list and order of applications to try.\n"
"preference system.\n"
"\n"
"This function is special in that it can be used to reset the card.\n"
"Most other functions will return an error when a card change has\n"
@ -354,6 +353,70 @@ cmd_serialno (assuan_context_t ctx, char *line)
}
static const char hlp_switchcard[] =
"SWITCHCARD [<serialno>]\n"
"\n"
"Make the card with SERIALNO the current card.\n"
"The command \"getinfo card_list\" can be used to list\n"
"the serial numbers of inserted and known cards. Note\n"
"that the command \"SERIALNO\" can be used to refresh\n"
"the list of known cards. A simple SERIALNO status\n"
"is printed on success.";
static gpg_error_t
cmd_switchcard (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err = 0;
unsigned char *sn_bin = NULL;
size_t sn_bin_len = 0;
if ((err = open_card (ctrl)))
return err;
line = skip_options (line);
if (*line)
{
sn_bin = hex_to_buffer (line, &sn_bin_len);
if (!sn_bin)
{
err = gpg_error_from_syserror ();
goto leave;
}
}
/* Note that an SN_BIN of NULL will only print the status. */
err = app_switch_current_card (ctrl, sn_bin, sn_bin_len);
leave:
xfree (sn_bin);
return err;
}
static const char hlp_switchapp[] =
"SWITCHAPP [<appname>]\n"
"\n"
"Make APPNAME the active application for the current card.\n"
"Only some cards support switching between application; the\n"
"command \"getinfo active_app\" can be used to get a list of\n"
"applications which can be switched to. A SERIALNO status\n"
"including the active appname is printed on success.";
static gpg_error_t
cmd_switchapp (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err = 0;
if ((err = open_card (ctrl)))
return err;
line = skip_options (line);
return app_switch_active_app (ctrl->card_ctx, ctrl, line);
}
static const char hlp_learn[] =
"LEARN [--force] [--keypairinfo] [--multi]\n"
"\n"
@ -1641,10 +1704,12 @@ static const char hlp_getinfo[] =
" app_list - Return a list of supported applications. One\n"
" application per line, fields delimited by colons,\n"
" first field is the name.\n"
" card_list - Return a list of serial numbers of active cards,\n"
" using a status response.\n"
" card_list - Return a list of serial numbers of all inserted cards.\n"
" active_apps - Return a list of active apps on the current card.\n"
" all_active_apps\n"
" - Return a list of active apps on all inserted cards.\n"
" cmd_has_option CMD OPT\n"
" - Returns OK if command CMD has option OPT.\n";
" - Returns OK if command CMD has option OPT.\n";
static gpg_error_t
cmd_getinfo (assuan_context_t ctx, char *line)
{
@ -1753,6 +1818,19 @@ cmd_getinfo (assuan_context_t ctx, char *line)
rc = app_send_card_list (ctrl);
}
else if (!strcmp (line, "active_apps"))
{
ctrl_t ctrl = assuan_get_pointer (ctx);
if (!ctrl->card_ctx)
rc = 0; /* No current card - no active apps. */
else
rc = app_send_active_apps (ctrl->card_ctx, ctrl);
}
else if (!strcmp (line, "all_active_apps"))
{
ctrl_t ctrl = assuan_get_pointer (ctx);
rc = app_send_active_apps (NULL, ctrl);
}
else
rc = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
return rc;
@ -2069,6 +2147,8 @@ register_commands (assuan_context_t ctx)
const char * const help;
} table[] = {
{ "SERIALNO", cmd_serialno, hlp_serialno },
{ "SWITCHCARD", cmd_switchcard,hlp_switchcard },
{ "SWITCHAPP", cmd_switchapp,hlp_switchapp },
{ "LEARN", cmd_learn, hlp_learn },
{ "READCERT", cmd_readcert, hlp_readcert },
{ "READKEY", cmd_readkey, hlp_readkey },