diff options
author | Werner Koch <wk@gnupg.org> | 2019-06-25 08:30:04 +0200 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2019-06-25 09:21:14 +0200 |
commit | d803b3bb3c084b6bce4d2bd161db50dc45442e5b (patch) | |
tree | f316cf0e99db9629dc24de46212efe5e69fc7408 /scd/app.c | |
parent | spelling: Fix "synchronize" (diff) | |
download | gnupg2-d803b3bb3c084b6bce4d2bd161db50dc45442e5b.tar.xz gnupg2-d803b3bb3c084b6bce4d2bd161db50dc45442e5b.zip |
scd: Add an re-select mechanism to switch apps.
* scd/app-common.h (struct app_ctx_s): Add func ptr 'reselect'.
* scd/app-piv.c (do_reselect): New.
(app_select_piv): Move AID constant to file scope.
* scd/app-openpgp.c (do_reselect): New.
(app_select_openpgp): Move AID constant to file scope.
* scd/app.c (apptype_from_name): New.
(check_application_conflict): Check against all apps of the card.
Always set current_apptype.
(select_additional_application): New.
(maybe_switch_app): New.
(app_write_learn_status, app_readcert, app_readkey, app_getattr)
(app_setattr, app_sign, app_auth, app_decipher, app_writecert)
(app_writekey, app_genkey, app_change_pin, app_check_pin): Use it here.
(app_do_with_keygrip): Force reselect on success.
(app_new_register): Move setting of CURRENT_APPTYPE to ...
(select_application): here so that it will be set to the requested
card.
* scd/command.c (open_card_with_request): Select additional
application if possible.
--
Noet that we will likely need to rework this even more so to get well
defined semantics for card access.
Signed-off-by: Werner Koch <wk@gnupg.org>
Diffstat (limited to 'scd/app.c')
-rw-r--r-- | scd/app.c | 234 |
1 files changed, 196 insertions, 38 deletions
@@ -91,6 +91,24 @@ strapptype (apptype_t t) } +/* Return the apptype for NAME. */ +static apptype_t +apptype_from_name (const char *name) +{ + int i; + + if (!name) + return APPTYPE_NONE; + + for (i=0; app_priority_list[i].apptype; i++) + if (!ascii_strcasecmp (app_priority_list[i].name, name)) + return app_priority_list[i].apptype; + if (!ascii_strcasecmp ("undefined", name)) + return APPTYPE_UNDEFINED; + 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. @@ -247,14 +265,18 @@ is_app_allowed (const char *name) gpg_error_t check_application_conflict (card_t card, const char *name) { + app_t app; + if (!card || !name) return 0; if (!card->app) return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); /* Should not happen. */ - if (!card->app->apptype - || !ascii_strcasecmp (strapptype (card->app->apptype), name)) - return 0; + /* Check whether the requested NAME matches any already selected + * application. */ + for (app = card->app; app; app = app->next) + if (!ascii_strcasecmp (strapptype (app->apptype), name)) + return 0; if (card->app->apptype == APPTYPE_UNDEFINED) return 0; @@ -535,10 +557,6 @@ app_new_register (int slot, ctrl_t ctrl, const char *name, card->next = card_top; card_top = card; - /* If no current apptype is known for this session, set it now. */ - if (!ctrl->current_apptype) - ctrl->current_apptype = app->apptype; - unlock_card (card); return 0; } @@ -632,7 +650,9 @@ select_application (ctrl_t ctrl, const char *name, card_t *r_card, card->next = card_top; card_top = card; } - } + + ctrl->current_apptype = card->app ? card->app->apptype : 0; + } unlock_card (card); } else @@ -644,6 +664,82 @@ select_application (ctrl_t ctrl, const char *name, card_t *r_card, } +/* This function needs to be called with the NAME of the new + * application to be selected on CARD. On success the application is + * added to the list of the card's active applications as currently + * active application. On error no new application is allocated. + * Selecting an already selected application has no effect. */ +gpg_error_t +select_additional_application (ctrl_t ctrl, const char *name) +{ + gpg_error_t err = 0; + apptype_t req_apptype; + card_t card; + app_t app = NULL; + int i; + + req_apptype = apptype_from_name (name); + if (!req_apptype) + err = gpg_error (GPG_ERR_NOT_FOUND); + + card = ctrl->card_ctx; + if (!card) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + + err = lock_card (card, ctrl); + if (err) + return err; + + /* Check that the requested app has not yet been put onto the list. */ + for (app = card->app; app; app = app->next) + if (app->apptype == req_apptype) + { + /* We already got this one. Note that in this case we don't + * make it the current one but it doesn't matter because + * maybe_switch_app will do that anyway. */ + err = 0; + app = NULL; + goto leave; + } + app = NULL; + + /* Allocate a new app object. */ + app = xtrycalloc (1, sizeof *app); + if (!app) + { + err = gpg_error_from_syserror (); + log_info ("error allocating app context: %s\n", gpg_strerror (err)); + goto leave; + } + + /* Find the app and run the select. */ + for (i=0; app_priority_list[i].apptype; i++) + { + if (app_priority_list[i].apptype == req_apptype + && is_app_allowed (app_priority_list[i].name)) + err = app_priority_list[i].select_func (app); + } + if (!app_priority_list[i].apptype + || (err && gpg_err_code (err) != GPG_ERR_OBJ_TERM_STATE)) + err = gpg_error (GPG_ERR_NOT_SUPPORTED); + + if (err) + goto leave; + + /* Add this app. We make it the current one to avoid an extra + * reselect by maybe_switch_app after the select we just did. */ + app->next = card->app; + card->app = app; + ctrl->current_apptype = app->apptype; + log_error ("added app '%s' to the card context\n", strapptype(app->apptype)); + + leave: + unlock_card (card); + xfree (app); + return err; +} + + char * get_supported_applications (void) { @@ -831,6 +927,63 @@ app_get_serialno (app_t app) } +/* 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 + * 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) +{ + gpg_error_t err; + app_t app, app_prev; + + if (!card->ref_count || !card->app) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (!ctrl->current_apptype) + { + /* For whatever reasons the current apptype has not been set - + * fix that and use the current app. */ + 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 (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); + + if (!app->fnc.reselect) + { + log_error ("oops: reselect function missing for '%s'\n", + strapptype(app->apptype)); + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + } + err = app->fnc.reselect (app, ctrl); + if (err) + { + log_error ("error re-selecting '%s': %s\n", + strapptype(app->apptype), gpg_strerror (err)); + return err; + } + /* Swap APP with the head of the app list. Note that APP is not the + * head of the list. */ + app_prev->next = app->next; + app->next = card->app; + card->app = app; + ctrl->current_apptype = app->apptype; + log_error ("switched to '%s'\n", strapptype(app->apptype)); + + return 0; +} + + /* Write out the application specific status lines for the LEARN command. */ gpg_error_t @@ -846,13 +999,14 @@ app_write_learn_status (card_t card, ctrl_t ctrl, unsigned int flags) if (err) return err; - app = card->app; - if (!app) - err = gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); - else if (!app->fnc.learn_status) + if ((err = maybe_switch_app (ctrl, card))) + ; + else if (!card->app->fnc.learn_status) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else { + app = card->app; + /* We do not send CARD and APPTYPE if only keypairinfo is requested. */ if (!(flags &1)) { @@ -891,8 +1045,8 @@ app_readcert (card_t card, ctrl_t ctrl, const char *certid, if (err) return err; - if (!card->ref_count || !card->app) - err = gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if ((err = maybe_switch_app (ctrl, card))) + ; else if (!card->app->fnc.readcert) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else @@ -928,8 +1082,8 @@ app_readkey (card_t card, ctrl_t ctrl, const char *keyid, unsigned int flags, if (err) return err; - if (!card->ref_count || !card->app) - err = gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if ((err = maybe_switch_app (ctrl, card))) + ; else if (!card->app->fnc.readkey) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else @@ -952,8 +1106,8 @@ app_getattr (card_t card, ctrl_t ctrl, const char *name) if (err) return err; - if (!card->ref_count || !card->app) - err = gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if ((err = maybe_switch_app (ctrl, card))) + ; else if (name && !strcmp (name, "CARDTYPE")) { send_status_direct (ctrl, "CARDTYPE", strcardtype (card->cardtype)); @@ -1000,8 +1154,8 @@ app_setattr (card_t card, ctrl_t ctrl, const char *name, if (err) return err; - if (!card->ref_count || !card->app) - err = gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if ((err = maybe_switch_app (ctrl, card))) + ; else if (!card->app->fnc.setattr) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else @@ -1031,8 +1185,8 @@ app_sign (card_t card, ctrl_t ctrl, const char *keyidstr, int hashalgo, if (err) return err; - if (!card->ref_count || !card->app) - err = gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if ((err = maybe_switch_app (ctrl, card))) + ; else if (!card->app->fnc.sign) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else @@ -1067,8 +1221,8 @@ app_auth (card_t card, ctrl_t ctrl, const char *keyidstr, if (err) return err; - if (!card->ref_count || !card->app) - err = gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if ((err = maybe_switch_app (ctrl, card))) + ; else if (!card->app->fnc.auth) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else @@ -1105,8 +1259,8 @@ app_decipher (card_t card, ctrl_t ctrl, const char *keyidstr, if (err) return err; - if (!card->ref_count || !card->app) - err = gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if ((err = maybe_switch_app (ctrl, card))) + ; else if (!card->app->fnc.decipher) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else @@ -1139,8 +1293,8 @@ app_writecert (card_t card, ctrl_t ctrl, if (err) return err; - if (!card->ref_count || !card->app) - err = gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if ((err = maybe_switch_app (ctrl, card))) + ; else if (!card->app->fnc.writecert) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else @@ -1170,8 +1324,8 @@ app_writekey (card_t card, ctrl_t ctrl, if (err) return err; - if (!card->ref_count || !card->app) - err = gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if ((err = maybe_switch_app (ctrl, card))) + ; else if (!card->app->fnc.writekey) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else @@ -1200,8 +1354,8 @@ app_genkey (card_t card, ctrl_t ctrl, const char *keynostr, if (err) return err; - if (!card->ref_count || !card->app) - err = gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if ((err = maybe_switch_app (ctrl, card))) + ; else if (!card->app->fnc.genkey) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else @@ -1255,8 +1409,8 @@ app_change_pin (card_t card, ctrl_t ctrl, const char *chvnostr, if (err) return err; - if (!card->ref_count || !card->app) - err = gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if ((err = maybe_switch_app (ctrl, card))) + ; else if (!card->app->fnc.change_pin) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else @@ -1286,8 +1440,8 @@ app_check_pin (card_t card, ctrl_t ctrl, const char *keyidstr, if (err) return err; - if (!card->ref_count || !card->app) - err = gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if ((err = maybe_switch_app (ctrl, card))) + ; else if (!card->app->fnc.check_pin) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else @@ -1513,8 +1667,12 @@ app_do_with_keygrip (ctrl_t ctrl, int action, const char *keygrip_str) /* FIXME: Add app switching logic. The above code assumes that the * actions can be performend without switching. This needs to be - * checked. For a lookup we also need to reorder the apps so that - * the selected one will be used. */ + * 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) + ctrl->current_apptype = a->apptype; npth_mutex_unlock (&card_list_lock); return c; |