Gnupg can't import or generate private key as root
Open, Needs TriagePublic

Description

gpg: key A4A0D73114FDE6FC/A4A0D73114FDE6FC: error sending to agent: Permission denied
gpg: error building skey array: Permission denied
gpg: error reading 'priv.key': Permission denied
gpg: import from 'priv.key' failed: Permission denied

write(5, "GENKEY", 6) = 6
write(5, "\n", 1) = 1
read(5, "S INQUIRE_MAXLEN 1024\nINQUIRE KE"..., 1002) = 39
write(5, "D (genkey(rsa(nbits 4:2048)))\n", 30) = 30
write(5, "END", 3) = 3
write(5, "\n", 1) = 1
read(5, "INQUIRE PINENTRY_LAUNCHED 16959 "..., 1002) = 65
write(5, "END", 3) = 3
write(5, "\n", 1) = 1
read(5, "ERR 83918849 Permission denied <"..., 1002) = 42

Used shell: zsh 5.7.1
Login: sudo -i

Details

External Link
https://bugs.archlinux.org/task/63711
Version
2.2.17
werner added a subscriber: werner.Sun, Sep 29, 7:40 PM

Please provide a full description of what you did. What command line did you use, have you su-ed or logged in regular.? What is the output of "gpgcof --list-dirs" ?

bionade24 updated the task description. (Show Details)Sun, Sep 29, 7:52 PM
sysconfdir:/etc/gnupg
bindir:/usr/bin
libexecdir:/usr/lib/gnupg
libdir:/usr/lib/gnupg
datadir:/usr/share/gnupg
localedir:/usr/share/locale
socketdir:/root/.gnupg
dirmngr-socket:/root/.gnupg/S.dirmngr
agent-ssh-socket:/root/.gnupg/S.gpg-agent.ssh
agent-extra-socket:/root/.gnupg/S.gpg-agent.extra
agent-browser-socket:/root/.gnupg/S.gpg-agent.browser
agent-socket:/root/.gnupg/S.gpg-agent
homedir:/root/.gnupg

You should always run gpg with --verbose if you run into an unknown error. It shows more information; in your case info about the requested pinentry. The strace does not show this. You probably have no permission to launch the X version opf the pinentry because the xauth does not work. As a quick test use ssh -X root@localhost instead.

bisson added a subscriber: bisson.Mon, Sep 30, 8:49 PM

What is weird is that pinentry supposedly detects the absence of an X session and falls back on curses. For instance, I have:

$ sudo su -l
# env | grep DISPLAY
# /usr/bin/pinentry-gtk-2
OK Pleased to meet you
GETPIN

This successfully runs an ncurses password prompt. However, when I run (in the exact same shell session):

# gpg --gen-key --verbose
gpg (GnuPG) 2.2.17; Copyright (C) 2019 Free Software Foundation, Inc.
[...]
gpg: no running gpg-agent - starting '/usr/bin/gpg-agent'
gpg: waiting for the agent to come up ... (5s)
gpg: connection to agent established
gpg: pinentry launched (124925 gtk2:curses 1.1.0 /dev/pts/6 xterm -)
gpg: agent_genkey failed: Permission denied
Key generation failed: Permission denied

if you run

gpg --debug ipc -K

you should see a line

[...] OPTION ttyname=/dev/pts/N

Check whether you can access that device. It is the device pinentry will use. If that does not get you any further add

log-file /somewhere/agent.log
verbose
debug-pinentry

to ~/.gnupg/gpg-agent.conf and restart gpg-agent. That will put all output from pinentry and the commands send to it into the log file.

Thanks for your help investigating this.

Here's the output of gpg --verbose --debug ipc --gen-key:

