diff --git a/SOURCES/clevis-7-nospam.patch b/SOURCES/clevis-7-nospam.patch new file mode 100644 index 0000000..363c4f3 --- /dev/null +++ b/SOURCES/clevis-7-nospam.patch @@ -0,0 +1,28 @@ +From e5170e17cac33706836f8b24e0a88e49cd0a47ef Mon Sep 17 00:00:00 2001 +From: Nathaniel McCallum +Date: Tue, 13 Feb 2018 13:33:57 -0500 +Subject: [PATCH] Skip uninitialized devices in clevis-luks-askpass + +This silences some log spam when using clevis while also having some +non-clevis LUKS volumes in the same system. +--- + src/systemd/clevis-luks-askpass | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/systemd/clevis-luks-askpass b/src/systemd/clevis-luks-askpass +index 6fe5269..93c59f4 100755 +--- a/src/systemd/clevis-luks-askpass ++++ b/src/systemd/clevis-luks-askpass +@@ -47,6 +47,9 @@ while true; do + + [ -z "$d" -o -z "$s" ] && continue + ++ # If the device is not initialized, sliently skip it. ++ luksmeta test -d "$d" || continue ++ + while read -r slot state uuid; do + [ "$state" != "active" ] && continue + [ "$uuid" != "$UUID" ] && continue +-- +2.17.1 + diff --git a/SOURCES/clevis-7-subshell.patch b/SOURCES/clevis-7-subshell.patch new file mode 100644 index 0000000..f9a905c --- /dev/null +++ b/SOURCES/clevis-7-subshell.patch @@ -0,0 +1,44 @@ +From 7d19c76bfc9a7b569a1077d1e5673a28bf31606f Mon Sep 17 00:00:00 2001 +From: Javier Martinez Canillas +Date: Tue, 6 Mar 2018 13:59:34 +0100 +Subject: [PATCH] Don't execute clevis-luks-unlock while loop in a subshell + +The loop that tries to open the dm-crypt devices using the pins in the +luksmeta header is executed in a subshell. So on success it calls exit +to exit the subshell. + +But then clevis-luks-unlock has no way to know if the encrypted device +was opened correctly or not. So run the loop in the main shell process +and return 0 as exit status if the operation was successful. + +Fixes: #36 + +Signed-off-by: Javier Martinez Canillas +--- + src/clevis-luks-unlock | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/clevis-luks-unlock b/src/clevis-luks-unlock +index 6396680..7d316cd 100755 +--- a/src/clevis-luks-unlock ++++ b/src/clevis-luks-unlock +@@ -54,7 +54,7 @@ fi + + NAME=${NAME:-luks-`cryptsetup luksUUID $DEV`} + +-luksmeta show -d "$DEV" | while read -r slot state uuid; do ++while read -r slot state uuid; do + [ "$state" != "active" ] && continue + [ "$uuid" != "$UUID" ] && continue + +@@ -62,6 +62,6 @@ luksmeta show -d "$DEV" | while read -r slot state uuid; do + echo -n "$pt" | cryptsetup open -d- "$DEV" "$NAME" + exit 0 + fi +-done ++done <<< "$(luksmeta show -d "$DEV")" + + exit 1 +-- +2.17.1 + diff --git a/SOURCES/clevis-7-tpm2.patch b/SOURCES/clevis-7-tpm2.patch new file mode 100644 index 0000000..a6b1ca6 --- /dev/null +++ b/SOURCES/clevis-7-tpm2.patch @@ -0,0 +1,734 @@ +From fb3c135906cab203cfd11b3cb4a502ef8530987a Mon Sep 17 00:00:00 2001 +From: Nathaniel McCallum +Date: Mon, 9 Jul 2018 08:47:14 +0200 +Subject: [PATCH] Add support for TPM 2.0 + +--- + Makefile.am | 5 ++ + configure.ac | 11 +++ + doc/clevis-encrypt-tpm2.1 | 142 +++++++++++++++++++++++++++++++ + doc/clevis-encrypt-tpm2.1.md | 108 +++++++++++++++++++++++ + doc/clevis.1 | 40 ++++++++- + src/Makefile.am | 6 ++ + src/clevis-decrypt-tpm2 | 126 +++++++++++++++++++++++++++ + src/clevis-encrypt-tpm2 | 156 ++++++++++++++++++++++++++++++++++ + src/dracut/module-setup.sh.in | 23 +++++ + 9 files changed, 615 insertions(+), 2 deletions(-) + create mode 100644 doc/clevis-encrypt-tpm2.1 + create mode 100644 doc/clevis-encrypt-tpm2.1.md + create mode 100755 src/clevis-decrypt-tpm2 + create mode 100755 src/clevis-encrypt-tpm2 + +diff --git a/Makefile.am b/Makefile.am +index 141642a..75f9745 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -14,3 +14,8 @@ dist_man1_MANS = \ + doc/clevis-luks-bind.1 \ + doc/clevis-decrypt.1 \ + doc/clevis.1 ++ ++if HAVE_TPM2_TOOLS ++ dist_man1_MANS += \ ++ doc/clevis-encrypt-tpm2.1 ++endif +diff --git a/configure.ac b/configure.ac +index f9ed01a..313f64e 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -54,6 +54,17 @@ fi + + AC_SUBST(SD_ACTIVATE) + ++for ac_prog in createprimary pcrlist createpolicy create load unseal; do ++ unset TPM2_TOOLS ++ unset ac_cv_prog_TPM2_TOOLS ++ AC_CHECK_PROG([TPM2_TOOLS], [tpm2_$ac_prog], [yes]) ++ test -z "$TPM2_TOOLS" && break ++done ++ ++test -n "$TPM2_TOOLS" || AC_MSG_WARN([tpm2_$ac_prog not found, tpm2 pin won't be installed]) ++ ++AM_CONDITIONAL([HAVE_TPM2_TOOLS], [test -n "$TPM2_TOOLS"]) ++ + AC_ARG_ENABLE([user], + AS_HELP_STRING([--enable-user=USER], + [Set unprivileged user (default: root)]), +diff --git a/doc/clevis-encrypt-tpm2.1 b/doc/clevis-encrypt-tpm2.1 +new file mode 100644 +index 0000000..096ad23 +--- /dev/null ++++ b/doc/clevis-encrypt-tpm2.1 +@@ -0,0 +1,142 @@ ++.\" Automatically generated by Pandoc 1.19.1 ++.\" ++.TH "CLEVIS\-ENCRYPT\-TPM2" "1" "November 2017" "" "" ++.hy ++.SH NAME ++.PP ++clevis\-encrypt\-tpm2 \-\- Encrypts using a TPM2.0 chip binding policy ++.SH SYNOPSIS ++.PP ++\f[C]clevis\ encrypt\ tpm2\f[] CONFIG < PT > JWE ++.SH OVERVIEW ++.PP ++The \f[C]clevis\ encrypt\ tpm2\f[] command encrypts using a Trusted ++Platform Module 2.0 (TPM2) chip. ++Its only argument is the JSON configuration object. ++.PP ++When using the tpm2 pin, we create a new, cryptographically\-strong, ++random key. ++This key is encrypted using the TPM2 chip. ++Then at decryption time, the key is decrypted again using the TPM2 chip. ++.IP ++.nf ++\f[C] ++$\ clevis\ encrypt\ tpm2\ \[aq]{}\[aq]\ <\ PT\ >\ JWE ++\f[] ++.fi ++.PP ++The pin has reasonable defaults for its configuration, but a different ++hierarchy, hash, and key algorithms can be chosen if the defaults used ++are not suitable: ++.IP ++.nf ++\f[C] ++$\ clevis\ encrypt\ tpm2\ \[aq]{"hash":"sha1","key":"rsa"}\[aq]\ <\ PT\ >\ JWE ++\f[] ++.fi ++.PP ++To decrypt the data, simply provide the ciphertext (JWE): ++.IP ++.nf ++\f[C] ++$\ clevis\ decrypt\ <\ JWE\ >\ PT ++\f[] ++.fi ++.PP ++Note that like other pins no configuration is used for decryption, this ++is due clevis storing the public and private keys to unseal the TPM2 ++encrypted object in the JWE so clevis can fetch that information from ++there. ++.PP ++The pin also supports sealing data to a Platform Configuration Registers ++(PCR) state. ++That way the data can only be unsealed if the PCRs hashes values match ++the policy used when sealing. ++.PP ++For example, to seal the data to the PCR with index 0 and 1 for the SHA1 ++bank: ++.IP ++.nf ++\f[C] ++$\ clevis\ encrypt\ tpm2\ \[aq]{"pcr_bank":"sha1","pcr_ids":"0,1"}\[aq]\ <\ PT\ >\ JWE ++\f[] ++.fi ++.PP ++The PCR digest values are looked up from the current hash values for the ++PCRs, but a digest can also be provided if the data needs to be sealed ++with values different to the current ones, by passing the binary hash ++encoded in base64: ++.IP ++.nf ++\f[C] ++$\ clevis\ encrypt\ tpm2\ \[aq]{"pcr_ids":"0","pcr_digest":"xy7J5svCtqlfM03d1lE5gdoA8MI"}\[aq]\ <\ PT\ >\ JWE ++\f[] ++.fi ++.SH Threat model ++.PP ++The Clevis security model relies in the fact that an attacker will not ++be able to access both the encrypted data and the decryption key. ++.PP ++For most Clevis pins, the decryption key is not locally stored, so the ++decryption policy is only satisfied if the decryption key can be ++remotely accessed. ++It could for example be stored in a remote server or in a hardware ++authentication device that has to be plugged into the machine. ++.PP ++The tpm2 pin is different in this regard, since a key is wrapped by a ++TPM2 chip that is always present in the machine. ++This does not mean that there are not use cases for this pin, but it is ++important to understand the fact that an attacker that has access to ++both the encrypted data and the local TPM2 chip will be able to decrypt ++the data. ++.SH CONFIG ++.PP ++This command uses the following configuration properties: ++.IP \[bu] 2 ++\f[C]hash\f[] (string) : Hash algorithm used in the computation of the ++object name (default: sha256) ++.PP ++It must be one of the following: ++.IP \[bu] 2 ++\f[C]sha1\f[] ++.IP \[bu] 2 ++\f[C]sha256\f[] ++.IP \[bu] 2 ++\f[C]sha384\f[] ++.IP \[bu] 2 ++\f[C]sha512\f[] ++.IP \[bu] 2 ++\f[C]sm3_256\f[] ++.IP \[bu] 2 ++\f[C]key\f[] (string) : Algorithm type for the generated key (default: ++ecc) ++.PP ++It must be one of the following: ++.IP \[bu] 2 ++\f[C]rsa\f[] ++.IP \[bu] 2 ++\f[C]keyedhash\f[] ++.IP \[bu] 2 ++\f[C]ecc\f[] ++.IP \[bu] 2 ++\f[C]symcipher\f[] ++.IP \[bu] 2 ++\f[C]pcr_bank\f[] (string) : PCR algorithm bank to use for policy ++(default: sha1) ++.PP ++It must be one of the following: ++.IP \[bu] 2 ++\f[C]sha1\f[] ++.IP \[bu] 2 ++\f[C]sha256\f[] ++.IP \[bu] 2 ++\f[C]pcr_ids\f[] (string) : Comma separated list of PCR used for policy. ++If not present, no policy is used ++.IP \[bu] 2 ++\f[C]pcr_digest\f[] (string) : Binary PCR hashes encoded in base64. ++If not present, the hash values are looked up ++.SH SEE ALSO ++.PP ++\f[C]clevis\-decrypt\f[](1) ++.SH AUTHORS ++Javier Martinez Canillas . +diff --git a/doc/clevis-encrypt-tpm2.1.md b/doc/clevis-encrypt-tpm2.1.md +new file mode 100644 +index 0000000..f533d67 +--- /dev/null ++++ b/doc/clevis-encrypt-tpm2.1.md +@@ -0,0 +1,108 @@ ++% CLEVIS-ENCRYPT-TPM2(1) ++% Javier Martinez Canillas ++% November 2017 ++ ++# NAME ++ ++clevis-encrypt-tpm2 -- Encrypts using a TPM2.0 chip binding policy ++ ++# SYNOPSIS ++ ++`clevis encrypt tpm2` CONFIG < PT > JWE ++ ++# OVERVIEW ++ ++The `clevis encrypt tpm2` command encrypts using a Trusted Platform Module 2.0 ++(TPM2) chip. Its only argument is the JSON configuration object. ++ ++When using the tpm2 pin, we create a new, cryptographically-strong, random key. ++This key is encrypted using the TPM2 chip. ++Then at decryption time, the key is decrypted again using the TPM2 chip. ++ ++ $ clevis encrypt tpm2 '{}' < PT > JWE ++ ++The pin has reasonable defaults for its configuration, but a different hierarchy, ++hash, and key algorithms can be chosen if the defaults used are not suitable: ++ ++ $ clevis encrypt tpm2 '{"hash":"sha1","key":"rsa"}' < PT > JWE ++ ++To decrypt the data, simply provide the ciphertext (JWE): ++ ++ $ clevis decrypt < JWE > PT ++ ++Note that like other pins no configuration is used for decryption, this is due ++clevis storing the public and private keys to unseal the TPM2 encrypted object ++in the JWE so clevis can fetch that information from there. ++ ++The pin also supports sealing data to a Platform Configuration Registers (PCR) ++state. That way the data can only be unsealed if the PCRs hashes values match ++the policy used when sealing. ++ ++For example, to seal the data to the PCR with index 0 and 1 for the SHA1 bank: ++ ++ $ clevis encrypt tpm2 '{"pcr_bank":"sha1","pcr_ids":"0,1"}' < PT > JWE ++ ++The PCR digest values are looked up from the current hash values for the PCRs, ++but a digest can also be provided if the data needs to be sealed with values ++different to the current ones, by passing the binary hash encoded in base64: ++ ++ $ clevis encrypt tpm2 '{"pcr_ids":"0","pcr_digest":"xy7J5svCtqlfM03d1lE5gdoA8MI"}' < PT > JWE ++ ++# Threat model ++ ++The Clevis security model relies in the fact that an attacker will not be able to ++access both the encrypted data and the decryption key. ++ ++For most Clevis pins, the decryption key is not locally stored, so the decryption ++policy is only satisfied if the decryption key can be remotely accessed. It could ++for example be stored in a remote server or in a hardware authentication device ++that has to be plugged into the machine. ++ ++The tpm2 pin is different in this regard, since a key is wrapped by a TPM2 chip ++that is always present in the machine. This does not mean that there are not use ++cases for this pin, but it is important to understand the fact that an attacker ++that has access to both the encrypted data and the local TPM2 chip will be able ++to decrypt the data. ++ ++# CONFIG ++ ++This command uses the following configuration properties: ++ ++* `hash` (string) : ++ Hash algorithm used in the computation of the object name (default: sha256) ++ ++ It must be one of the following: ++ ++ * `sha1` ++ * `sha256` ++ * `sha384` ++ * `sha512` ++ * `sm3_256` ++ ++* `key` (string) : ++ Algorithm type for the generated key (default: ecc) ++ ++ It must be one of the following: ++ ++ * `rsa` ++ * `keyedhash` ++ * `ecc` ++ * `symcipher` ++ ++* `pcr_bank` (string) : ++ PCR algorithm bank to use for policy (default: sha1) ++ ++ It must be one of the following: ++ ++ * `sha1` ++ * `sha256` ++ ++* `pcr_ids` (string) : ++ Comma separated list of PCR used for policy. If not present, no policy is used ++ ++* `pcr_digest` (string) : ++ Binary PCR hashes encoded in base64. If not present, the hash values are looked up ++ ++# SEE ALSO ++ ++`clevis-decrypt`(1) +diff --git a/doc/clevis.1 b/doc/clevis.1 +index 3d4a10d..0937533 100644 +--- a/doc/clevis.1 ++++ b/doc/clevis.1 +@@ -80,6 +80,42 @@ $\ clevis\ decrypt\ <\ JWE\ >\ PT + .fi + .PP + For more information, see \f[C]clevis\-encrypt\-tang\f[](1). ++.SH TPM2 BINDING ++.PP ++Clevis provides support to encrypt a key in a Trusted Platform Module ++2.0 (TPM2) chip. ++The cryptographically\-strong, random key used for encryption is ++encrypted using the TPM2 chip, and then at decryption time is decrypted ++using the TPM2 to allow clevis to decrypt the secret stored in the JWE. ++.PP ++Encrypting data using the tpm2 pin works the same than the pins ++mentioned above: ++.IP ++.nf ++\f[C] ++$\ clevis\ encrypt\ tpm2\ \[aq]{}\[aq]\ <\ PT\ >\ JWE ++\f[] ++.fi ++.PP ++The pin has reasonable defaults for its configuration, but a different ++hierarchy, hash, and key algorithms can be chosen if the defaults used ++are not suitable. ++.PP ++Decryption also works similar to other pins, only the JWE needs to be ++provided: ++.IP ++.nf ++\f[C] ++$\ clevis\ decrypt\ <\ JWE\ >\ PT ++\f[] ++.fi ++.PP ++Note that like other pins no configuration is used for decryption, this ++is due clevis storing the public and private keys to unseal the TPM2 ++encrypted object in the JWE so clevis can fetch that information from ++there. ++.PP ++For more information see \f[C]clevis\-encrypt\-tpm2\f[](1). + .SH SHAMIR\[aq]S SECRET SHARING + .PP + Clevis provides a way to mix pins together to create sophisticated +@@ -151,7 +187,7 @@ For more information, see \f[C]clevis\-luks\-bind\f[](1). + .SH SEE ALSO + .PP + \f[C]clevis\-encrypt\-http\f[](1), \f[C]clevis\-encrypt\-tang\f[](1), +-\f[C]clevis\-encrypt\-sss\f[](1), \f[C]clevis\-luks\-bind\f[](1), +-\f[C]clevis\-decrypt\f[](1) ++\f[C]clevis\-encrypt\-tpm2\f[](1), \f[C]clevis\-encrypt\-sss\f[](1), ++\f[C]clevis\-luks\-bind\f[](1), \f[C]clevis\-decrypt\f[](1) + .SH AUTHORS + Nathaniel McCallum . +diff --git a/src/Makefile.am b/src/Makefile.am +index 244874b..8562502 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -25,6 +25,12 @@ dist_bin_SCRIPTS = \ + clevis-decrypt \ + clevis + ++if HAVE_TPM2_TOOLS ++ dist_bin_SCRIPTS += \ ++ clevis-encrypt-tpm2 \ ++ clevis-decrypt-tpm2 ++endif ++ + clevis_encrypt_sss_SOURCES = clevis-encrypt-sss.c sss.c sss.h + clevis_decrypt_sss_SOURCES = clevis-decrypt-sss.c sss.c sss.h + clevis_encrypt_sss_LDADD = @jose_LIBS@ @libcrypto_LIBS@ +diff --git a/src/clevis-decrypt-tpm2 b/src/clevis-decrypt-tpm2 +new file mode 100755 +index 0000000..f3871d8 +--- /dev/null ++++ b/src/clevis-decrypt-tpm2 +@@ -0,0 +1,126 @@ ++#!/bin/bash -e ++# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2017 Red Hat, Inc. ++# Author: Javier Martinez Canillas ++# ++# This program is free software: you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation, either version 3 of the License, or ++# (at your option) any later version. ++# ++# This program 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. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++# ++ ++# The owner hierarchy is the one that should be used by the Operating System. ++auth="o" ++ ++function on_exit() { ++ if ! rm -r $TMP; then ++ echo "Delete temporary files failed!" >&2 ++ exit 1 ++ fi ++} ++ ++[ $# -eq 1 -a "$1" == "--summary" ] && exit 1 ++ ++if [ -t 0 ]; then ++ echo >&2 ++ echo "Usage: clevis decrypt tpm2 < JWE > PLAINTEXT" >&2 ++ echo >&2 ++ exit 1 ++fi ++ ++export TPM2TOOLS_TCTI_NAME=device ++export TPM2TOOLS_DEVICE_FILE=`ls /dev/tpmrm? 2>/dev/null` ++ ++if [ -z "${TPM2TOOLS_DEVICE_FILE[0]}" ]; then ++ echo "A TPM2 device with the in-kernel resource manager is needed!" >&2 ++ exit 1 ++fi ++ ++if ! [[ -r "${TPM2TOOLS_DEVICE_FILE[0]}" && -w "${TPM2TOOLS_DEVICE_FILE[0]}" ]]; then ++ echo "The ${TPM2TOOLS_DEVICE_FILE[0]} device must be readable and writable!" >&2 ++ exit 1 ++fi ++ ++read -d . hdr ++ ++if ! jhd=`jose b64 dec -i- <<< "$hdr"`; then ++ echo "Error decoding JWE protected header!" >&2 ++ exit 1 ++fi ++ ++if [ `jose fmt -j- -Og clevis -g pin -u- <<< "$jhd"` != "tpm2" ]; then ++ echo "JWE pin mismatch!" >&2 ++ exit 1 ++fi ++ ++if ! hash=`jose fmt -j- -Og clevis -g tpm2 -g hash -Su- <<< "$jhd"`; then ++ echo "JWE missing required 'hash' header parameter!" >&2 ++ exit 1 ++fi ++ ++if ! key=`jose fmt -j- -Og clevis -g tpm2 -g key -Su- <<< "$jhd"`; then ++ echo "JWE missing required 'key' header parameter!" >&2 ++ exit 1 ++fi ++ ++if ! jwk_pub=`jose fmt -j- -Og clevis -g tpm2 -g jwk_pub -Su- <<< "$jhd"`; then ++ echo "JWE missing required 'key' header parameter!" >&2 ++ exit 1 ++fi ++ ++if ! jwk_priv=`jose fmt -j- -Og clevis -g tpm2 -g jwk_priv -Su- <<< "$jhd"`; then ++ echo "JWE missing required 'key' header parameter!" >&2 ++ exit 1 ++fi ++ ++if ! TMP=`mktemp -d`; then ++ echo "Creating a temporary dir for TPM files failed!" >&2 ++ exit 1 ++fi ++ ++trap 'on_exit' EXIT ++ ++pcr_ids=`jose fmt -j- -Og clevis -g tpm2 -g pcr_ids -Su- <<< "$jhd"` || true ++ ++if [ -n "$pcr_ids" ]; then ++ pcr_bank=`jose fmt -j- -Og clevis -g tpm2 -g pcr_bank -Su- <<< "$jhd"` ++ policy_options="-L $pcr_bank:$pcr_ids" ++fi ++ ++if ! `jose b64 dec -i- -O $TMP/jwk.pub <<< "$jwk_pub"`; then ++ echo "Decoding jwk.pub from Base64 failed!" >&2 ++ exit 1 ++fi ++ ++if ! `jose b64 dec -i- -O $TMP/jwk.priv <<< "$jwk_priv"`; then ++ echo "Decoding jwk.priv from Base64 failed!" >&2 ++ exit 1 ++fi ++ ++if ! tpm2_createprimary -Q -H "$auth" -g "$hash" -G "$key" \ ++ -C $TMP/primary.context 2>/dev/null; then ++ echo "Creating TPM2 primary key failed!" >&2 ++ exit 1 ++fi ++ ++if ! tpm2_load -Q -c $TMP/primary.context -u $TMP/jwk.pub -r $TMP/jwk.priv \ ++ -C $TMP/load.context 2>/dev/null; then ++ echo "Loading jwk to TPM2 failed!" >&2 ++ exit 1 ++fi ++ ++if ! jwk=`tpm2_unseal -c $TMP/load.context $policy_options 2>/dev/null`; then ++ echo "Unsealing jwk from TPM failed!" >&2 ++ exit 1 ++fi ++ ++jose jwe dec -k- -i- < <(echo -n "$jwk$hdr."; cat) +diff --git a/src/clevis-encrypt-tpm2 b/src/clevis-encrypt-tpm2 +new file mode 100755 +index 0000000..b99aa97 +--- /dev/null ++++ b/src/clevis-encrypt-tpm2 +@@ -0,0 +1,156 @@ ++#!/bin/bash -e ++# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2017 Red Hat, Inc. ++# Author: Javier Martinez Canillas ++# ++# This program is free software: you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation, either version 3 of the License, or ++# (at your option) any later version. ++# ++# This program 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. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++# ++ ++SUMMARY="Encrypts using a TPM2.0 chip binding policy" ++# The owner hierarchy is the one that should be used by the Operating System. ++auth="o" ++# Algorithm type must be keyedhash for object with user provided sensitive data. ++alg_create_key="keyedhash" ++# Attributes for the created TPM2 object with the JWK as sensitive data. ++obj_attr="fixedtpm|fixedparent|sensitivedataorigin|noda|adminwithpolicy" ++ ++function on_exit() { ++ if ! rm -rf $TMP; then ++ echo "Delete temporary files failed!" >&2 ++ exit 1 ++ fi ++} ++ ++if [ "$1" == "--summary" ]; then ++ echo "$SUMMARY" ++ exit 0 ++fi ++ ++if [ -t 0 ]; then ++ echo >&2 ++ echo "Usage: clevis encrypt tpm2 CONFIG < PLAINTEXT > JWE" >&2 ++ echo >&2 ++ echo $SUMMARY >&2 ++ echo >&2 ++ echo "This command uses the following configuration properties:" >&2 ++ echo >&2 ++ echo " hash: Hash algorithm used in the computation of the object name (default: sha256)" >&2 ++ echo >&2 ++ echo " key: Algorithm type for the generated key (default: ecc)" >&2 ++ echo >&2 ++ echo " pcr_bank: PCR algorithm bank to use for policy (default: sha1)" >&2 ++ echo >&2 ++ echo " pcr_ids: PCR list used for policy. If not present, no policy is used" >&2 ++ echo >&2 ++ echo " pcr_digest: Binary PCR hashes encoded in base64. If not present, the hash values are looked up" >&2 ++ echo >&2 ++ exit 1 ++fi ++ ++export TPM2TOOLS_TCTI_NAME=device ++export TPM2TOOLS_DEVICE_FILE=`ls /dev/tpmrm? 2>/dev/null` ++ ++if [ -z "${TPM2TOOLS_DEVICE_FILE[0]}" ]; then ++ echo "A TPM2 device with the in-kernel resource manager is needed!" >&2 ++ exit 1 ++fi ++ ++if ! [[ -r "${TPM2TOOLS_DEVICE_FILE[0]}" && -w "${TPM2TOOLS_DEVICE_FILE[0]}" ]]; then ++ echo "The ${TPM2TOOLS_DEVICE_FILE[0]} device must be readable and writable!" >&2 ++ exit 1 ++fi ++ ++if ! cfg=`jose fmt -j "$1" -Oo- 2>/dev/null`; then ++ echo "Configuration is malformed!" >&2 ++ exit 1 ++fi ++ ++hash=`jose fmt -j- -Og hash -u- <<< "$cfg"` || hash="sha256" ++ ++key=`jose fmt -j- -Og key -u- <<< "$cfg"` || key="ecc" ++ ++pcr_bank=`jose fmt -j- -Og pcr_hash -u- <<< "$cfg"` || pcr_bank="sha1" ++ ++pcr_ids=`jose fmt -j- -Og pcr_ids -u- <<< "$cfg"` || true ++ ++pcr_digest=`jose fmt -j- -Og pcr_digest -u- <<< "$cfg"` || true ++ ++if ! jwk=`jose jwk gen -i '{"alg":"A256GCM"}'`; then ++ echo "Generating a jwk failed!" >&2 ++ exit 1 ++fi ++ ++if ! TMP=`mktemp -d`; then ++ echo "Creating a temporary dir for TPM files failed!" >&2 ++ exit 1 ++fi ++ ++trap 'on_exit' EXIT ++ ++if ! tpm2_createprimary -Q -H "$auth" -g "$hash" -G "$key" -C $TMP/primary.context; then ++ echo "Creating TPM2 primary key failed!" >&2 ++ exit 1 ++fi ++ ++if [ -n "$pcr_ids" ]; then ++ if [ -z "$pcr_digest" ]; then ++ if ! tpm2_pcrlist -Q -L "$pcr_bank":"$pcr_ids" -o $TMP/pcr.digest; then ++ echo "Creating PCR hashes file failed!" >&2 ++ exit 1 ++ fi ++ else ++ if ! jose b64 dec -i- -O "$TMP"/pcr.digest <<< "$pcr_digest"; then ++ echo "Error decoding PCR digest!" >&2 ++ exit 1 ++ fi ++ fi ++ ++ if ! tpm2_createpolicy -Q -P -L "$pcr_bank":"$pcr_ids" -F $TMP/pcr.digest -f $TMP/pcr.policy; then ++ echo "create policy fail, please check the environment or parameters!" ++ exit 1 ++ fi ++ ++ policy_options="-L $TMP/pcr.policy" ++fi ++ ++if ! tpm2_create -Q -g "$hash" -G "$alg_create_key" -c $TMP/primary.context -u $TMP/jwk.pub \ ++ -r $TMP/jwk.priv -A "$obj_attr" $policy_options -I- <<< "$jwk"; then ++ echo "Creating TPM2 object for jwk failed!" >&2 ++ exit 1 ++fi ++ ++if ! jwk_pub=`jose b64 enc -I $TMP/jwk.pub`; then ++ echo "Encoding jwk.pub in Base64 failed!" >&2 ++ exit 1 ++fi ++ ++if ! jwk_priv=`jose b64 enc -I $TMP/jwk.priv`; then ++ echo "Encoding jwk.priv in Base64 failed!" >&2 ++ exit 1 ++fi ++ ++jwe='{"protected":{"clevis":{"pin":"tpm2","tpm2":{}}}}' ++jwe=`jose fmt -j "$jwe" -g protected -g clevis -g tpm2 -q "$hash" -s hash -UUUUo-` ++jwe=`jose fmt -j "$jwe" -g protected -g clevis -g tpm2 -q "$key" -s key -UUUUo-` ++ ++if [ -n "$pcr_ids" ]; then ++ jwe=`jose fmt -j "$jwe" -g protected -g clevis -g tpm2 -q "$pcr_bank" -s pcr_bank -UUUUo-` ++ jwe=`jose fmt -j "$jwe" -g protected -g clevis -g tpm2 -q "$pcr_ids" -s pcr_ids -UUUUo-` ++fi ++ ++jwe=`jose fmt -j "$jwe" -g protected -g clevis -g tpm2 -q "$jwk_pub" -s jwk_pub -UUUUo-` ++jwe=`jose fmt -j "$jwe" -g protected -g clevis -g tpm2 -q "$jwk_priv" -s jwk_priv -UUUUo-` ++ ++jose jwe enc -i- -k- -I- -c < <(echo -n "$jwe$jwk"; cat) +diff --git a/src/dracut/module-setup.sh.in b/src/dracut/module-setup.sh.in +index 5087d56..119762e 100755 +--- a/src/dracut/module-setup.sh.in ++++ b/src/dracut/module-setup.sh.in +@@ -28,6 +28,8 @@ cmdline() { + } + + install() { ++ local ret=0 ++ + cmdline > "${initdir}/etc/cmdline.d/99clevis.conf" + + inst_hook initqueue/online 60 "$moddir/clevis-hook.sh" +@@ -41,10 +43,31 @@ install() { + clevis-decrypt \ + luksmeta \ + clevis \ ++ mktemp \ + curl \ + jose \ + nc + ++ for cmd in clevis-decrypt-tpm2 \ ++ tpm2_createprimary \ ++ tpm2_unseal \ ++ tpm2_load; do ++ ++ if ! find_binary "$cmd" &>/dev/null; then ++ ((ret++)) ++ fi ++ done ++ ++ if (($ret == 0)); then ++ inst_multiple clevis-decrypt-tpm2 \ ++ tpm2_createprimary \ ++ tpm2_unseal \ ++ tpm2_load ++ fi ++ + dracut_need_initqueue + } + ++installkernel() { ++ hostonly='' instmods =drivers/char/tpm ++} +-- +2.17.1 + diff --git a/SPECS/clevis.spec b/SPECS/clevis.spec index c80b6d3..5662291 100644 --- a/SPECS/clevis.spec +++ b/SPECS/clevis.spec @@ -2,7 +2,7 @@ Name: clevis Version: 7 -Release: 4%{?dist} +Release: 8%{?dist} Summary: Automated decryption framework License: GPLv3+ @@ -10,6 +10,9 @@ URL: https://github.com/latchset/%{name} Source0: https://github.com/latchset/%{name}/releases/download/v%{version}/%{name}-%{version}.tar.bz2 Patch0: clevis-7-dracut.patch Patch1: clevis-7-retry.patch +Patch2: clevis-7-nospam.patch +Patch3: clevis-7-tpm2.patch +Patch4: clevis-7-subshell.patch BuildRequires: libjose-devel >= 8 BuildRequires: libluksmeta-devel >= 8 @@ -17,6 +20,9 @@ BuildRequires: audit-libs-devel >= 2.8.1 BuildRequires: libudisks2-devel BuildRequires: openssl-devel +%ifarch i686 x86_64 +BuildRequires: tpm2-tools >= 3.0.0 +%endif BuildRequires: desktop-file-utils BuildRequires: pkgconfig BuildRequires: autoconf @@ -26,6 +32,9 @@ BuildRequires: dracut BuildRequires: tang >= 6 BuildRequires: curl +%ifarch i686 x86_64 +Requires: tpm2-tools >= 3.0.0 +%endif Requires: coreutils Requires: jose >= 8 Requires: curl @@ -118,6 +127,12 @@ exit 0 %{_mandir}/man1/%{name}-decrypt.1* %{_mandir}/man1/%{name}.1* +%ifarch i686 x86_64 +%{_bindir}/%{name}-decrypt-tpm2 +%{_bindir}/%{name}-encrypt-tpm2 +%{_mandir}/man1/%{name}-encrypt-tpm2.1* +%endif + %files luks %{_mandir}/man1/%{name}-luks-unlockers.1* %{_mandir}/man1/%{name}-luks-unlock.1* @@ -140,6 +155,22 @@ exit 0 %attr(4755, root, root) %{_libexecdir}/%{name}-luks-udisks2 %changelog +* Mon Jul 30 2018 Nathaniel McCallum - 7-8 +- Fix quoting on older bash shells (from previous patch) +- Resolves: rhbz#1599728 + +* Tue Jul 24 2018 Nathaniel McCallum - 7-7 +- Backport subshell fix for clevis-luks-unlock +- Resolves: rhbz#1599728 + +* Mon Jul 09 2018 Nathaniel McCallum - 7-6 +- Backport TPM 2.0 support +- Resolves: rhbz#1472435 + +* Mon Jul 09 2018 Nathaniel McCallum - 7-5 +- Don't spam the log for uninitialized devices. +- Resolves: rhbz#1538759 + * Mon Nov 13 2017 Nathaniel McCallum - 7-4 - Retry unlocking under systemd. This prevents a race condition. - Resolves: rhbz#1475406