diff options
Diffstat (limited to 'modules.d/91crypt-gpg')
-rw-r--r-- | modules.d/91crypt-gpg/README | 50 | ||||
-rwxr-xr-x | modules.d/91crypt-gpg/crypt-gpg-lib.sh | 68 | ||||
-rwxr-xr-x | modules.d/91crypt-gpg/module-setup.sh | 73 |
3 files changed, 191 insertions, 0 deletions
diff --git a/modules.d/91crypt-gpg/README b/modules.d/91crypt-gpg/README new file mode 100644 index 0000000..be6df55 --- /dev/null +++ b/modules.d/91crypt-gpg/README @@ -0,0 +1,50 @@ +# Directions for changing a system from password-based gpg keyfile +# to smartcard-based gpg keyfile + +# Be sure that you meet the following requirements: +# 1. GnuPG >= 2.1 installed with +# * Smartcard support enabled (scdaemon must be built) +# * Direct CCID access built into scdaemon +# 2. A password-based gpg keyfile ${KEYFILE} (e.g. "keyfile.gpg"): +# That is, a file containing the slot key for LUKS, which +# has been encrypted symmetrically with GnuPG using +# a password. +# 3. Your public OpenPGP identity ${RECIPIENT} (e.g. "3A696356") +# 4. An OpenPGP smartcard holding the decryption key associated +# with your public identity +# 5. A CCID smartcard reader + +# Notes: Requirement 4. and 5. can of course be one device, e.g. +# a USB token with an integrated OpenPGP smartcard + +# Make a backup of your keyfile (assuming it lies on the boot partition) +$ cp /boot/${KEYFILE} /safe/place/keyfile.bak.gpg + +# Change your keyfile from purely password-based to both +# password-based and key-based (you can then decrypt the keyfile +# with either method). As an example aes256 is chosen, the cipher +# is not important to this guide, but do note that your kernel +# must support it at boot time (be it built into the kernel image +# or loaded as a module from the initramfs). +$ cat /safe/place/keyfile.bak.gpg | gpg -d | gpg --encrypt --recipient ${RECIPIENT} --cipher-algo aes256 --armor -c > /safe/place/keyfile_sc.gpg + +# Verify that you can decrypt your new keyfile both with the password +# and your smartcard. +# (with smartcard inserted, you should be prompted for your PIN, unless +# you already did so and have not yet timed out) +$ gpg -d /safe/place/keyfile_sc.gpg +# (with smartcard disconnected, you should be prompted for your password) +$ gpg -d /safe/place/keyfile_sc.gpg + +# After verification, replace your old keyfile with your new one +$ su -c 'cp /safe/place/keyfile_sc.gpg /boot/${KEYFILE}' + +# Export your public key to where crypt-gpg can find it +$ gpg --armor --export-options export-minimal --export ${RECIPIENT} > /safe/place/crypt-public-key.gpg +$ su -c 'cp /safe/place/crypt-public-key.gpg /etc/dracut.conf.d/crypt-public-key.gpg' + +# Rebuild your initramfs as usual +# When booting with any of the requirements not met, crypt-gpg will default to password-based keyfile unlocking. +# If all requirements are met and smartcard support is not disabled by setting the kernel option "rd.luks.smartcard=0" +# crypt-gpg will try find and use a connected OpenPGP smartcard by prompting you for the PIN and then +# unlocking the gpg keyfile with the smartcard. diff --git a/modules.d/91crypt-gpg/crypt-gpg-lib.sh b/modules.d/91crypt-gpg/crypt-gpg-lib.sh new file mode 100755 index 0000000..538419f --- /dev/null +++ b/modules.d/91crypt-gpg/crypt-gpg-lib.sh @@ -0,0 +1,68 @@ +#!/bin/sh + +command -v ask_for_password > /dev/null || . /lib/dracut-crypt-lib.sh + +# gpg_decrypt mnt_point keypath keydev device +# +# Decrypts symmetrically encrypted (password or OpenPGP smartcard) key to standard output. +# +# mnt_point - mount point where <keydev> is already mounted +# keypath - GPG encrypted key path relative to <mnt_point> +# keydev - device on which key resides; only to display in prompt +# device - device to be opened by cryptsetup; only to display in prompt +gpg_decrypt() { + local mntp="$1" + local keypath="$2" + local keydev="$3" + local device="$4" + + local gpghome=/tmp/gnupg + local opts="--homedir $gpghome --no-mdc-warning --skip-verify --quiet" + opts="$opts --logger-file /dev/null --batch --no-tty --passphrase-fd 0" + + mkdir -m 0700 -p "$gpghome" + + # Setup GnuPG home and gpg-agent for usage of OpenPGP smartcard. + # This requires GnuPG >= 2.1, as it uses the new ,,pinentry-mode´´ + # feature, which - when set to ,,loopback´´ - allows us to pipe + # the smartcard's pin to GnuPG (instead of using a normal pinentry + # program needed with GnuPG < 2.1), making for uncomplicated + # integration with the existing codebase. + local useSmartcard="0" + local gpgMajorVersion + local gpgMinorVersion + local cmd + gpgMajorVersion="$(gpg --version | sed -n 1p | sed -n -r -e 's|.* ([0-9]*).*|\1|p')" + gpgMinorVersion="$(gpg --version | sed -n 1p | sed -n -r -e 's|.* [0-9]*\.([0-9]*).*|\1|p')" + + if [ "${gpgMajorVersion}" -ge 2 ] && [ "${gpgMinorVersion}" -ge 1 ] \ + && [ -f /root/crypt-public-key.gpg ] && getargbool 1 rd.luks.smartcard; then + useSmartcard="1" + echo "allow-loopback-pinentry" >> "$gpghome/gpg-agent.conf" + GNUPGHOME="$gpghome" gpg-agent --quiet --daemon + GNUPGHOME="$gpghome" gpg --quiet --no-tty --import < /root/crypt-public-key.gpg + local smartcardSerialNumber + smartcardSerialNumber="$(GNUPGHOME=$gpghome gpg --no-tty --card-status \ + | sed -n -r -e 's|Serial number.*: ([0-9]*)|\1|p' | tr -d '\n')" + if [ -n "${smartcardSerialNumber}" ]; then + inputPrompt="PIN (OpenPGP card ${smartcardSerialNumber})" + fi + GNUPGHOME="$gpghome" gpg-connect-agent 1> /dev/null learn /bye + opts="$opts --pinentry-mode=loopback" + cmd="GNUPGHOME=$gpghome gpg --card-status --no-tty > /dev/null 2>&1; gpg $opts --decrypt $mntp/$keypath" + else + cmd="gpg $opts --decrypt $mntp/$keypath" + fi + + ask_for_password \ + --cmd "$cmd" \ + --prompt "${inputPrompt:-Password ($keypath on $keydev for $device)}" \ + --tries 3 --tty-echo-off + + # Clean up the smartcard gpg-agent + if [ "${useSmartcard}" = "1" ]; then + GNUPGHOME="$gpghome" gpg-connect-agent 1> /dev/null killagent /bye + fi + + rm -rf -- "$gpghome" +} diff --git a/modules.d/91crypt-gpg/module-setup.sh b/modules.d/91crypt-gpg/module-setup.sh new file mode 100755 index 0000000..501869a --- /dev/null +++ b/modules.d/91crypt-gpg/module-setup.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +# GPG support is optional +# called by dracut +check() { + require_binaries gpg tr || return 1 + + if sc_requested; then + if ! sc_supported; then + dwarning "crypt-gpg: GnuPG >= 2.1 with scdaemon and libusb required for ccid smartcard support" + return 1 + fi + return 0 + fi + + return 255 +} + +# called by dracut +depends() { + echo crypt +} + +# called by dracut +install() { + inst_multiple gpg tr + inst "$moddir/crypt-gpg-lib.sh" "/lib/dracut-crypt-gpg-lib.sh" + + if sc_requested; then + inst_multiple gpg-agent + inst_multiple gpg-connect-agent + inst_multiple -o /usr/libexec/scdaemon /usr/lib/gnupg/scdaemon + cp "$dracutsysrootdir$(sc_public_key)" "${initdir}/root/" + fi +} + +sc_public_key() { + echo -n "/etc/dracut.conf.d/crypt-public-key.gpg" +} + +# CCID Smartcard support requires GnuPG >= 2.1 with scdaemon and libusb +sc_supported() { + local gpgMajor + local gpgMinor + local scdaemon + gpgMajor="$(gpg --version | sed -n 1p | sed -n -r -e 's|.* ([0-9]*).*|\1|p')" + gpgMinor="$(gpg --version | sed -n 1p | sed -n -r -e 's|.* [0-9]*\.([0-9]*).*|\1|p')" + + if [[ -x "$dracutsysrootdir"/usr/libexec/scdaemon ]]; then + scdaemon=/usr/libexec/scdaemon + elif [[ -x "$dracutsysrootdir"/usr/lib/gnupg/scdaemon ]]; then + scdaemon=/usr/lib/gnupg/scdaemon + else + return 1 + fi + + if [[ ${gpgMajor} -gt 2 || ${gpgMajor} -eq 2 && ${gpgMinor} -ge 1 ]] \ + && require_binaries gpg-agent \ + && require_binaries gpg-connect-agent \ + && ($DRACUT_LDD "${dracutsysrootdir}${scdaemon}" | grep libusb > /dev/null); then + return 0 + else + return 1 + fi +} + +sc_requested() { + if [ -f "$dracutsysrootdir$(sc_public_key)" ]; then + return 0 + else + return 1 + fi +} |