webmin / usermin

Usermin source code
http://www.usermin.com/
Other
117 stars 48 forks source link

Mail cannot GPG sign when key has a passphrase. #72

Closed mcmeel closed 3 years ago

mcmeel commented 3 years ago

When attempting to GPG sign a message with a key that has a passphrase the following error is presented after clicking send:

Failed to send mail: Failed to sign message:
You need a passphrase to unlock the secret key for user: "Test User" 2048-bit RSA key, ID <key ID>, ...

The key was created using the Usermin GPG setup screen, and a file is present at ~/.usermin/gnupg/ named "pass.\<key ID>" with the correct passphrase. A private key created without a passphrase works without error.

OS: CentOS 7.9.2009 Usermin: 1.823

/var/usermin/miniserv.error:

Use of uninitialized value in split at ./mailbox-lib.pl line 601.
Use of uninitialized value in split at /usr/libexec/usermin/mailbox/send_mail.cgi line 287.
Use of uninitialized value in split at /usr/libexec/usermin/mailbox/send_mail.cgi line 320.
Error: Failed to sign message :
You need a passphrase to unlock the secret key for
user: "Test User"
2048-bit RSA key, ID 2E3F4FD2, created 2021-05-05

gpg: cancelled by user
gpg: no default secret key: Operation cancelled
gpg: signing failed
jcameron commented 3 years ago

If you use the gpg command on your system to sign a file using that same key, what exactly does it output when prompting for the passphrase?

mcmeel commented 3 years ago

I tried to sign the file that contains the passphrase for example:

gpg --sign .usermin/gnupg/pass.2E3F4FD2

outputs:

You need a passphrase to unlock the secret key for
user: "Test User <testuser@testdomain.com>"
2048-bit RSA key, ID 2E3F4FD2, created 2021-05-05

gpg: cancelled by user
gpg: no default secret key: Operation cancelled
gpg: signing failed: Operation cancelled

I was not prompted for any input, it just output the above.

jcameron commented 3 years ago

Was that run via SSH?

mcmeel commented 3 years ago

Yes, it was run in a shell via SSH as the user that owns the mailbox.

mcmeel commented 3 years ago

In an attempt to recreate with a different set of conditions I attempted this with Ubuntu 20.04 as the OS and Usermin version 1.823.

It fails still, but differently (of course).

In this case the correct passphrase is stored in ~/.usermin/gpupg/pass.\<key ID> as before, but the error in miniserv.error is:

Error: Failed to sign message : gpg: using "F7D9344DD2C91485B7BF7DE6F91C50BFF2F8C87E" as default secret key for signing
gpg: signing failed

(F7D9344DD2C91485B7BF7DE6F91C50BFF2F8C87E is the key ID)

When attempting to sign a file on the CLI, pinentry-curses prompts me for the passphrase and the signing works properly. A second attempt to send a signed email in Usermin now works properly.

I made the following observations:

There are differences in versions of gpg and pinentry between the two hosts as well.

CentOS gpg/pinentry = 2.0.22/0.8.1
Ubuntu gpg/pinentry = 2.2.19/1.1.0

If I kill the gpg-agent process on the Ubuntu host the mail fails to sign again until I sign again in the shell gpg command and the gpg-agent process starts back up. Killing the gpg-agent process on the CentOS host has no effect on the mail signing or shell gpg command signing.

jcameron commented 3 years ago

Interesting, so it looks like when you enter the passphrase, it's being kept in the gpg-agent so that Usermin's attempt to sign now works.

What if you supply the phrase to the gpg command with the --passphrase command line flag?

mcmeel commented 3 years ago

Short story, I wasn't able to supply the passphrase via the --passphrase command line flag no matter what I tried. When attempting to do so the same error messages were presented.

While looking at why that wasn't working I found information regarding the way pinentry tries to find the tty to prompt for the passphrase. Since this user is an email only type user, they didn't have a valid shell. I was running these commands with a combination of sudo -u directly with gpg, and /bin/bash for an interactive shell. All of these were still using my same tty owned by the user I logged in with.

To get around this I started /bin/bash in screen running as the user which allocated a tty. At this point pinentry prompts me for the passphrase. Once entered gpg signs successfully on the command line, and a gpg-agent process is started. At this point I tried to sign an email in Usermin but it failed with the same error as before, even with the gpg-agent still running.

Digging some more I found to the following command to provide the passphrase to gpg: --passphrase-fd

From the man page:

--passphrase-fd n
Read the passphrase from file descriptor n. If you use 0 for n, the passphrase will be read from stdin. This can only be used if only one passphrase is supplied. Don't use this option if you can avoid it.

--batch
Use batch mode. Never ask, do not allow interactive commands.

Using this I was able to use the ~/.usermin/gnupg/pass.\<key ID> file to sign on the command line without the need for a tty or pinentry by passing the contents of that file to the gpg command on STDIN (combination of --batch and --passphrase-fd 0).

cat ~/.usermin/gnupg/pass.2E3F4FD2 | gpg --batch --passphrase-fd 0 --sign ~/.usermin/gnupg/pass.2E3F4FD2

Although even after success there, Usermin is unable to sign emails using that key with or without the gpg-agent running (same errors are present in usermin.error).

Looking at the command in gnupg-lib.pl that is called as a result of sign_data:

$cmd = "$gpgpath --armor --output $dstfile --default-key $_[2]->{'key'} --detach-sig $srcfile";

I recreated that on the command line, adding --batch and --passphrase-fd 0, and passing the contents of the passphrase file via STDIN and it created a signature.

cat ~/.usermin/gnupg/pass.2E3F4FD2 | gpg --batch --armor --output foo.asc --default-key 2E3F4FD2 --passphrase-fd 0
--detach-sig foo.sh

But then I am lost.

jcameron commented 3 years ago

Did that final command with the passphrase being supplied as input work as expected? If so, it seems like I need to update Usermin to use that --passphrase-fd flag.

mcmeel commented 3 years ago

Yes, it worked as expected.

jcameron commented 3 years ago

Ok, I have implemented fixes for this issue which will be included in the next Usermin release.