diff --git a/.clevis.metadata b/.clevis.metadata new file mode 100644 index 0000000..0964fdd --- /dev/null +++ b/.clevis.metadata @@ -0,0 +1 @@ +70cc377c0976fb32fd55a3033f2374080b713b5d SOURCES/clevis-7.tar.bz2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..695fe37 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/clevis-7.tar.bz2 diff --git a/SOURCES/clevis-7-dracut.patch b/SOURCES/clevis-7-dracut.patch new file mode 100644 index 0000000..beb8941 --- /dev/null +++ b/SOURCES/clevis-7-dracut.patch @@ -0,0 +1,61 @@ +From bcb46fe1e40b390cb39353b75806bf3e05177ef0 Mon Sep 17 00:00:00 2001 +From: Nathaniel McCallum +Date: Mon, 13 Nov 2017 11:28:14 -0500 +Subject: [PATCH] Fix dracut unlocker + +We weren't generating the path to clevis-luks-askpass correctly in the +dracut module. + +Fixes: #23 +--- + src/dracut/Makefile.am | 10 ++++------ + src/dracut/clevis-hook.sh.in | 2 +- + src/dracut/{module-setup.sh => module-setup.sh.in} | 2 +- + 3 files changed, 6 insertions(+), 8 deletions(-) + rename src/dracut/{module-setup.sh => module-setup.sh.in} (96%) + +diff --git a/src/dracut/Makefile.am b/src/dracut/Makefile.am +index 5a3a0f4..e26b61f 100644 +--- a/src/dracut/Makefile.am ++++ b/src/dracut/Makefile.am +@@ -1,12 +1,10 @@ + dracutdir = @dracutmodulesdir@/60$(PACKAGE_NAME) +-nodist_dracut_SCRIPTS = clevis-hook.sh +-dist_dracut_SCRIPTS = module-setup.sh +- +-CLEANFILES=clevis-hook.sh +-EXTRA_DIST=clevis-hook.sh.in ++nodist_dracut_SCRIPTS = clevis-hook.sh module-setup.sh ++EXTRA_DIST=clevis-hook.sh.in module-setup.sh.in ++CLEANFILES=clevis-hook.sh module-setup.sh + + %: %.in + $(AM_V_GEN)mkdir -p $(dir $@) + $(AM_V_GEN)$(SED) \ +- -e 's,@libexedir\@,$(libexecdir),g' \ ++ -e 's,@libexecdir\@,$(libexecdir),g' \ + $(srcdir)/$@.in > $@ +diff --git a/src/dracut/clevis-hook.sh.in b/src/dracut/clevis-hook.sh.in +index 5d0c814..cb257c9 100755 +--- a/src/dracut/clevis-hook.sh.in ++++ b/src/dracut/clevis-hook.sh.in +@@ -1,2 +1,2 @@ + #!/bin/bash +-@libexec@/clevis-luks-askpass ++@libexecdir@/clevis-luks-askpass +diff --git a/src/dracut/module-setup.sh b/src/dracut/module-setup.sh.in +similarity index 96% +rename from src/dracut/module-setup.sh +rename to src/dracut/module-setup.sh.in +index 92fe08e..5087d56 100755 +--- a/src/dracut/module-setup.sh ++++ b/src/dracut/module-setup.sh.in +@@ -37,7 +37,7 @@ install() { + clevis-decrypt-http \ + clevis-decrypt-tang \ + clevis-decrypt-sss \ +- clevis-luks-askpass \ ++ @libexecdir@/clevis-luks-askpass \ + clevis-decrypt \ + luksmeta \ + clevis \ 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-retry.patch b/SOURCES/clevis-7-retry.patch new file mode 100644 index 0000000..2826d72 --- /dev/null +++ b/SOURCES/clevis-7-retry.patch @@ -0,0 +1,103 @@ +From 2a82ba4040c8dc10dcbe7e2c3ae6646c2778f0b1 Mon Sep 17 00:00:00 2001 +From: Nathaniel McCallum +Date: Tue, 16 Jan 2018 13:29:54 -0500 +Subject: [PATCH] Retry until success during systemd boot + +With dracut, we just try once because we're being called in a loop. But with +systemd, there might be a race condition for network to come up. So when +running under systemd, we loop until success. This should not change the dracut +behavior. +--- + src/systemd/clevis-luks-askpass | 66 ++++++++++++++++++++---------- + src/systemd/clevis-luks-askpass.service.in | 2 +- + 2 files changed, 46 insertions(+), 22 deletions(-) + +diff --git a/src/systemd/clevis-luks-askpass b/src/systemd/clevis-luks-askpass +index 2fadd5c..6fe5269 100755 +--- a/src/systemd/clevis-luks-askpass ++++ b/src/systemd/clevis-luks-askpass +@@ -23,26 +23,50 @@ UUID=cb6e8904-81ff-40da-a84a-07ab9ab5715e + + shopt -s nullglob + +-for question in /run/systemd/ask-password/ask.*; do +- d= +- s= +- +- while read line; do +- case "$line" in +- Id=cryptsetup:*) d="${line##Id=cryptsetup:}";; +- Socket=*) s="${line##Socket=}";; +- esac +- done < "$question" +- +- [ -z "$d" -o -z "$s" ] && continue +- +- luksmeta show -d "$d" | while read -r slot state uuid; do +- [ "$state" != "active" ] && continue +- [ "$uuid" != "$UUID" ] && continue +- +- if pt="`luksmeta load -d $d -s $slot -u $UUID | clevis decrypt`"; then +- echo -n "+$pt" | nc -U -u --send-only "$s" +- break +- fi ++while getopts ":l" o; do ++ case "$o" in ++ l) loop=true;; ++ esac ++done ++ ++while true; do ++ todo=0 ++ ++ for question in /run/systemd/ask-password/ask.*; do ++ metadata=false ++ unlocked=false ++ d= ++ s= ++ ++ while read line; do ++ case "$line" in ++ Id=cryptsetup:*) d="${line##Id=cryptsetup:}";; ++ Socket=*) s="${line##Socket=}";; ++ esac ++ done < "$question" ++ ++ [ -z "$d" -o -z "$s" ] && continue ++ ++ while read -r slot state uuid; do ++ [ "$state" != "active" ] && continue ++ [ "$uuid" != "$UUID" ] && continue ++ metadata=true ++ ++ if pt="`luksmeta load -d $d -s $slot -u $UUID | clevis decrypt`"; then ++ echo -n "+$pt" | nc -U -u --send-only "$s" ++ unlocked=true ++ break ++ fi ++ done < <(luksmeta show -d "$d") ++ ++ [ $metadata == true ] || continue ++ [ $unlocked == true ] && continue ++ todo=$((todo + 1)) + done ++ ++ if [ $todo -eq 0 ] || [ "$loop" != "true" ]; then ++ break; ++ fi ++ ++ sleep 0.5 + done +diff --git a/src/systemd/clevis-luks-askpass.service.in b/src/systemd/clevis-luks-askpass.service.in +index aa38a5b..2c6bbed 100644 +--- a/src/systemd/clevis-luks-askpass.service.in ++++ b/src/systemd/clevis-luks-askpass.service.in +@@ -5,4 +5,4 @@ After=network-online.target + + [Service] + Type=oneshot +-ExecStart=@libexecdir@/clevis-luks-askpass ++ExecStart=@libexecdir@/clevis-luks-askpass -l +-- +2.14.3 + 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 new file mode 100644 index 0000000..5662291 --- /dev/null +++ b/SPECS/clevis.spec @@ -0,0 +1,218 @@ +%global _hardened_build 1 + +Name: clevis +Version: 7 +Release: 8%{?dist} +Summary: Automated decryption framework + +License: GPLv3+ +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 +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 +BuildRequires: automake +BuildRequires: systemd +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 +Requires(pre): shadow-utils + +%description +Clevis is a framework for automated decryption. It allows you to encrypt +data using sophisticated unlocking policies which enable decryption to +occur automatically. + +The clevis package provides basic encryption/decryption policy support. +Users can use this directly; but most commonly, it will be used as a +building block for other packages. For example, see the clevis-luks +and clevis-dracut packages for automatic root volume unlocking of LUKSv1 +volumes during early boot. + +%package luks +Summary: LUKSv1 integration for clevis +Requires: %{name}%{?_isa} = %{version}-%{release} +Requires: cryptsetup +Requires: luksmeta >= 8 + +%description luks +LUKSv1 integration for clevis. This package allows you to bind a LUKSv1 +volume to a clevis unlocking policy. For automated unlocking, an unlocker +will also be required. See, for example, clevis-dracut and clevis-udisks2. + +%package systemd +Summary: systemd integration for clevis +Requires: %{name}-luks%{?_isa} = %{version}-%{release} +Requires: systemd%{?_isa} >= 219-45.20171030 +Requires: nc + +%description systemd +Automatically unlocks LUKSv1 _netdev block devices from /etc/crypttab. + +%package dracut +Summary: Dracut integration for clevis +Requires: %{name}-systemd%{?_isa} = %{version}-%{release} +Requires: dracut-network + +%description dracut +Automatically unlocks LUKSv1 block devices in early boot. + +%package udisks2 +Summary: UDisks2/Storaged integration for clevis +Requires: %{name}-luks%{?_isa} = %{version}-%{release} + +%description udisks2 +Automatically unlocks LUKSv1 block devices in desktop environments that +use UDisks2 or storaged (like GNOME). + +%prep +%autosetup -p1 + +%build +autoreconf -if +%configure --enable-user=clevis --enable-group=clevis +%make_build V=1 + +%install +%make_install +ln -sf %{name}-luks-bind.1.gz %{buildroot}/%{_mandir}/man1/%{name}-bind-luks.1.gz + +%check +desktop-file-validate \ + %{buildroot}/%{_sysconfdir}/xdg/autostart/%{name}-luks-udisks2.desktop +%make_build check + +%pre +getent group %{name} >/dev/null || groupadd -r %{name} +getent passwd %{name} >/dev/null || \ + useradd -r -g %{name} -d %{_localstatedir}/cache/%{name} -s /sbin/nologin \ + -c "Clevis Decryption Framework unprivileged user" %{name} +exit 0 + +%files +%license COPYING +%{_bindir}/%{name}-decrypt-http +%{_bindir}/%{name}-decrypt-tang +%{_bindir}/%{name}-decrypt-sss +%{_bindir}/%{name}-decrypt +%{_bindir}/%{name}-encrypt-http +%{_bindir}/%{name}-encrypt-tang +%{_bindir}/%{name}-encrypt-sss +%{_bindir}/%{name} +%{_mandir}/man1/%{name}-encrypt-http.1* +%{_mandir}/man1/%{name}-encrypt-tang.1* +%{_mandir}/man1/%{name}-encrypt-sss.1* +%{_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* +%{_mandir}/man1/%{name}-luks-bind.1* +%{_mandir}/man1/%{name}-bind-luks.1* +%{_bindir}/%{name}-luks-unlock +%{_bindir}/%{name}-luks-bind +%{_bindir}/%{name}-bind-luks + +%files systemd +%{_libexecdir}/%{name}-luks-askpass +%{_unitdir}/%{name}-luks-askpass.path +%{_unitdir}/%{name}-luks-askpass.service + +%files dracut +%{_prefix}/lib/dracut/modules.d/60%{name} + +%files udisks2 +%{_sysconfdir}/xdg/autostart/%{name}-luks-udisks2.desktop +%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 + +* Mon Nov 13 2017 Nathaniel McCallum - 7-3 +- Add patch to fix path generation issues with dracut +- Resolves: rhbz#1512638 + +* Fri Nov 03 2017 Nathaniel McCallum - 7-2 +- Add man page symlink for the clevis-bind-luks => clevis-luks-bind +- Related: rhbz#1475406 + +* Fri Oct 27 2017 Nathaniel McCallum - 7-1 +- Update to v7 +- Resolves: rhbz#1467907 +- Resolves: rhbz#1467908 +- Resolves: rhbz#1475406 +- Resolves: rhbz#1500975 +- Resolves: rhbz#1478888 + +* Tue Jun 27 2017 Nathaniel McCallum - 6-1 +- New upstream release +- Specify unprivileged user/group during configuration +- Move clevis user/group creation to base clevis package + +* Mon Jun 26 2017 Nathaniel McCallum - 5-1 +- New upstream release +- Run clevis decryption from udisks2 under an unprivileged user +- Reenable udisks2 subpackage + +* Wed Jun 14 2017 Nathaniel McCallum - 4-1 +- New upstream release +- Disable udisks2 subpackage + +* Wed Jun 14 2017 Nathaniel McCallum - 3-1 +- New upstream release + +* Fri Feb 10 2017 Fedora Release Engineering - 2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Fri Nov 18 2016 Nathaniel McCallum - 2-1 +- New upstream release + +* Mon Nov 14 2016 Nathaniel McCallum - 1-1 +- First release