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

Reply via email to