Hi all,
I've been debugging using gpg in flatpak apps. Particularly, I use
Evolution as a flatpak for email. The issue I am having is that gpg
private key operations only work if the card pin or key passphrase is
already in the agent cache. Pinentry does not work when triggered by a
gpg operation from a flatpak sandboxed app. I can only decrypt email
when I've done a gpg operation outside the sandbox to get the pin
entered.
The following debugging/demonstration notes are with Kleopatra for ease
of testing gpg operations, but I observe the same behavior and results
with Evolution. The following are notes as I verified the behavior in a
fresh VM (Fedora 41 Workstation) environment.
System GPG version:
gpg (GnuPG) 2.4.5
libgcrypt 1.11.0-unknown
Flatpak sandbox GPG version:
gpg (GnuPG) 2.5.1
libgcrypt 1.11.0
Agent configuration:
$ cat ~/.gnupg/gpg-agent.conf
pinentry-program /usr/bin/pinentry-gnome3
debug-level basic
With a freshly started agent process, I open Kleopatra and attempt to
sign some text in the Notepad view. This fails.
The error message in Kleopatra's diagnostics:
gpg: WARNING: server 'gpg-agent' is older than us (2.4.5 < 2.5.1)
gpg: Note: Outdated servers may lack important security fixes.
gpg: Note: Use the command "gpgconf --kill all" to restart them.
gpg: signing failed: pinentry error
gpg: -&48: sign+encrypt failed: pinentry error
Looking at the output of gpg-agent, I see this line:
DBG: chan_6 <- OPTION
putenv=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/flatpak/bus
I think this is the smoking gun. If I understand correctly, Kleopatra
is telling the agent where it thinks the DBUS socket is and the agent
attempts to use this value when launching pinentry. This of course
fails, as there is no dbus socket at that location from gpg-agent's
perspective. This value is what Flatpak sets up when it starts the
sandbox, from Kleopatra's perspective this the correct value. For
gpg-agent, the correct value is of course
`DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus`.
I now do a GPG operation outside the sandbox so that the passphrase gets
prompted and cached. Once the passphrase is cached, I can switch back
to Kleopatra, and the signing operation (or any other using the private
key) succeeds. If I restart the agent, Kleopatra will fail to
sign/decrypt again until I get the passphrase cached.
Another interesting demonstration is to modify DBUS_SESSION_BUS_ADDRESS
before starting Kleopatra from a shell inside the sandbox.
If I first open a shell in the sandbox and launch Kleopatra, I do not
get a pinentry prompt and operation fails. Same as above.
$ flatpak run --command=bash org.kde.kleopatra
[π¦ org.kde.kleopatra ~]$ echo $DBUS_SESSION_BUS_ADDRESS
unix:path=/run/flatpak/bus
[π¦ org.kde.kleopatra ~]$ kleopatra
If I modify DBUS_SESSION_BUS_ADDRESS in the sandbox shell before
launching Kleopatra as per below, everything works as expected. I get a
pinentry prompt and private key operations are successful.
Interestingly `$XDG_RUNTIME_DIR/bus` is symlinked to `/run/flatpak/bus`,
but flatpak must have it's reasons for using
`DBUS_SESSION_BUS_ADDRESS=/run/flatpak/bus`.
[π¦ org.kde.kleopatra ~]$ export
DBUS_SESSION_BUS_ADDRESS=unix:path=$XDG_RUNTIME_DIR/bus
[π¦ org.kde.kleopatra ~]$ ls -l $XDG_RUNTIME_DIR/bus
lrwxrwxrwx. 1 jay jay 17 Feb 8 15:14 /run/user/1000/bus ->
../../flatpak/bus
[π¦ org.kde.kleopatra ~]$ kleopatra
In summary, steps to reproduce:
1. Restart agent or otherwise clear cache
2. Attempt to use private key in Flatpak app, pinentry fails
3. Use gnupg outside sandbox so that the pin gets entered and cached
3. Repeat in Flatpak app, using the private key now works
In the notes above with a clean environment, I only tested with
Kleopatra. Kleopatra gets access to the gpg socket by a filesystem
permission - `$XDG_RUNTIME_DIR/gnupg` is mapped into the sandbox. On my
production environment, Evolution gets access using Flatpak's socket
portal mechanism (`--socket=gpg-agent` permission). This does not seem
to be significant, the same behavior happens in both cases. Both
options are just making the socket available inside the sandbox.
I imagine there are relevant uses for the calling gpg process to
communicate variables from it's environment to the agent process (e.g.
PINENTRY_GEOM_HINT?). But this also means that the calling gpg process
can unintentionally cause the pinentry process to fail. I haven't
thought about this further, but could a malicious program compromise the
agent somehow by setting some unexpected values? I wonder if it would
be useful to either blocklist some specific variables (e.g.
DBUS_SESSION_BUS_ADDRESS) from being set through the socket. Or better
yet, perhaps the agent should only accept values for allowlisted
variables?
BR Jay
_______________________________________________
Gnupg-users mailing list
Gnupg-users@gnupg.org
https://lists.gnupg.org/mailman/listinfo/gnupg-users