Open mchack-work opened 7 months ago
Ah, there are two usecases: When the agent is started as a service, it seems a bit tricky to ask the user for a secret. Requiring some kind of graphical environment might be reasonable, even though I don't get how the daemon will connect to it. Another option might be to require an explicit Unlock call (ssh-add -X) to the agent before first use.
In my case, I'm starting the agent from a terminal, and I was trying tkey-ssh-agent -p --uss (to check that I do get a different pubkey with a uss, as expected). In this case, the agent never daemonizes. And it would make sense to me to have tkey-ssh-agent --uss always ask for the user secret before it daemonizes and detaches from the terminal.
I'm also a little confused because when I start it with tkey-ssh-agent -a (to make it a "daemon" that serves requests), it stays running in the foreground, and it is terminated on ^C, so I don't see how it detaches from the terminal, and I also see nothing like that after a quick look in the source.
The agent talks to the pinentry program with the Assuan protocol:
https://www.gnupg.org/documentation/manuals/assuan/
I believe it simply starts a child program (pinentry) and talks the Assuan protocol on its stdout/stdin.
On many Linux distributions this pinentry program is itself a script and tries to start yet another program.
I can't reproduce your exact problem on my Arch system if I remove my gpg-agent config. /usr/bin/pinentry (a script) is started, then it starts pinentry-curses in the terminal running tkey-ssh-agent in the foreground. I'll try on a Debian system later.
tkey-ssh-agent never daemonizes itself. It uses systemd or similar things to do that, like most modern services.
Since the USS is entered every time you insert a new TKey it doesn't make much sense to ask for USS before daemonizing. You need to enter USS again and again every time you insert a TKey and try to load a device app, so you will have to have some way of entering it even after daemonizing.
Ok, then it's not just me that finds the errors about not a tty puzzling. For reference, these were the errors I saw:
$ tkey-ssh-agent -p --uss
Auto-detected serial port /dev/ttyACM0
Connecting to TKey on serial port /dev/ttyACM0
TKey is in firmware mode.
Notify message "Could not show USS prompt: pinentry: unexpected response: \"S ERROR curses.isatty 83918950 \"" failed: beeep: The name org.freedesktop.Notifications was not provided by any .service files; exec: "kdialog": executable file not found in $PATH
Failed to load app: Failed to get USS: pinentry GetPin: pinentry: unexpected response: "S ERROR curses.isatty 83918950 "
Connect failed
My "pinentry" program is a symlink (via /etc/alternatives) to pinentry-curses, debian package version 1.2.1-1. As far as I understand, it ought to work to spawn that program and talk the "assuan" protocol to it. The line "S ERROR..." looks like it's almost an assuan protocol response, but from the docs an error response should be "ERR ...", not "S ERROR ...". And the actual error is odd. Maybe the pinentry program I have is just broken, or implementing some older (undocumented?) variant of assuan.
I wasn't thinking of the case with a long lived tkey-ssh-agent, while user removes and inserts tkeys. I take it there are many different use cases, but for me, one reasonable behavior would be that if tkey-ssh-agent is configured to pass a user secret when loading the device app, then a newly inserted tkey (in provision mode) could be considered locked, and requiring unlocking (ssh-add -X). When tkey-ssh-agent gets the unlock request, it can use the provided password as the uss and load the device app. But I haven't looked up in detail how that request works in the ssh-agent protocol.
To get some more details of what's going on, I'm trying this crude hack, pinentry-bug.sh:
#! /bin/bash
exec 2> pinentry.err
coproc PINENTRY { pinentry-curses; }
echo 0: ${PINENTRY[0]} 1: ${PINENTRY[1]} >&2
# Assume pinentry starts ("OK Pleased to meet you")
# followed by single-line req and response.
while read -r -u ${PINENTRY[0]} resp ; do
echo resp: "${resp}" >&2
echo "${resp}"
read -r req || break
echo req: "${req}" >&2
echo ${req} >& ${PINENTRY[1]}
done
# Read any queued up responses
while read -r -t + -u ${PINENTRY[0]} resp ; do
echo resp: "${resp}" >&2
echo "${resp}"
done
Running tkey-ssh-agent -p --uss --pinentry ./pinentry-bug.sh
then logs this conversation in pinentry.err:
0: 63 1: 60
resp: OK Pleased to meet you
req: SETDESC tkey-ssh-agent needs a User Supplied Secret%0A(USS) for your TKey with number:%0A01337:2:1:0000011b
resp: OK
req: SETPROMPT User Supplied Secret
resp: OK
req: SETTITLE tkey-ssh-agent
resp: OK
req: GETPIN
resp: S ERROR curses.isatty 83918950
req: BYE
resp: ERR 83918950 Inappropriate ioctl for device <Pinentry>
resp: ERR 83918950 Inappropriate ioctl for device <Pinentry>
resp: ERR 83918950 Inappropriate ioctl for device <Pinentry>
resp: ERR 83918950 Inappropriate ioctl for device <Pinentry>
resp: ERR 83918950 Inappropriate ioctl for device <Pinentry>
resp: ERR 83918950 Inappropriate ioctl for device <Pinentry>
resp: ERR 83918950 Inappropriate ioctl for device <Pinentry>
resp: ERR 83918950 Inappropriate ioctl for device <Pinentry>
resp: ERR 83918950 Inappropriate ioctl for device <Pinentry>
resp: ERR 83918950 Inappropriate ioctl for device <Pinentry>
resp: ERR 83918950 Inappropriate ioctl for device <Pinentry>
resp: ERR 83918950 Inappropriate ioctl for device <Pinentry>
resp: ERR 83918950 Inappropriate ioctl for device <Pinentry>
resp: ERR 83918950 Inappropriate ioctl for device <Pinentry>
resp: ERR 83918950 Inappropriate ioctl for device <Pinentry>
resp: ERR 83918950 Inappropriate ioctl for device <Pinentry>
resp: ERR 83918950 Inappropriate ioctl for device <Pinentry>
./pinentry-bug.sh: line 20: read: resp: invalid file descriptor specification
Correction: The " -t +" arg to one of the read calls above was a typo, intended "-t 0".
And it seems that the problem is that pinentry-curses by default tries to use stdin as the terminal to display its curses ui, which is a bit silly since that's likely going to be a pipe to the program that needs the pin. Maybe the pinentry script you mentioned works around that?
Using this script as the pinentry program actually works:
#! /bin/bash
exec pinentry-curses -T /dev/tty
(in my use case, where the tkey-ssh-agent and its child processes do have a controlling tty).
Annoying, and not clear to me what tkey-ssh-agent can do about that, besides documenting more of the pinentry details, and have the error message point to those docs.
Great investigation @niels-moller, thanks!
I reproduced the issue you have. I get the same error when running an Ubuntu VM, also verified the script you wrote works on that very same VM. What is interesting is that pinentry seems to behave a bit different on different platforms. When running macos and using "pinentry", which points to the same homepage as pinentry-curses on Ubuntu (https://www.gnupg.org/related_software/pinentry/) it works fine an provides a pinentry dialog in the same terminal as tkey-ssh-agent -p --uss was called from.
I have updated the original issue text to what we want to do, that is update the documentation about our assumptions, and maybe remove the thing with gpg-agent.conf.
I noted that the minimalist pinentry-bemenu
doesn't work because it doesn't handle the SETTITLE command. Maybe we can drop that? Using --pinentry pinentry-bemenu
works fine if I comment out the setting of the title.
Users of straight WM's and compositors without desktop environments then doesn't need neither pinentry-gnome3, dbus, or gcr-prompter.
Also, let me quote the code:
// Title is not displayed by all pinentry programs (or
// displayed obscurely in window title).
pinentry.WithTitle(progname),
It's likely that the corresponding X11 program pinentry-dmenu
also works, but I don't know if that grabs the X server, so might be insecure.
I noted that the minimalist
pinentry-bemenu
doesn't work because it doesn't handle the SETTITLE command. Maybe we can drop that? Using--pinentry pinentry-bemenu
works fine if I comment out the setting of the title.
I think that a pinentry server implementation should be fixed to handle common commands (possibly doing nothing, if it doesn't make sense). Or the pinentry client should accept an error result from a command which is known to not be broadly implemented (if it isn't something very important).
Now it happens that pinentry-bemenu 0.13.0 does support SETTITLE, perhaps you are using an older version?
Nice! Thank you. Yes, on 0.12.0 here.
We need to document the assumptions behind running tkey-ssh-agent, and especially pinentry, on Linux systems better. The assumptions are:
A working graphical pinentry typically means something like
pinentry-gnome3
orpinentry-qt
on most systems.People who use GNOME or KDE or another full desktop are probably all set. People who use more lightweight systems with a minimal wm or a minimal Wayland compositor need to set things up themselves.
pinentry-gnome3 and Desktop Notifications typically also have dependencies on a working dbus session. If pinentry-gnome3 can't connect to the dbus session you get the dreaded:
It wants to speak to gcr-prompter, which listens on the dbus.
When running, for instance, Sway or River this means you have to do something like
in your config to get both pinentry-gnome3 and screen sharing working.
Note well: The agent tries to look in the user's
gpg-agent.conf
for what they want to use as a pinentry and tries to use that. This can be really confusing if they have something there that's old and isn't even installed on their system. Perhaps remove that functionality altogether? If they really want control they can set it with the--pinentry
flag.To check if you have a working graphical pinentry, first install something like pinentry-gnome3, then:
You should get a graphical window asking for something. I typed in "foo" and hit return.
Theoretically something other than pinentry-gnome3 or pinentry-qt should work fine, but I haven't tested for instance pinentry-gtk2. Sadly, the minimalist pinentry-bemenu doesn't work. However, pinentry-emacs does! See the different implementations here:
git://git.gnupg.org/pinentry.git
Note well: If you just want to run the agent in the foreground and want it to ask for the USS in the same terminal, just call it with pinentry-curses: