diff options
-rw-r--r-- | .SRCINFO | 14 | ||||
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | PKGBUILD | 17 | ||||
-rw-r--r-- | README | 83 | ||||
-rw-r--r-- | hook_tpm | 234 | ||||
-rw-r--r-- | hosts | 4 | ||||
-rw-r--r-- | install_tpm | 45 | ||||
-rw-r--r-- | shadow | 2 |
8 files changed, 130 insertions, 272 deletions
@@ -1,6 +1,6 @@ pkgbase = mkinitcpio-tpm-encrypt - pkgdesc = mkinitcpio to decrypt LUKS passphrase with TPM - pkgver = 0.1 + pkgdesc = mkinitcpio hook that decrypts a TPM-sealed LUKS keyfile + pkgver = 1.0 pkgrel = 1 url = https://aur.archlinux.org/packages/mkinitcpio-tpm-encrypt/ arch = any @@ -12,14 +12,12 @@ pkgbase = mkinitcpio-tpm-encrypt source = hook_tpm source = hosts source = passwd - source = shadow source = README - sha256sums = da6ddaff8783a80568e8990016ee275a6e6ab64d57881a8438e5fadc0a5f07a1 - sha256sums = 5630d592ab3adc2d461fa90026f2bcc0a81cc4219f62f968992c1c2cc8d958e1 - sha256sums = 498f494232085ec83303a2bc6f04bea840c2b210fbbeda31a46a6e5674d4fc0e + sha256sums = 96ca20898dd38c4828da7e9dede1d09bd752a4f8961420f0a064e74cf9a4b711 + sha256sums = 32ef6c10d64b3af1e83dee1583d15d22d9b192ad5710c56e544c8cdb2e49185f + sha256sums = b30167ccb1927f0f62dfb658fee49ba857b3f7b85cc6a4d3c659236bb01b1a03 sha256sums = 4b263523f4904bfe340a3208f327697ebd78f9f921e8be0dabdf33535c54a1b5 - sha256sums = 674c97df063879b4ba04f70312dc67762a36dc81b49a2b2131e7f71432efa1a3 - sha256sums = 70288bb0e9dc6bf4cd49079a503fb436c1b0b234f23201b24762e47db3cfd1ab + sha256sums = c3e777c9d620927045991a88723b06d2947ced566ce6404239662006b9e186c6 pkgname = mkinitcpio-tpm-encrypt diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000000..2b2f66f1ee18 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.pkg.tar.xz +/pkg +/src @@ -1,9 +1,10 @@ # Maintainer: Max Resch <resch.max@gmail.com> +# Maintainer: Corey Hinshaw <coreyhinshaw@gmail.com> pkgname=mkinitcpio-tpm-encrypt -pkgver=0.1 +pkgver=1.0 pkgrel=1 -pkgdesc="mkinitcpio to decrypt LUKS passphrase with TPM" +pkgdesc="mkinitcpio hook that decrypts a TPM-sealed LUKS keyfile" url="https://aur.archlinux.org/packages/mkinitcpio-tpm-encrypt/" arch=(any) license=('GPL') @@ -12,22 +13,18 @@ source=('install_tpm' 'hook_tpm' 'hosts' 'passwd' - 'shadow' 'README') -sha256sums=('da6ddaff8783a80568e8990016ee275a6e6ab64d57881a8438e5fadc0a5f07a1' - '5630d592ab3adc2d461fa90026f2bcc0a81cc4219f62f968992c1c2cc8d958e1' - '498f494232085ec83303a2bc6f04bea840c2b210fbbeda31a46a6e5674d4fc0e' +sha256sums=('96ca20898dd38c4828da7e9dede1d09bd752a4f8961420f0a064e74cf9a4b711' + '32ef6c10d64b3af1e83dee1583d15d22d9b192ad5710c56e544c8cdb2e49185f' + 'b30167ccb1927f0f62dfb658fee49ba857b3f7b85cc6a4d3c659236bb01b1a03' '4b263523f4904bfe340a3208f327697ebd78f9f921e8be0dabdf33535c54a1b5' - '674c97df063879b4ba04f70312dc67762a36dc81b49a2b2131e7f71432efa1a3' - '70288bb0e9dc6bf4cd49079a503fb436c1b0b234f23201b24762e47db3cfd1ab') + 'c3e777c9d620927045991a88723b06d2947ced566ce6404239662006b9e186c6') package() { install -Dm644 install_tpm "${pkgdir}/usr/lib/initcpio/install/tpm" install -Dm644 hook_tpm "${pkgdir}/usr/lib/initcpio/hooks/tpm" install -Dm644 hosts "${pkgdir}/usr/lib/initcpio/tpm/hosts" install -Dm644 passwd "${pkgdir}/usr/lib/initcpio/tpm/passwd" - install -Dm644 shadow "${pkgdir}/usr/lib/initcpio/tpm/shadow" install -Dm644 README "${pkgdir}/usr/share/doc/${pkgname}/README" } - @@ -1,66 +1,41 @@ +mkinitcpio TPM hook +=================== -This is a modified version of the default Archlinux encrypt hook. -The only difference is that the cryptkey file from the commandline -is handled by tpm_unsealdata before it is passed to cryptosetup. +This hook allows for an encrypted root device to use a keyfile sealed by the +TPM. It should be placed immediately before the `encrypt` hook in +`/etc/mkinitcpio.conf`. -Fist the TPM has to be configured and trousers and tpm_tools -have to be installed. + HOOKS="base udev ... block tpm encrypt filesystems ..." -To initialize the TPM run +The `tpm` hook attempts to "unseal" a dm-crypt keyfile previously sealed by the +TPM. This file must reside on an unencrypted filesystem available to the kernel +at boot. For example, assuming your unencrypted keyfile is at `/root/mykey`: -# tpm_takeownership + # tpm_sealdata -i /root/mykey -o /boot/mykey.enc -p 0 -p 1 -p 2 -p 3 -p 5 -p 7 -p 8 -Don't use either -y or -z these are the all-zero default -passwords. The Storage Root Key (SRK) password will be requestet on -every boot to open the encrypted LUKS key. +After generating a TPM-sealed keyfile, 'tpmkey=device:fstype:path' should be +specified on the kernel cmdline, where 'device' represents the raw block device +where the key exists, 'fstype' is the filesystem type of 'device' (or auto), +and 'path' is the absolute path of the keyfile within the device. For example, +if `/dev/sda1` is an EFI partition mounted at `/boot`: -The SRK password can be changed with + tpmkey=/dev/sda1:vfat:/mykey.enc -# tpm_changeownerauth -s +You may also need to add the file system driver to the `MODULES` array: -In order to create a valid key file run: + MODULES="vfat" -# tpm_sealdata --pcr 0 --pcr 1 --pcr 2 --pcr 3 --pcr 4 --pcr 8 --pcr 9 --pcr 12 --pcr 14 -o root.enc +Finally, rebuild the initramfs: -where root.enc is the resulting encrypted file. Save it on your -EFI System Partition, and specifiy it in the kernel commandline: + # mkinitcpio -p linux -cryptkey=/dev/sda1:vfat:/root.enc - -where /dev/sda1 is your ESP, and /root.enc the path to the key. - -Modify /etc/mkinitcpio.conf and add the tpm hook -in the HOOKS array: - -HOOKS="base udev keymap autodetect keyboard block tpm filesystems fsck" - -(the same as with normal encrypt, you need to add keymap and keyboard -if you need support for either non US keyboards or keyboards that -require i.e. USB). Don't forget to add the filesystem dirver -from cryptkey= to the MODULES array - -MODULES="vfat" - -Now you can run mkinitcpio, to be save make a backup copy of your working -initram disk, so that you can use it if the new one is corrupt. - -# mkinitcpio -p linux - -If everything worked the scipt will ask for your SRK password. - -In case a kernel update was performed or if a Firmware configuration -setting was changed, unsealdata will not return the LUKS key, and a -warning is displayed. - -If you haven't changed something on your system, this warining could -indicate a possible evil maiden attack. - -In order to continue booting the LUKS key has to -be supplied manually. - -So it is IMHO best to have two LUKS keys configured, one that is typable -in case of an update, and one that is encrypted on the ESP. - -After the LUKS key was manually entered, the key has to be reencrypted -with tpm_seal data as described before with the new PCR data. +During boot, the hook will initialize the TPM and attempt to unseal the keyfile. +If the SRK is protected by a passphrase, you will be prompted to enter it. If +the file is successfully unsealed, it will be passed to the `encrypt` hook to +perform the actual unlocking of the root fs. +Depending on the PCRs the sealed keyfile is bound to, system changes such as +kernel updates or firmware adjustments may prevent the file from being unsealed. +If this happens, the disk must be manually unlocked with a passphrase and a new +sealed key file needs to be generated. For this reason, it is CRUCIAL to add +a separate "recovery" passphrase to the LUKS keys. @@ -1,180 +1,94 @@ #!/usr/bin/ash run_hook() { - msg ":: Initializing TPM..." - modprobe -q tpm_tis >/dev/null 2>&1 - iplink set lo up - tcsd - - modprobe -a -q dm-crypt >/dev/null 2>&1 - [ "${quiet}" = "y" ] && CSQUIET=">/dev/null" - - # Get keyfile if specified + # This file will be loaded by the emcrypt hook ckeyfile="/crypto_keyfile.bin" - if [ -n "$cryptkey" ]; then - IFS=: read ckdev ckarg1 ckarg2 <<EOF -$cryptkey + + # Get TPM keyfile if specified + tpmkeyfile="/tpm_keyfile.enc" + if [ -n "$tpmkey" ]; then + IFS=: read tkdev tkarg1 tkarg2 <<EOF +$tpmkey EOF - if [ "$ckdev" = "rootfs" ]; then - ckeyfile=$ckarg1 - elif resolved=$(resolve_device "${ckdev}" ${rootdelay}); then - case ${ckarg1} in - *[!0-9]*) - # Use a file on the device - # ckarg1 is not numeric: ckarg1=filesystem, ckarg2=path - mkdir /ckey - mount -r -t "$ckarg1" "$resolved" /ckey - dd if="/ckey/$ckarg2" of="$ckeyfile" >/dev/null 2>&1 - if [ -f /ckey/tpm/system.data ]; then - cp /ckey/tpm/system.data /var/lib/tpm/ - fi - umount /ckey - ;; - *) - # Read raw data from the block device - # ckarg1 is numeric: ckarg1=offset, ckarg2=length - dd if="$resolved" of="$ckeyfile" bs=1 skip="$ckarg1" count="$ckarg2" >/dev/null 2>&1 - ;; + if [ "$tkdev" = "rootfs" ]; then + tpmkeyfile=$tkarg1 + elif resolved=$(resolve_device "${tkdev}" ${rootdelay}); then + case ${tkarg1} in + *[!0-9]*) + # Use a file on the device + # tkarg1 is not numeric: tkarg1=filesystem, tkarg2=path + mkdir /tpmkey + mount -r -t "$tkarg1" "$resolved" /tpmkey + dd if="/tpmkey/$tkarg2" of="$tpmkeyfile" >/dev/null 2>&1 + umount /tpmkey + rmdir /tpmkey + ;; + *) + # Read raw data from the block device + # tkarg1 is numeric: tkarg1=offset, tkarg2=length + dd if="$resolved" of="$tpmkeyfile" bs=1 skip="$tkarg1" count="$tkarg2" >/dev/null 2>&1 + ;; esac fi - [ ! -f ${ckeyfile} ] && echo "Keyfile could not be opened. Reverting to passphrase." + [ ! -f "$tpmkeyfile" ] && err "TPM keyfile could not be opened" fi - if [ -n "${cryptdevice}" ]; then - DEPRECATED_CRYPT=0 - IFS=: read cryptdev cryptname cryptoptions <<EOF -$cryptdevice -EOF - else - DEPRECATED_CRYPT=1 - cryptdev="${root}" - cryptname="root" - fi + # If we have a keyfile, decrypt it + if [ -f ${tpmkeyfile} ]; then + # initialize the TPM + msg ":: Initializing TPM..." + modprobe -q tpm_tis >/dev/null 2>&1 + iplink set lo up + tcsd - warn_deprecated() { - echo "The syntax 'root=${root}' where '${root}' is an encrypted volume is deprecated" - echo "Use 'cryptdevice=${root}:root root=/dev/mapper/root' instead." - } + tpm_unsealdata -z -i "$tpmkeyfile" -o "$ckeyfile" > /dev/null 2>&1 + unseal=$? + if [ $unseal -eq 1 ]; then + tpm_unsealdata -i "$tpmkeyfile" -o "$ckeyfile" > /dev/null 2>&1 + unseal=$? + fi - for cryptopt in ${cryptoptions//,/ }; do - case ${cryptopt} in - allow-discards) - cryptargs="${cryptargs} --allow-discards" - ;; - *) - echo "Encryption option '${cryptopt}' not known, ignoring." >&2 - ;; + tpmok=0 + case $unseal in + 0) + tpmok=1 + ;; + 1) + err "Wrong SEK password" + ;; + 17) + err "TPM communication error" + ;; + 24) + echo + echo "!!! TPM WARNING: PCR HAS BEEN ALTERED !!!" + echo "This is an indication that the boot configuration has changed since the TPM" + echo "keyfile was generated. This is normal after kernel updates or firmware changes," + echo "however this could also indicate a malicious change to your system." + echo + ;; + 255) + err "Could not read TPM keyfile" + ;; + *) + err "Unknown TPM error $unseal" + ;; esac - done - if resolved=$(resolve_device "${cryptdev}" ${rootdelay}); then - if cryptsetup isLuks ${resolved} >/dev/null 2>&1; then - [ ${DEPRECATED_CRYPT} -eq 1 ] && warn_deprecated - dopassphrase=1 - # If keyfile exists, try to use that - if [ -f ${ckeyfile} ]; then - tpm_unsealdata -i ${ckeyfile} -o ${ckeyfile}.orig - ret=$? - tpmok=0 - case $ret in - 0) - tpmok=1 - ;; - 1) - echo "TPM: Wrong SEK" - ;; - 17) - echo "TPM: Communication Error" - ;; - 24) - echo "" - echo -e "\033[31;1mTPM WARNING: PCR HAS BEEN ALTERED!\033[0m" - echo "This is an indication that the system boot is different from when the key was generated" - echo "This is normal when the kernel was updated or the BIOS settings were changed." - echo "" - echo -e "\033[31;1mHowever, this could also indicate a breach in the bootup code.\033[0m" - echo "" - ;; - 255) - echo "TPM: Could not read keyfile" - ;; - *) - echo "TPM: Unknown Error $ret" - ;; - esac - if [ ${tpmok} -gt 0 ]; then - if eval cryptsetup --key-file ${ckeyfile}.orig open --type luks ${resolved} ${cryptname} ${cryptargs} ${CSQUIET}; then - dopassphrase=0 - else - echo "Invalid keyfile. Reverting to passphrase." - fi - else - echo "TPM Could not decrypt keyfile, proceed manually." - fi - fi - # Ask for a passphrase - if [ ${dopassphrase} -gt 0 ]; then - echo "" - echo "A password is required to access the ${cryptname} volume:" - - #loop until we get a real password - while ! eval cryptsetup open --type luks ${resolved} ${cryptname} ${cryptargs} ${CSQUIET}; do - sleep 2; - done - fi - if [ -e "/dev/mapper/${cryptname}" ]; then - if [ ${DEPRECATED_CRYPT} -eq 1 ]; then - export root="/dev/mapper/root" - fi - else - err "Password succeeded, but ${cryptname} creation failed, aborting..." - exit 1 - fi - elif [ -n "${crypto}" ]; then - [ ${DEPRECATED_CRYPT} -eq 1 ] && warn_deprecated - msg "Non-LUKS encrypted device found..." - if echo "$crypto" | awk -F: '{ exit(NF == 5) }'; then - err "Verify parameter format: crypto=hash:cipher:keysize:offset:skip" - err "Non-LUKS decryption not attempted..." - return 1 - fi - exe="cryptsetup open --type plain $resolved $cryptname $cryptargs" - IFS=: read c_hash c_cipher c_keysize c_offset c_skip <<EOF -$crypto -EOF - [ -n "$c_hash" ] && exe="$exe --hash '$c_hash'" - [ -n "$c_cipher" ] && exe="$exe --cipher '$c_cipher'" - [ -n "$c_keysize" ] && exe="$exe --key-size '$c_keysize'" - [ -n "$c_offset" ] && exe="$exe --offset '$c_offset'" - [ -n "$c_skip" ] && exe="$exe --skip '$c_skip'" - if [ -f "$ckeyfile" ]; then - exe="$exe --key-file $ckeyfile" - else - exe="$exe --verify-passphrase" - echo "" - echo "A password is required to access the ${cryptname} volume:" - fi - eval "$exe $CSQUIET" - - if [ $? -ne 0 ]; then - err "Non-LUKS device decryption failed. verify format: " - err " crypto=hash:cipher:keysize:offset:skip" - exit 1 - fi - if [ -e "/dev/mapper/${cryptname}" ]; then - if [ ${DEPRECATED_CRYPT} -eq 1 ]; then - export root="/dev/mapper/root" - fi - else - err "Password succeeded, but ${cryptname} creation failed, aborting..." - exit 1 - fi + if [ $tpmok -gt 0 ]; then + msg ":: Keyfile successfully decrypted" else - err "Failed to open encryption mapping: The device ${cryptdev} is not a LUKS volume and the crypto= paramater was not specified." + rm -f "/crypto_keyfile.bin" + echo "TPM Could not decrypt keyfile" fi + + killall -q tcsd fi - rm -f ${ckeyfile} +} + +run_cleanuphook() { + rm -f "/crypto_keyfile.bin" } # vim: set ft=sh ts=4 sw=4 et: @@ -1,2 +1,2 @@ -127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 -::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 +127.0.0.1 localhost.localdomain localhost +::1 localhost.localdomain localhost diff --git a/install_tpm b/install_tpm index 3fc5f3308a38..aeb5cd103a76 100644 --- a/install_tpm +++ b/install_tpm @@ -1,56 +1,29 @@ #!/bin/bash build() { - local mod - add_module "tpm_tis" add_binary "/usr/bin/tpm_unsealdata" "/usr/bin/tpm_unsealdata" - add_binary "/usr/sbin/tpm_version" "/usr/bin/tpm_version" + add_binary "/usr/bin/tpm_version" "/usr/bin/tpm_version" add_binary "/usr/sbin/tcsd" "/usr/bin/tcsd" - + add_dir "/var/lib/tpm" add_file "/var/lib/tpm/system.data" - add_file "/usr/lib/initcpio/tpm/hosts" "/etc/hosts" + add_file "/usr/lib/initcpio/tpm/hosts" "/etc/hosts" add_file "/usr/lib/initcpio/tpm/passwd" "/etc/passwd" - add_file "/usr/lib/initcpio/tpm/shadow" "/etc/shadow" add_file "/usr/lib/libnss_files.so.2" - add_file "/etc/nsswitch.conf" - - add_module dm-crypt - if [[ $CRYPTO_MODULES ]]; then - for mod in $CRYPTO_MODULES; do - add_module "$mod" - done - else - add_all_modules '/crypto/' - fi - - add_binary "cryptsetup" - add_binary "dmsetup" - add_file "/usr/lib/udev/rules.d/10-dm.rules" - add_file "/usr/lib/udev/rules.d/13-dm-disk.rules" - add_file "/usr/lib/udev/rules.d/95-dm-notify.rules" - add_file "/usr/lib/initcpio/udev/11-dm-initramfs.rules" "/usr/lib/udev/rules.d/11-dm-initramfs.rules" add_runscript } help() { cat <<HELPEOF -This hook allows for an encrypted root device. Users should specify the device -to be unlocked using 'cryptdevice=device:dmname' on the kernel command line, -where 'device' is the path to the raw device, and 'dmname' is the name given to -the device after unlocking, and will be available as /dev/mapper/dmname. - -For unlocking via keyfile, 'cryptkey=device:fstype:path' should be specified on -the kernel cmdline, where 'device' represents the raw block device where the key -exists, 'fstype' is the filesystem type of 'device' (or auto), and 'path' is -the absolute path of the keyfile within the device. +This hook allows for an encrypted root device to use a key file sealed by the +TPM. It should be placed immediately before the 'encrypt' hook. -Without specifying a keyfile, you will be prompted for the password at runtime. -This means you must have a keyboard available to input it, and you may need -the keymap hook as well to ensure that the keyboard is using the layout you -expect. +After generating a TPM-sealed keyfile, 'tpmkey=device:fstype:path' should be +specified on the kernel cmdline, where 'device' represents the raw block device +where the key exists, 'fstype' is the filesystem type of 'device' (or auto), +and 'path' is the absolute path of the keyfile within the device. HELPEOF } diff --git a/shadow b/shadow deleted file mode 100644 index f64c6eabe0ab..000000000000 --- a/shadow +++ /dev/null @@ -1,2 +0,0 @@ -root:*:14715:0:99999:7::: -tss:!!:15217:::::: |