From 8fb0d5e3c775f40e321689b35431d81425406237 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 9 Nov 2020 13:04:38 +0100 Subject: [PATCH] card: Run factory-reset in locked stated. * scd/command.c (reset_notify): Add option --keep-lock. (do_reset): Add arg keep_lock. (cmd_lock): Send progress status. * g10/call-agent.c (agent_scd_apdu): Add more pseudo APDUs. * g10/card-util.c (send_apdu): Ditto. (factory_reset): Use lock commands. -- This is required so that for example Kleopatra does not detect the RESET and issues a SERIALNO of its own, thus conflicting with our SERIALNO undefined. Signed-off-by: Werner Koch --- doc/DETAILS | 1 + g10/call-agent.c | 30 ++++++++++++++++++++++++++++-- g10/card-util.c | 24 +++++++++++++++++++----- scd/command.c | 20 ++++++++++---------- 4 files changed, 58 insertions(+), 17 deletions(-) diff --git a/doc/DETAILS b/doc/DETAILS index 6eefb8b4f..44bee32ad 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -1130,6 +1130,7 @@ pkd:0:1024:B665B1435F4C2 .... FF26ABB: - learncard :: Send by the agent and gpgsm while learing the data of a smartcard. - card_busy :: A smartcard is still working + - scd_locked :: Waiting for other clients to unlock the scdaemon When refers to a file path, it may be truncated. diff --git a/g10/call-agent.c b/g10/call-agent.c index c4d146392..01f59aca8 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -975,8 +975,14 @@ agent_scd_keypairinfo (ctrl_t ctrl, const char *keyref, keypair_info_t *r_list) /* Send an APDU to the current card. On success the status word is * stored at R_SW. With HEXAPDU being NULL only a RESET command is - * send to scd. With HEXAPDU being the string "undefined" the command - * "SERIALNO undefined" is send to scd. + * send to scd. HEXAPDU may also be one of these special strings: + * + * "undefined" :: Send the command "SCD SERIALNO undefined" + * "lock" :: Send the command "SCD LOCK --wait" + * "trylock" :: Send the command "SCD LOCK" + * "unlock" :: Send the command "SCD UNLOCK" + * "reset-keep-lock" :: Send the command "SCD RESET --keep-lock" + * * Used by: * card-util.c */ @@ -997,6 +1003,26 @@ agent_scd_apdu (const char *hexapdu, unsigned int *r_sw) NULL, NULL, NULL, NULL, NULL, NULL); } + else if (!strcmp (hexapdu, "reset-keep-lock")) + { + err = assuan_transact (agent_ctx, "SCD RESET --keep-lock", + NULL, NULL, NULL, NULL, NULL, NULL); + } + else if (!strcmp (hexapdu, "lock")) + { + err = assuan_transact (agent_ctx, "SCD LOCK --wait", + NULL, NULL, NULL, NULL, NULL, NULL); + } + else if (!strcmp (hexapdu, "trylock")) + { + err = assuan_transact (agent_ctx, "SCD LOCK", + NULL, NULL, NULL, NULL, NULL, NULL); + } + else if (!strcmp (hexapdu, "unlock")) + { + err = assuan_transact (agent_ctx, "SCD UNLOCK", + NULL, NULL, NULL, NULL, NULL, NULL); + } else if (!strcmp (hexapdu, "undefined")) { err = assuan_transact (agent_ctx, "SCD SERIALNO undefined", diff --git a/g10/card-util.c b/g10/card-util.c index d43081588..74d49aaf2 100644 --- a/g10/card-util.c +++ b/g10/card-util.c @@ -1859,8 +1859,13 @@ send_apdu (const char *hexapdu, const char *desc, unsigned int ignore) if (err) tty_printf ("sending card command %s failed: %s\n", desc, gpg_strerror (err)); - else if (!hexapdu || !strcmp (hexapdu, "undefined")) - ; + else if (!hexapdu + || !strcmp (hexapdu, "undefined") + || !strcmp (hexapdu, "reset-keep-lock") + || !strcmp (hexapdu, "lock") + || !strcmp (hexapdu, "trylock") + || !strcmp (hexapdu, "unlock")) + ; /* Ignore pseudo APDUs. */ else if (ignore == 0xffff) ; /* Ignore all status words. */ else if (sw != 0x9000) @@ -1889,6 +1894,7 @@ factory_reset (void) char *answer = NULL; int termstate = 0; int i; + int locked = 0; /* The code below basically does the same what this gpg-connect-agent script does: @@ -1950,8 +1956,14 @@ factory_reset (void) goto leave; /* We need to select a card application before we can send APDUs - to the card without scdaemon doing anything on its own. */ - err = send_apdu (NULL, "RESET", 0); + to the card without scdaemon doing anything on its own. We + then lock the connection so that other tools (e.g. Kleopatra) + don't try a new select. */ + err = send_apdu ("lock", "locking connection ", 0); + if (err) + goto leave; + locked = 1; + err = send_apdu ("reset-keep-lock", "reset", 0); if (err) goto leave; err = send_apdu ("undefined", "dummy select ", 0); @@ -1993,7 +2005,7 @@ factory_reset (void) goto leave; /* Finally we reset the card reader once more. */ - err = send_apdu (NULL, "RESET", 0); + err = send_apdu ("reset-keep-lock", "reset", 0); /* Then, connect the card again. */ if (!err) @@ -2005,6 +2017,8 @@ factory_reset (void) } leave: + if (locked) + send_apdu ("unlock", "unlocking connection ", 0); xfree (answer); agent_release_card_info (&info); } diff --git a/scd/command.c b/scd/command.c index b8f7bccab..0f31218e8 100644 --- a/scd/command.c +++ b/scd/command.c @@ -149,9 +149,10 @@ hex_to_buffer (const char *string, size_t *r_length) /* Reset the card and free the application context. With SEND_RESET set to true actually send a RESET to the reader; this is the normal - way of calling the function. */ + way of calling the function. If KEEP_LOCK is set and the session + is locked that lock wil not be released. */ static void -do_reset (ctrl_t ctrl, int send_reset) +do_reset (ctrl_t ctrl, int send_reset, int keep_lock) { card_t card = ctrl->card_ctx; @@ -159,7 +160,7 @@ do_reset (ctrl_t ctrl, int send_reset) card_reset (card, ctrl, IS_LOCKED (ctrl)? 0: send_reset); /* If we hold a lock, unlock now. */ - if (locked_session && ctrl->server_local == locked_session) + if (!keep_lock && locked_session && ctrl->server_local == locked_session) { locked_session = NULL; log_info ("implicitly unlocking due to RESET\n"); @@ -173,9 +174,7 @@ reset_notify (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); - (void) line; - - do_reset (ctrl, 1); + do_reset (ctrl, 1, has_option (line, "--keep-lock")); return 0; } @@ -1656,9 +1655,10 @@ cmd_lock (assuan_context_t ctx, char *line) npth_sleep (1); /* Better implement an event mechanism. However, for card operations this should be sufficient. */ - /* FIXME: Need to check that the connection is still alive. - This can be done by issuing status messages. */ - goto retry; + /* Send a progress so that we can detect a connection loss. */ + rc = send_status_printf (ctrl, "PROGRESS", "scd_locked . 0 0"); + if (!rc) + goto retry; } #endif /*USE_NPTH*/ @@ -2372,7 +2372,7 @@ scd_command_handler (ctrl_t ctrl, int fd) } /* Cleanup. We don't send an explicit reset to the card. */ - do_reset (ctrl, 0); + do_reset (ctrl, 0, 0); /* Release the server object. */ if (session_list == ctrl->server_local)