John Heatherington
2020-Oct-06 01:40 UTC
Accessing SSH key path using SSH_ASKPASS and passwordstore
Hello, With the introduction of SSH_ASKPASS_REQUIRE in version 8.4, I've set up a script for SSH_ASKPASS to query my local passwordstore (https://www.passwordstore.org/) vault to retrieve the password for a given key. This works for ssh-add as well as ssh (configured with AddKeysToAgent set to 'yes'). My workflow effectively transforms into entering the password for the GPG key used to encrypt my vault for any given key. It works especially well now that I don't have to alter DISPLAY and confuse gpg's pin input inference. Thanks for that enhancement! The tricky part here is the way I have to figure out which key is being unlocked. I was initially only working with ssh-add, so it seemed trivial to just deal with the input to a script acting as a wrapper and feed that to the askpass script as an environment variable. When I realized I could also take advantage of AddKeysToAgent and simply call ssh, I had to change my strategy given that the path to the key being unlocked does not appear to get passed separately to my script; rather it's just given a prompt that happens to contain the path to the key. Using this knowledge I just have my script infer the path using sed. This strategy works, but I noticed that when you call ssh (with AddKeysToAgent set to 'yes') vs ssh-add, the prompts are slightly different: $ ssh user at host Enter passphrase for key '/home/user/.ssh/id_ed25519_somekey': $ ssh-add /home/user/.ssh/id_ed25519_somekey Enter passphrase for /home/user/.ssh/id_ed25519_somekey: Notice the single quotes around the path in the prompt when calling ssh. I'm not sure if that's a bug with regard to consistency. I was able to modify the regex to account for this difference, but overall I wondered if this couldn't be improved. For my usage, it would be great to receive the path to the key as another askpass argument. Alternatively I could also envision accessing this information as an environment variable. I understand that my use-case may diverge too greatly from the original intentions for this component, but I thought I'd ask anyway in case I'm either doing something wrong or missing out on another feature. What I have currently works, but I fear it leaves me prone to breaking changes later on. I've included my askpass script below. Also just to note, I'm running on Arch, but I've confirmed these behaviors in the GitHub repo. Thanks, John pass-askpass.sh --- #!/usr/bin/env bash # This translates "Enter passphrase for /home/user/.ssh/id_ed25519_somekey:" to "id_ed25519_somekey" # It also accounts for the case where the path is surrounded by single quotes in the prompt key_filename="$(echo "$1" | sed -e "s/^.*\/\(.*\)'*:.*$/\1/")" # Assume we store all our keys in one folder in pass, and they are all uniquely identifiable # This will result in a prompt for my GPG key password to retrieve the SSH key password pass "${PASS_SSH_FOLDER:-SSH}/${key_filename}" | head -n1
Damien Miller
2020-Oct-06 02:18 UTC
Accessing SSH key path using SSH_ASKPASS and passwordstore
On Mon, 5 Oct 2020, John Heatherington wrote:> Hello, > > With the introduction of SSH_ASKPASS_REQUIRE in version 8.4, I've set > up a script for SSH_ASKPASS to query my local passwordstore > (https://www.passwordstore.org/) vault to retrieve the password for a > given key. This works for ssh-add as well as ssh (configured with > AddKeysToAgent set to 'yes'). My workflow effectively transforms into > entering the password for the GPG key used to encrypt my vault for any > given key. It works especially well now that I don't have to alter > DISPLAY and confuse gpg's pin input inference. Thanks for that > enhancement! > > The tricky part here is the way I have to figure out which key is > being unlocked. I was initially only working with ssh-add, so it > seemed trivial to just deal with the input to a script acting as a > wrapper and feed that to the askpass script as an environment > variable. When I realized I could also take advantage of > AddKeysToAgent and simply call ssh, I had to change my strategy given > that the path to the key being unlocked does not appear to get passed > separately to my script; rather it's just given a prompt that happens > to contain the path to the key. Using this knowledge I just have my > script infer the path using sed. > > This strategy works, but I noticed that when you call ssh (with > AddKeysToAgent set to 'yes') vs ssh-add, the prompts are slightly > different: > > $ ssh user at host > Enter passphrase for key '/home/user/.ssh/id_ed25519_somekey': > > $ ssh-add /home/user/.ssh/id_ed25519_somekey > Enter passphrase for /home/user/.ssh/id_ed25519_somekey: > > Notice the single quotes around the path in the prompt when calling > ssh. I'm not sure if that's a bug with regard to consistency. I was > able to modify the regex to account for this difference, but overall I > wondered if this couldn't be improved. For my usage, it would be great > to receive the path to the key as another askpass argument. > Alternatively I could also envision accessing this information as an > environment variable.Unfortunately the askpass convention is pretty old and baked-in to too many places to change radically. OTOH we could certainly harmonise the prompts, e.g. diff --git a/ssh-add.c b/ssh-add.c index 0ce989f..2a0b207 100644 --- a/ssh-add.c +++ b/ssh-add.c @@ -228,8 +228,7 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag, const char *skprovider) { struct sshkey *private, *cert; - char *comment = NULL; - char msg[1024], *certpath = NULL; + char *comment = NULL, *msg = NULL, *certpath = NULL; int r, fd, ret = -1; size_t i; u_int32_t left; @@ -282,7 +281,8 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag, if (private == NULL) { /* clear passphrase since it did not work */ clear_pass(); - snprintf(msg, sizeof msg, "Enter passphrase for %s%s: ", + free(msg); + xasprintf(&msg, "Enter passphrase for key '%s%s': ", filename, confirm ? " (will confirm each use)" : ""); for (;;) { pass = read_passphrase(msg, RP_ALLOW_STDIN); @@ -298,11 +298,13 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag, fail_load: clear_pass(); sshbuf_free(keyblob); + free(msg); return -1; } clear_pass(); - snprintf(msg, sizeof msg, - "Bad passphrase, try again for %s%s: ", filename, + free(msg); + xasprintf(&msg, + "Bad passphrase, try again for '%s%s': ", filename, confirm ? " (will confirm each use)" : ""); } } @@ -435,6 +437,7 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag, free(certpath); free(comment); sshkey_free(private); + free(msg); return ret; } diff --git a/sshconnect2.c b/sshconnect2.c index 2aca328..149eb11 100644 --- a/sshconnect2.c +++ b/sshconnect2.c @@ -1503,7 +1503,7 @@ static struct sshkey * load_identity_file(Identity *id) { struct sshkey *private = NULL; - char prompt[300], *passphrase, *comment; + char *prompt = NULL, *passphrase, *comment; int r, quit = 0, i; struct stat st; @@ -1512,8 +1512,7 @@ load_identity_file(Identity *id) id->filename, strerror(errno)); return NULL; } - snprintf(prompt, sizeof prompt, - "Enter passphrase for key '%.100s': ", id->filename); + xasprintf(&prompt, "Enter passphrase for key '%s': ", id->filename); for (i = 0; i <= options.number_of_password_prompts; i++) { if (i == 0) passphrase = ""; @@ -1568,6 +1567,7 @@ load_identity_file(Identity *id) if (private != NULL || quit) break; } + free(prompt); return private; }
Thorsten Glaser
2020-Oct-06 14:07 UTC
Accessing SSH key path using SSH_ASKPASS and passwordstore
On Tue, 6 Oct 2020, Damien Miller wrote:> Unfortunately the askpass convention is pretty old and baked-in to > too many places to change radically. OTOH we could certainly harmoniseIn kwalletcli, I just use the full prompt string as ?key?. (This does something like the thing the OP described, except for storing/retrieving the passwords from the KDE 3/4/5 wallet.) bye, //mirabilos -- tarent solutions GmbH Rochusstra?e 2-4, D-53123 Bonn ? http://www.tarent.de/ Tel: +49 228 54881-393 ? Fax: +49 228 54881-235 HRB 5168 (AG Bonn) ? USt-ID (VAT): DE122264941 Gesch?ftsf?hrer: Dr. Stefan Barth, Kai Ebenrett, Boris Esser, Alexander Steeg ************************************************* Mit unserem Consulting bieten wir Unternehmen ma?geschneiderte Angebote in Form von Beratung, Trainings sowie Workshops in den Bereichen Softwaretechnologie, IT Strategie und Architektur, Innovation und Umsetzung sowie Agile Organisation. Besuchen Sie uns auf https://www.tarent.de/consulting . Wir freuen uns auf Ihren Kontakt. *************************************************