dyne / tomb

the Crypto Undertaker
https://dyne.org/software/tomb
GNU General Public License v3.0
1.32k stars 150 forks source link

Tomb 2.9.0 as root with smartcard [Bug? + Workaround] #449

Open so-rose opened 1 year ago

so-rose commented 1 year ago

A little background first. I'm using Secure Boot, which requires one to sign kernel modules with a user-generated, firmware-enrolled key. Anybody with this key can theoretically execute kernel-level code. Therefore, I'm attempting to use tomb to make these keys inaccessible without a GPG private key, stored on a piece of hardware (Yubikey).

This is my setup for running tomb:

I'm running the following commands as root, with no preexisting /root/.gnupg:

gpg --recv-keys $KEY_ID  ## import the tomb's owner + initialize /root/.gnupg

tomb dig -s 10 /root/mok.tomb
tomb forge /root/mok.tomb.key -gr $KEY_ID
tomb lock /root/mok.tomb -k /root/mok.tomb.key -gr $KEY_ID

#...open the tomb and use it!

The Problem

Running a similar script (s/root/$HOME/g) as a normal user works perfectly: The GUI pinentry shows, all is well.

As root, however, tomb lock fails insisting that there's no valid password (<> is redaction):

$ tomb lock /root/mok.tomb -k /root/mok.tomb.key -gr $KEY_ID -D
tomb  .  Locking using cipher: aes-xts-plain64
tomb [D] no password needed, using GPG key
tomb [D] get_lukskey
...
tomb [D] [GNUPG:] ENC_TO <> 1 0
tomb [D] [GNUPG:] KEY_CONSIDERED $KEY_ID 0
tomb [D] [GNUPG:] PINENTRY_LAUNCHED 140666 gnome3:curses 1.1.0 - xterm-kitty :0
tomb [D] [GNUPG:] KEY_CONSIDERED <> 0
tomb [D] gpg: encrypted with 4096-bit RSA key, ID $KEY_ID, created <>
tomb [D]       "<> <>"
tomb [D] gpg: public key decryption failed: Inappropriate ioctl for device
tomb [D] [GNUPG:] ERROR pkdecrypt_failed 83918950
tomb [D] [GNUPG:] BEGIN_DECRYPTION
tomb [D] [GNUPG:] DECRYPTION_FAILED
tomb [D] gpg: decryption failed: No secret key
tomb [D] [GNUPG:] END_DECRYPTION
tomb [D] get_lukskey returns 1
tomb [E] No valid password supplied.

It seems like gpg can't find anywhere to launch its pinentry. I observed:

All in all, it seems like gpg doesn't know how to launch the pinentry, and thus just fails. tomb gets no secret to unlock the key with, and thus - no tomb :frowning_face:

The Workaround

I noticed that the end of gpg_decrypt was where the critical gpg invocation was in the locking/opening procedure:

https://github.com/dyne/Tomb/blob/f35ad11e3f5b29fd9b441a3111cc7a0c097036b1/tomb#L1118-L1121

As it seemed like the pinentry-mode was an issue. I tried setting /root/.gnupg/gpg.conf after reading things like https://superuser.com/questions/520980/how-to-force-gpg-to-use-console-mode-pinentry-to-prompt-for-passwords, but this didn't seem to get picked up on.

Finally, because of answers like https://stackoverflow.com/questions/18123918/why-is-gpg-not-working-even-with-pinentry-installed, I tried adding --pinentry-mode loopback:

TOMBSECRET=`print - "$gpgpass" | \
        gpg --decrypt ${gpgpopt[@]} \
            --status-fd 2 --no-mdc-warning --no-permission-warning \
            --no-secmem-warning --pinentry-mode loopback 2> $tmpres`

Now, when I run the same command as root, it works!

$ tomb lock /root/mok.tomb -k /root/mok.tomb.key -gr $KEY_ID -D
tomb  .  Locking using cipher: aes-xts-plain64
tomb [D] no password needed, using GPG key
tomb [D] get_lukskey
tomb [D] Created tempfile: /tmp/14336175871231220299
Enter Passphrase: 

Again, it's curious that it still doesn't call the pinentry. This also breaks tomb when used as a non-root user.

Patch

