QubesOS / qubes-issues

The Qubes OS Project issue tracker
https://www.qubes-os.org/doc/issue-tracking/
539 stars 48 forks source link

split-gpg2 client exits non-zero when importing public keys on alternate gpg homedir #9534

Open ben-grande opened 1 week ago

ben-grande commented 1 week ago

Qubes OS release

R4.2

Brief summary

When import a public key for the first time to a split-gpg2 client, it has a delay as the agent doesn't respond, imports the key but exits non zero. What I am currently doing to overcome the error exit code:

Steps to reproduce

Add a new public key (not previously imported) to the client keyring.

Expected behavior

Import happens successfully and exits with code zero. The /usr/share/split-gpg2/gpg-agent-placeholder should reply with something useful when importing a key, not only exit zero.

Actual behavior

Import happens successfully, but it has a delay and exits with code non-zero.

gpg --status-fd=2 --homedir=/tmp/tmp.mWSPrpSXoW --import salt/qubes-builder/files/client/qusal/keys/DF3834875B65758713D92E91A475969DE4E371E3.asc
[GNUPG:] KEY_CONSIDERED DF3834875B65758713D92E91A475969DE4E371E3 0
gpg: key A475969DE4E371E3: public key "Ben Grande (Code signing key) <ben.grande.b@gmail.com>" imported
[GNUPG:] IMPORTED A475969DE4E371E3 Ben Grande (Code signing key) <ben.grande.b@gmail.com>
[GNUPG:] IMPORT_OK 1 DF3834875B65758713D92E91A475969DE4E371E3
gpg: can't connect to the agent: IPC connect call failed
gpg: Total number processed: 1
gpg:               imported: 1
[GNUPG:] IMPORT_RES 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0
[GNUPG:] KEY_CONSIDERED DF3834875B65758713D92E91A475969DE4E371E3 0
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
zsh: exit 2     gpg --homedir=/tmp/tmp.kre9T3wViO --import
marmarek commented 1 week ago

What does it do with the agent? Agent is responsible for handling only secret keys... Maybe it tries to check if it has secret part for this key? Can you enabled debugging in the split-gpg2 and see what it tries to do? See debug_log option in the config: https://github.com/QubesOS/qubes-app-linux-split-gpg2/blob/main/qubes-split-gpg2.conf.example

marmarek commented 1 week ago

Or maybe just journalctl/.xsession-errors in the backend will have that info already?

ben-grande commented 1 week ago

What does it do with the agent? Agent is responsible for handling only secret keys... Maybe it tries to check if it has secret part for this key?

Possibly tries to check if there is a secret part.

Can you enabled debugging in the split-gpg2 and see what it tries to do?

Checked the dom0 logs now and it never calls the split-gpg2 backend, nothing logged to the journal of qubes-qrexec-policy-daemon.

marmarek commented 1 week ago

hmm, does it mean split-gpg2 isn't working for you there at all? maybe the client part fails to start or such?

ben-grande commented 1 week ago

split-gpg2 works fine, I can list secret keys only available in the backend.

Euwiiwueir commented 6 days ago

split-gpg2 works fine, I can list secret keys only available in the backend.

Can you also sign or encrypt with them?

ben-grande commented 6 days ago

Yes, I have full split-gpg2 functionality, listing, signing, encrypting.

It is on every minimal template based qube, I have not tried on a full template.

On Sat, Oct 26, 2024, 5:38 PM Euwiiwueir @.***> wrote:

split-gpg2 works fine, I can list secret keys only available in the backend.

Can you also sign or encrypt with them?

— Reply to this email directly, view it on GitHub https://github.com/QubesOS/qubes-issues/issues/9534#issuecomment-2439624569, or unsubscribe https://github.com/notifications/unsubscribe-auth/BCE2O4OC5Z7R2IFOVR6NK3TZ5OZOXAVCNFSM6AAAAABQRATGTOVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDIMZZGYZDINJWHE . You are receiving this because you authored the thread.Message ID: @.***>

ben-grande commented 4 days ago

Example to demonstrate the problem:

#!/bin/sh
set -eu

key_url="https://keys.openpgp.org/vks/v1/by-fingerprint/86BA6E93318FBA446642A90ADB8FD31CCAD7D72C"
gnupg_homedir="$(mktemp -d)"
trap 'rm -rf - "${gnupg_homedir}"' EXIT INT HUP QUIT ABRT
cd "${gnupg_homedir}" || exit 1
curl -o key.asc "${key_url}"
gpg --homedir . --import key.asc
user@disp8589:~$ sudo touch /var/run/qubes-service/split-gpg2-client

