From 718555874efcbad502112449c7d15025cb193628 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 16 Jan 2020 19:42:16 +0100 Subject: [PATCH] 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 --- scd/app-common.h | 8 +++ scd/app.c | 177 ++++++++++++++++++++++++++++++++++++++++++++--- scd/command.c | 90 ++++++++++++++++++++++-- 3 files changed, 262 insertions(+), 13 deletions(-) diff --git a/scd/app-common.h b/scd/app-common.h index 72ad8e7cd..dc5684b39 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -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); diff --git a/scd/app.c b/scd/app.c index 5fa500ad2..3cd40608b 100644 --- a/scd/app.c +++ b/scd/app.c @@ -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 diff --git a/scd/command.c b/scd/command.c index 46c879e6b..60904429c 100644 --- a/scd/command.c +++ b/scd/command.c @@ -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 []\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 []\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 },