gpg: DBG: chan_4 <- OK Pleased to meet you, process 134432
gpg: DBG: connection to agent established
gpg: DBG: chan_4 -> RESET
gpg: DBG: chan_4 <- OK
gpg: DBG: chan_4 -> OPTION ttyname=/dev/pts/0
gpg: DBG: chan_4 <- OK
gpg: DBG: chan_4 -> OPTION ttytype=xterm
gpg: DBG: chan_4 <- OK
gpg: DBG: chan_4 -> OPTION lc-ctype=en_US.UTF-8
gpg: DBG: chan_4 <- OK
gpg: DBG: chan_4 -> OPTION lc-messages=en_US.UTF-8
gpg: DBG: chan_4 <- OK
gpg: DBG: chan_4 -> GETINFO version
gpg: DBG: chan_4 <- D 2.2.17
gpg: DBG: chan_4 <- OK
gpg: DBG: chan_4 -> OPTION allow-pinentry-notify
gpg: DBG: chan_4 <- OK
gpg: DBG: chan_4 -> OPTION agent-awareness=2.1.0
gpg: DBG: chan_4 <- OK
gpg: DBG: chan_4 -> RESET
gpg: DBG: chan_4 <- OK
gpg: DBG: chan_4 -> GENKEY
gpg: DBG: chan_4 <- S INQUIRE_MAXLEN 1024
gpg: DBG: chan_4 <- INQUIRE KEYPARAM
gpg: DBG: chan_4 -> D (genkey(rsa(nbits 4:2048)))
gpg: DBG: chan_4 -> END
gpg: DBG: chan_4 <- INQUIRE PINENTRY_LAUNCHED 134434 gtk2:curses 1.1.0 /dev/pts/0 xterm -
gpg: pinentry launched (134434 gtk2:curses 1.1.0 /dev/pts/0 xterm -)
gpg: DBG: chan_4 -> END
gpg: DBG: chan_4 <- ERR 83918849 Permission denied <Pinentry>
gpg: agent_genkey failed: Permission denied
Key generation failed: Permission denied
gpg: secmem usage: 1344/32768 bytes in 2 blocks

Indeed, the permissions on /dev/pts/0 are:

# ls -la /dev/pts/0
crw--w---- 1 bisson tty 136, 0 Sep 30 11:05 /dev/pts/0

while I am running as root. But running /usr/bin/pinentry-gtk-2 directly works as expected. What is different when gpg --gen-key runs pinentry that makes it fail?

werner added a comment.Tue, Oct 1, 9:51 AM

Do you have

GPG_TTY=$(tty)
export GPG_TTY

in your shell's profile so that gpg-agent knowns the right tty to use? /dev/pts/0 does not look right.

I did not (neither in my root shell nor in my user shell) but setting and exporting this environment variable does not make any difference: gpg --gen-key still fails as above. (Note that tty indeed returns /dev/pts/0 .)

Also in another terminal?

bisson added a comment.Tue, Oct 1, 8:46 PM

My other terminals (xterm) are /dev/pts/1, /dev/pts/2, etc. and I can reproduce the bug in them too.

I can also reproduce the bug in a virtual console: log in as a regular user, run su -l, then gpg --gen-key fails as above; in that case tty returns /dev/tty2 which has the following permissions:

crw------- 1 bisson tty 4, 2 Oct  1 08:41 /dev/tty2

However, in both xterm and virtual consoles, running /usr/bin/pinentry and typing GETPIN successfully displays a curses password prompt.

bisson added a comment.Tue, Oct 1, 9:42 PM

I found a way to replicate that error with just pinentry by doing (as root):

# tty
/dev/pts/1
# pinentry
OK Pleased to meet you
OPTION ttyname=/dev/pts/1
OK
GETPIN
S ERROR gtk2.open_tty_for_read 83918849
ERR 83918849 Permission denied <Pinentry>

When I remove OPTION ttyname=... there is no error.

bisson added a comment.Tue, Oct 1, 9:54 PM

I believe the issue is as follows. When given the option ttyname=... pinentry will open() the given tty and that fails since it is owned by the regular user and not root; strace reports:

openat(AT_FDCWD, "/dev/pts/1", O_RDONLY) = -1 EACCES (Permission denied)

However, when not given this option, pinentry will simply write() to stdout which causes no permission problem; through sudo and the terminal this goes to /dev/pts/1.