user@disp8589:~$ sudo apt install -y split-gpg2

user@disp8589:~$ sh -x ./imp.sh
+ set -eu
+ key_url=https://keys.openpgp.org/vks/v1/by-fingerprint/86BA6E93318FBA446642A90ADB8FD31CCAD7D72C
+ mktemp -d
+ gnupg_homedir=/tmp/tmp.axcQxkaPH3
+ trap rm -rf - "${gnupg_homedir}" EXIT INT HUP QUIT ABRT
+ cd /tmp/tmp.axcQxkaPH3
+ curl -o key.asc https://keys.openpgp.org/vks/v1/by-fingerprint/86BA6E93318FBA446642A90ADB8FD31CCAD7D72C
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  4426  100  4426    0     0  25248      0 --:--:-- --:--:-- --:--:-- 25291
+ gpg --homedir . --import key.asc
gpg: keybox '/tmp/tmp.axcQxkaPH3/pubring.kbx' created
gpg: key DB8FD31CCAD7D72C: 1 signature not checked due to a missing key
gpg: /tmp/tmp.axcQxkaPH3/trustdb.gpg: trustdb created
gpg: key DB8FD31CCAD7D72C: public key "Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>" imported
gpg: can't connect to the agent: IPC connect call failed
gpg: Total number processed: 1
gpg:               imported: 1
gpg: no ultimately trusted keys found
+ rm -rf - /tmp/tmp.axcQxkaPH3
user@disp8589:~$ sudo rm -f /var/run/qubes-service/split-gpg2-client

user@disp8589:~$ sh -x ./imp.sh
+ set -eu
+ key_url=https://keys.openpgp.org/vks/v1/by-fingerprint/86BA6E93318FBA446642A90ADB8FD31CCAD7D72C
+ mktemp -d
+ gnupg_homedir=/tmp/tmp.VFGlryM71C
+ trap rm -rf - "${gnupg_homedir}" EXIT INT HUP QUIT ABRT
+ cd /tmp/tmp.VFGlryM71C
+ curl -o key.asc https://keys.openpgp.org/vks/v1/by-fingerprint/86BA6E93318FBA446642A90ADB8FD31CCAD7D72C
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  4426  100  4426    0     0  23739      0 --:--:-- --:--:-- --:--:-- 23795
+ gpg --homedir . --import key.asc
gpg: keybox '/tmp/tmp.VFGlryM71C/pubring.kbx' created
gpg: key DB8FD31CCAD7D72C: 1 signature not checked due to a missing key
gpg: /tmp/tmp.VFGlryM71C/trustdb.gpg: trustdb created
gpg: key DB8FD31CCAD7D72C: public key "Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>" imported
gpg: Total number processed: 1
gpg:               imported: 1
gpg: no ultimately trusted keys found
+ rm -rf - /tmp/tmp.VFGlryM71C
marmarek commented 4 days ago

I think it's not related to the import operation, but to a changed homedir. split-gpg2 listens only on the primary (default) socket. I guess the solution would be to change gpg-agent-placeholder to check also for changed home dir (option and/or env variable) and don't intercept those calls. Alternative would be to start listening on those extra sockets, but IMO that would be confusing - you use alternative gpg homedir usually to not have other keys there.

ben-grande commented 4 days ago

Good point. It is not always passed to the environment variable but always as parameter, $@ contains: --homedir /tmp/tmp.un8FBXDKRM --use-standard-socket --daemon. Setting GNUPGHOME or --homedir option on the command line, results in the positional parameter --homedir being passed to the agent in the end.

/usr/share/split-gpg2/gpg-agent-placeholder:

#!/bin/bash

set -eo pipefail

printf '%s\n' "GNUPGHOME=${GNUPGHOME:-}" | tee -- /tmp/log >/dev/null
printf '%s\n' "$@" | tee -a -- /tmp/log >/dev/null
gpgconf --list-dirs | grep -e -socket -e homedir | tee -a -- /tmp/log >/dev/null

# prevent starting real gpg-agent locally if it's redirected via split-gpg2
if [ -e /run/qubes-service/split-gpg2-client ]; then
    exit 0
fi
# otherwise, launch gpg-agent
gpgagent=$(gpgconf --list-components | grep ^gpg-agent: | cut -d ':' -f 3)
exec "$gpgagent" "$@"

imp.sh:

#!/bin/sh
set -eu

key_url="https://keys.openpgp.org/vks/v1/by-fingerprint/86BA6E93318FBA446642A90ADB8FD31CCAD7D72C"
gnupg_homedir="$(mktemp -d)"
trap 'rm -rf - "${gnupg_homedir}"' EXIT INT HUP QUIT ABRT
cd "${gnupg_homedir}" || exit 1
curl -o key.asc "${key_url}"
#gpg --import key.asc
gpg --homedir . --import key.asc
#GNUPGHOME=. gpg --import key.asc
#GNUPGHOME="${gnupg_homedir}" gpg --import key.asc
$ GNUPGHOME="${gnupg_homedir}" gpg --import key.asc
> GNUPGHOME=/tmp/tmp.gQzeYPoncU
> --homedir /tmp/tmp.gQzeYPoncU --use-standard-socket --daemon
> dirmngr-socket:/run/user/1000/gnupg/d.7e941cr5w3kz9fjxqp48yqfe/S.dirmngr
> agent-ssh-socket:/run/user/1000/gnupg/d.7e941cr5w3kz9fjxqp48yqfe/S.gpg-agent.ssh
> agent-extra-socket:/run/user/1000/gnupg/d.7e941cr5w3kz9fjxqp48yqfe/S.gpg-agent.extra
> agent-browser-socket:/run/user/1000/gnupg/d.7e941cr5w3kz9fjxqp48yqfe/S.gpg-> agent.browser
> agent-socket:/run/user/1000/gnupg/d.7e941cr5w3kz9fjxqp48yqfe/S.gpg-agent
> homedir:/tmp/tmp.gQzeYPoncU
$ GNUPGHOME=. gpg --import key.asc
> GNUPGHOME=.
> --homedir /tmp/tmp.vC3JfJwgpM --use-standard-socket --daemon
> dirmngr-socket:/run/user/1000/gnupg/d.qsn8stftdyd1o57odsjuedkc/S.dirmngr
> agent-ssh-socket:/run/user/1000/gnupg/d.qsn8stftdyd1o57odsjuedkc/S.gpg-agent.ssh
> agent-extra-socket:/run/user/1000/gnupg/d.qsn8stftdyd1o57odsjuedkc/S.gpg-agent.extra
> agent-browser-socket:/run/user/1000/gnupg/d.qsn8stftdyd1o57odsjuedkc/S.gpg-agent.browser
> agent-socket:/run/user/1000/gnupg/d.qsn8stftdyd1o57odsjuedkc/S.gpg-agent
> homedir:/tmp/tmp.vC3JfJwgpM
$ gpg --homedir . --import key.asc
> GNUPGHOME=
> --homedir /tmp/tmp.M2QRZOmkxv --use-standard-socket --daemon
> dirmngr-socket:/run/user/1000/gnupg/S.dirmngr
> agent-ssh-socket:/run/user/1000/gnupg/S.gpg-agent.ssh
> agent-extra-socket:/run/user/1000/gnupg/S.gpg-agent.extra
> agent-browser-socket:/run/user/1000/gnupg/S.gpg-agent.browser
> agent-socket:/run/user/1000/gnupg/S.gpg-agent
> homedir:/home/user/.gnupg

The last one is quite problematic as the homedir on the command line and CWD changed, but gpgconf reports the default homedir and GNUPGHOME reports emtpy.

What happens when I try to list secret with the default homedir:

$ gpg -K
> GNUPGHOME=
> --homedir /home/user/.gnupg --use-standard-socket --daemon
> dirmngr-socket:/run/user/1000/gnupg/S.dirmngr
> agent-ssh-socket:/run/user/1000/gnupg/S.gpg-agent.ssh
> agent-extra-socket:/run/user/1000/gnupg/S.gpg-agent.extra
> agent-browser-socket:/run/user/1000/gnupg/S.gpg-agent.browser
> agent-socket:/run/user/1000/gnupg/S.gpg-agent
> homedir:/home/user/.gnupg

From my tests, --homedir is always passed. I finished with the gpg-agent-placeholder like this:

#!/bin/sh
set -euo pipefail

# prevent starting real gpg-agent locally if it's redirected via split-gpg2
if [ -e /run/qubes-service/split-gpg2-client ]; then
    case "$@" in
        *"--homedir $HOME/.gnupg "*)
            exit 0
            ;;
    esac
    # Another option
    #if printf '%s\n' "$@" | grep -q -- "--homedir $HOME/.gnupg "; then
    #  exit 0
    #fi
fi

# otherwise, launch gpg-agent
gpgagent="$(gpgconf --list-components | awk -F: '/^gpg-agent:/{print $3}')"
exec "$gpgagent" "$@"
ben-grande commented 3 days ago

PR submitted.