If I understood the problem better, I'd be happy to suggest a PR. Perhaps a CLI option to explicitly turn on loopback pinentry? For now, I just have a patch (works on the Debian 11 version of tomb; I haven't tested upstream. It should be easy enough to modify the line number 1123 below to work on any tomb install)

tomb-loopback.patch

1123c1123
<           --no-secmem-warning 2> $tmpres`
---
>           --no-secmem-warning --pinentry-mode loopback 2> $tmpres`

used with:

patch /usr/bin/tomb < tomb-loopback.patch       ## Apply the patch
patch -R /usr/bin/tomb < tomb-loopback.patch   ## Remove the patch

Whenever I need to run tomb as root, I just wrap it in the patch:

gpg --recv-keys $KEY_ID  ## import the tomb's owner + initialize /root/.gnupg

patch /usr/bin/tomb < tomb-loopback.patch  ## A trap could make this more reliable
    tomb dig -s 10 /root/mok.tomb
    tomb forge /root/mok.tomb.key -gr $KEY_ID
    tomb lock /root/mok.tomb -k /root/mok.tomb.key -gr $KEY_ID

    tomb open /root/mok.tomb -k /root/mok.tomb.key -g -p
patch -R /usr/bin/tomb < tomb-loopback.patch

#...add root:root owned secrets to the tomb (courtesy -p).

When actually signing kernel modules via DKMS, I just modified /etc/dkms/sign_helper.sh to patch tomb, open mok.tomb (with GPG and -p, to avoid chowning files to a user calling via sudo), sign, tomb slam and unpatch tomb.

Thus, it's been achieved that no kernel modules can be signed without the presence of the smartcard w/the GPG private key :smile:

System Info

Here's tomb -v:

  Tomb 2.9.0 - a strong and gentle undertaker for your secrets

   Copyright (C) 2007-2021 Dyne.org Foundation, License GNU GPL v3+
   This is free software: you are free to change and redistribute it
   For the latest sourcecode go to <http://dyne.org/software/tomb>

   This source code is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
   When in need please refer to <http://dyne.org/support>.

  System utils:

No $DBUS_SESSION_BUS_ADDRESS found, falling back to curses
gpg: WARNING: unsafe ownership on homedir '/home/sofus/.gnupg'
gpg: WARNING: unsafe ownership on homedir '/home/sofus/.gnupg'
  zsh 5.8 (x86_64-debian-linux-gnu)
  Sudo version 1.9.5p2
  cryptsetup 2.3.7
  pinentry-gnome3 (pinentry) 1.1.0
  findmnt from util-linux 2.36.1
  gpg (GnuPG) 2.2.27 - key forging algorithms (GnuPG symmetric ciphers):
  IDEA 3DES CAST5 BLOWFISH AES AES192 AES256 TWOFISH CAMELLIA128 CAMELLIA192 CAMELLIA256

  Optional utils:

  /usr/bin/gettext
  dcfldd not found
  /usr/bin/shred
  steghide not found
  /usr/sbin/resize2fs
  /usr/libexec/tomb/tomb-kdb-pbkdf2
  /usr/bin/qrencode
  swish-e not found
  unoconv not found
  /usr/bin/lsof
jaromil commented 1 year ago

Hi @so-rose and thanks for the detailed description!

I suspect that it may be possible to detect the specific situation of calling tomb from root user and then add the pinentry-loopback option to gpg invokation.

so-rose commented 1 year ago

@jaromil Sure thing! Hoped it might be of help to others.

Would you be open to a PR that detects root invocation (not just sudo) and injects --pinentry-mode loopback accordingly? It still bugs me that the normal pinentry doesn't show when starting explictly as root, but perhaps that's by gpg's design...

If you'd like, I could also document the firmware-key usage somewhere. I don't know where would be best, though?

jaromil commented 1 year ago

Yes, that would be good, as long as it doesn't break current tests.

jaromil commented 7 months ago

Hi @so-rose ! in case you are still up for this, I keep this issue open. It is a very interesting use case so please let us know if we can link your docs on firmware-key usage and of course you are welcome to file such a PR, else I'll do it myself.

jaromil commented 4 months ago

Will try, any help crafting the right check is very welcome.