Skip to content

pinentry-tty not killed when rbw client receives SIGINT (Ctrl+C) #352

Description

@C-Furber

Summary

When rbw is configured to use pinentry-tty and the user presses Ctrl+C during a passphrase prompt, pinentry-tty is not killed and continues to hold /dev/tty, competing with the shell for keystrokes until the retry limit is exhausted.

Environment

  • rbw version: 1.15.0
  • OS: Arch Linux, Hyprland
  • pinentry-tty version: pinentry-tty (pinentry) 1.3.2
  • rbw config: pinentry = "pinentry-tty"

Steps to reproduce

  1. Configure rbw to use pinentry-tty: rbw config set pinentry pinentry-tty
  2. Run rbw get <any entry>
  3. When the Master Password: prompt appears, press Ctrl+C

Expected behavior

Ctrl+C should cancel the pinentry prompt and return cleanly to the shell, as it does when using gpg-agent 2.4+ with the same pinentry-tty binary.

Actual behavior

pinentry-tty continues running as an orphan process attached to /dev/tty. The shell reclaims the foreground but keystrokes are raced between the shell and pinentry. Subsequent keystrokes register as password attempts (visible in the "attempt 2/3", "attempt 3/3" output), and only after the retry limit is exhausted does pinentry exit.

ps aux during the prompt confirms pinentry-tty has no controlling terminal (TTY column shows ?), meaning it is fully detached from the foreground process group and unreachable via terminal signals:

cfurber  71363  0.0  0.0  21872  7296 ?  SL  15:25  0:00 pinentry-tty --timeout 0 --ttyname /dev/pts/0 --display :0

Root cause

When the user presses Ctrl+C, SIGINT is delivered to the foreground process group. The rbw client process is in that group and receives the signal, but rbw-agent is a background daemon in a different process group and does not. Since rbw-agent spawned pinentry-tty, and rbw-agent never receives or handles the SIGINT, it never sends a cancel/SIGINT to the pinentry child process.

This is confirmed by comparison with gpg-agent 2.4+, which explicitly propagates cancellation to pinentry when its client is interrupted (fix: GnuPG T4585 / T2011). The same pinentry-tty binary works correctly with gpg-agent — Ctrl+C exits cleanly:

$ echo "test" | gpg --symmetric -
Enter passphrase
Passphrase:
gpg: signal Interrupt caught ... exiting

Workaround

Ctrl+D cancels the prompt correctly since it sends EOF directly on the tty fd rather than via process group signal delivery.

A shell wrapper around the rbw binary also works. The key is keeping rbw in the foreground (no &) so it stays in the foreground process group and tty context is preserved. The trap then explicitly kills pinentry when SIGINT arrives:

#!/bin/bash
trap "pkill -x pinentry-tty 2>/dev/null" INT TERM
/usr/bin/rbw "$@"

Suggested fix

rbw-agent should monitor for client disconnection/cancellation and send SIGINT (or the Assuan CANCEL command) to any in-progress pinentry process when the client that initiated the request is interrupted. This is the behavior implemented in gpg-agent 2.4 (GnuPG commits addressing T2011 and T4585).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions