Handle non-GUI sessions (SSH, tmux)

When there is a "global" SSH agent running in a GUI session, attempting
to use it from a remote SSH session may not work well.  For keys that
need a passphrase or confirmation, the prompt will appear on the
graphical display, rather than the remote client's terminal, essentially
preventing the remote client from using those keys.

To resolve this, we need to use a separate SSH agent for remote clients.
At login, the remote client should spawn its own agent, e.g. with `eval
$(ssh-agent)`.  Then, the ssh-askpass script will be able to spawn
`pinentry` attached to theh remote client's terminal.

In the specific case of running `tmux` in a remote SSH session, things
get a bit more complicated.  Since `tmux` has control of the SSH
client's terminal, attaching `pinentry` to it wreaks havoc and makes
both programs unusable.  Thus, we need to spawn `pinentry` _inside_
`tmux`.  Since we do not know which TTY the invoker is using, we need to
spawn a new window for the `pinentry` prompt.  This of course detaches
the `pinentry` process from our stdin/stdout, so we have to used named
pipes to communicate with it.
master
Dustin C. Hatch 2024-04-19 11:03:02 -05:00
parent 4d101cf27b
commit f03d3a8561
1 changed files with 29 additions and 3 deletions

View File

@ -1,8 +1,34 @@
#!/bin/sh #!/bin/sh
# vim: set sw=4 ts=4 sts=4 et : # vim: set sw=4 ts=4 sts=4 et :
pinentry() {
if [ -n "${TMUX}" ] && [ -z "${DISPLAY}" ]; then
# We're running in a TMUX pane, launch a new window to handle
# passphrase/confirmation prompt
unset t1 t2
mkfifo "${t1:=$(mktemp -u)}"
mkfifo "${t2:=$(mktemp -u)}"
tmux new-window sh -c "exec pinentry -T \$(tty) < '$t1' > '$t2'"
cat > "$t1"
cat "$t2"
rm -f "$t1" "$t2"
return
elif [ -n "${SSH_TTY}" ]; then
# We're in an SSH session, so prompt for the passphrase on the
# SSH client terminal
set -- -T "${SSH_TTY}" "$@"
elif [ -t 0 ]; then
set -- -T $(tty) "$@"
elif [ -t 1 ]; then
set -- -T $(tty <&1) "$@"
elif [ -t 2 ]; then
set -- -T $(tty <&2) "$@"
fi
command pinentry "$@"
}
prompt_confirm() { prompt_confirm() {
result=$(pinentry -T $(tty) -g <<EOF | grep -E '^(D|ERR)' result=$(pinentry -g <<EOF | grep -E '^(D|ERR)'
SETTITLE ssh-askpass SETTITLE ssh-askpass
SETDESC $1 SETDESC $1
SETOK Yes SETOK Yes
@ -26,7 +52,7 @@ EOF
} }
prompt_notify() { prompt_notify() {
pinentry -T $(tty) -g <<EOF || : pinentry -g <<EOF || :
SETTITLE ssh-askpass SETTITLE ssh-askpass
SETDESC $1 SETDESC $1
SETTIMEOUT 3 SETTIMEOUT 3
@ -35,7 +61,7 @@ EOF
} }
prompt_passphrase() { prompt_passphrase() {
result=$(pinentry -T $(tty) -g <<EOF | grep -E '^(D|ERR)' result=$(pinentry -g <<EOF | grep -E '^(D|ERR)'
SETTITLE ssh-askpass SETTITLE ssh-askpass
SETDESC $1 SETDESC $1
SETPROMPT Passphrase: SETPROMPT Passphrase: