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. *************************************************