diff options
author | Koni Marti <koni.marti@gmail.com> | 2024-08-06 22:37:28 +0200 |
---|---|---|
committer | Robin Jarry <robin@jarry.cc> | 2024-10-12 00:12:25 +0200 |
commit | 163ea3ec7d2af3bac1afe6489071a8a286f282b8 (patch) | |
tree | 7f018a3910b373d374786eaceaadabe806ce4fbb /lib/pinentry | |
parent | bc8698e1f088cf144a797d2d0b8f875138a79967 (diff) | |
download | aerc-163ea3ec7d2af3bac1afe6489071a8a286f282b8.tar.gz |
aerc: support terminal-based pinentry programs
Support terminal-based pinentry programs. Suspend vaxis before running
the command that can trigger a pinentry call. Provide the proper tty in
the GPG_TTY environment variable (and set a TERM variable if not
provided; this is necessary for pinentry-curses). Finally, resume vaxis.
To enable terminal-based pinentry support, you have to set
[general]
use-terminal-pinentry = true
in your aerc.conf. Any GUI-based pinentry programs will work the same as
before if this option is not set to true.
To test pinentry-tty, add the following to your ~/.gnupg/gpg-agent.conf:
pinentry-program /usr/bin/pinentry-tty
and kill all running gpg-agents:
$ killall gpg-agent
Fixes: https://todo.sr.ht/~rjarry/aerc/202
Changelog-fixed: Terminal-based pinentry programs
(e.g. `pinentry-curses`) now work properly.
Signed-off-by: Koni Marti <koni.marti@gmail.com>
Acked-by: Robin Jarry <robin@jarry.cc>
Diffstat (limited to 'lib/pinentry')
-rw-r--r-- | lib/pinentry/pinentry.go | 71 | ||||
-rw-r--r-- | lib/pinentry/ttyname.go | 45 |
2 files changed, 116 insertions, 0 deletions
diff --git a/lib/pinentry/pinentry.go b/lib/pinentry/pinentry.go new file mode 100644 index 00000000..51b54920 --- /dev/null +++ b/lib/pinentry/pinentry.go @@ -0,0 +1,71 @@ +package pinentry + +import ( + "fmt" + "os" + "os/exec" + "strings" + "sync/atomic" + + "git.sr.ht/~rjarry/aerc/config" + "git.sr.ht/~rjarry/aerc/lib/log" + "git.sr.ht/~rjarry/aerc/lib/ui" +) + +var pinentryMode int32 = 0 + +func Enable() { + if !config.General.UsePinentry { + return + } + if atomic.SwapInt32(&pinentryMode, 1) == 1 { + // cannot enter pinentry mode twice + return + } + ui.SuspendScreen() +} + +func Disable() { + if atomic.SwapInt32(&pinentryMode, 0) == 0 { + // not in pinentry mode + return + } + ui.ResumeScreen() +} + +func SetCmdEnv(cmd *exec.Cmd) { + if cmd == nil || atomic.LoadInt32(&pinentryMode) == 0 { + return + } + + env := cmd.Env + if env == nil { + env = os.Environ() + } + + hasTerm := false + hasGPGTTY := false + for _, e := range env { + switch { + case strings.HasPrefix(strings.ToUpper(e), "TERM="): + log.Debugf("pinentry: use %v", e) + hasTerm = true + case strings.HasPrefix(strings.ToUpper(e), "GPG_TTY="): + log.Debugf("pinentry: use %v", e) + hasGPGTTY = true + } + } + + if !hasTerm { + env = append(env, "TERM=xterm-256color") + log.Debugf("pinentry: set TERM=xterm-256color") + } + + if !hasGPGTTY { + tty := ttyname() + env = append(env, fmt.Sprintf("GPG_TTY=%s", tty)) + log.Debugf("pinentry: set GPG_TTY=%s", tty) + } + + cmd.Env = env +} diff --git a/lib/pinentry/ttyname.go b/lib/pinentry/ttyname.go new file mode 100644 index 00000000..053cff74 --- /dev/null +++ b/lib/pinentry/ttyname.go @@ -0,0 +1,45 @@ +package pinentry + +import ( + "fmt" + "os" + "strings" + + "git.sr.ht/~rjarry/aerc/lib/log" +) + +var missingGPGTTYmsg = ` +You need to set GPG_TTY manually before starting aerc. Add the following to your +.bashrc or whatever initialization file is used for shell invocations: + + GPG_TTY=$(tty) + export GPG_TTY + +Further information can be found here: +https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html +` + +// ttyname returns current name of the pty. This is necessary in order to tell +// pinentry where to ask for the passphrase. +// +// If there is a GPG_TTY environment variable set, use this one. Otherwise, try +// readline() on /proc/<pid>/fd/0. +// +// If both approaches fail, the user's only option is to set GPG_TTY manually. +// +// If tty name could not be determined, an empty string is returned. +func ttyname() string { + if s := os.Getenv("GPG_TTY"); s != "" { + return s + } + + // try readlink or else show missing GPG_TTY warning msg + tty, err := os.Readlink(fmt.Sprintf("/proc/%d/fd/0", os.Getpid())) + if err != nil { + log.Debugf("readlink: '%s' with err: %v", tty, err) + log.Warnf(missingGPGTTYmsg) + return "" + } + + return strings.TrimSpace(tty) +} |