diff --git a/.clevis.metadata b/.clevis.metadata index 4602876..2d0caa9 100644 --- a/.clevis.metadata +++ b/.clevis.metadata @@ -1 +1 @@ -086374814a4d71db8625d27a1719e03244a7cff0 SOURCES/clevis-11.tar.xz +83aebcbe5792b43bf281b442f379cea08d7c43b0 SOURCES/clevis-13.tar.xz diff --git a/.gitignore b/.gitignore index f017bbb..a769e5f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/clevis-11.tar.xz +SOURCES/clevis-13.tar.xz diff --git a/SOURCES/0001-Adjust-pin-tang-test-to-account-for-newer-tang-witho.patch b/SOURCES/0001-Adjust-pin-tang-test-to-account-for-newer-tang-witho.patch new file mode 100644 index 0000000..8455003 --- /dev/null +++ b/SOURCES/0001-Adjust-pin-tang-test-to-account-for-newer-tang-witho.patch @@ -0,0 +1,84 @@ +From 27a27befed2257c2156ed8b94d679951b9b1a4d5 Mon Sep 17 00:00:00 2001 +From: Sergio Correia +Date: Wed, 13 May 2020 23:51:04 -0300 +Subject: [PATCH 1/8] Adjust pin-tang test to account for newer tang without + tangd-update + +--- + src/luks/tests/unbind-unbound-slot-luks2 | 1 + + src/pins/tang/meson.build | 8 +------- + src/pins/tang/pin-tang | 11 ++++++++--- + 3 files changed, 10 insertions(+), 10 deletions(-) + +diff --git a/src/luks/tests/unbind-unbound-slot-luks2 b/src/luks/tests/unbind-unbound-slot-luks2 +index 6a2aca5..6d814ad 100755 +--- a/src/luks/tests/unbind-unbound-slot-luks2 ++++ b/src/luks/tests/unbind-unbound-slot-luks2 +@@ -36,6 +36,7 @@ TMP="$(mktemp -d)" + + DEV="${TMP}/luks2-device" + new_device "luks2" "${DEV}" ++SLT=2 + if clevis luks unbind -d "${DEV}" -s "${SLT}"; then + error "${TEST}: Unbind is expected to fail for device ${DEV} and slot ${SLT}" >&2 + fi +diff --git a/src/pins/tang/meson.build b/src/pins/tang/meson.build +index 74a3442..9b9a3db 100644 +--- a/src/pins/tang/meson.build ++++ b/src/pins/tang/meson.build +@@ -9,12 +9,6 @@ kgen = find_program( + '/usr/lib/x86_64-linux-gnu/tangd-keygen', + required: false + ) +-updt = find_program( +- join_paths(libexecdir, 'tangd-update'), +- '/usr/libexec/tangd-update', +- '/usr/lib/x86_64-linux-gnu/tangd-update', +- required: false +-) + tang = find_program( + join_paths(libexecdir, 'tangd'), + '/usr/libexec/tangd', +@@ -28,7 +22,7 @@ if curl.found() + bins += join_paths(meson.current_source_dir(), 'clevis-encrypt-tang') + mans += join_paths(meson.current_source_dir(), 'clevis-encrypt-tang.1') + +- if actv.found() and kgen.found() and updt.found() and tang.found() ++ if actv.found() and kgen.found() and tang.found() + env = environment() + env.set('SD_ACTIVATE', actv.path()) + env.append('PATH', +diff --git a/src/pins/tang/pin-tang b/src/pins/tang/pin-tang +index 1720d3d..8190f3d 100755 +--- a/src/pins/tang/pin-tang ++++ b/src/pins/tang/pin-tang +@@ -31,18 +31,23 @@ mkdir -p "$TMP"/db + mkdir -p "$TMP"/cache + + # Generate the server keys ++KEYS="$TMP"/db + tangd-keygen "$TMP"/db sig exc +-tangd-update "$TMP"/db "$TMP"/cache ++if which tangd-update; then ++ tangd-update "$TMP"/db "$TMP"/cache ++ KEYS=$TMP/cache ++fi + + # Start the server + port="$(shuf -i 1024-65536 -n 1)" +-$SD_ACTIVATE --inetd -l 127.0.0.1:$port -a tangd "$TMP"/cache & ++$SD_ACTIVATE --inetd -l 127.0.0.1:$port -a tangd "$KEYS" & + PID=$! + sleep 0.25 + + thp="$(jose jwk thp -i "$TMP/db/sig.jwk")" +-adv="$TMP/cache/default.jws" + url="http://localhost:${port}" ++adv="$TMP/adv" ++curl "$url/adv" -o $adv + + cfg="$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv")" + enc="$(echo -n "hi" | clevis encrypt tang "$cfg")" +-- +2.18.4 + diff --git a/SOURCES/0002-Fix-clevis-luks-unlock-and-add-related-tests.patch b/SOURCES/0002-Fix-clevis-luks-unlock-and-add-related-tests.patch new file mode 100644 index 0000000..0f4b835 --- /dev/null +++ b/SOURCES/0002-Fix-clevis-luks-unlock-and-add-related-tests.patch @@ -0,0 +1,732 @@ +From e5f6d87d5c71f3faf0c0dbe38534fd3eab30f43e Mon Sep 17 00:00:00 2001 +From: Sergio Correia +Date: Wed, 13 May 2020 23:51:04 -0300 +Subject: [PATCH 2/8] Fix clevis luks unlock and add related tests + +--- + src/luks/clevis-luks-common-functions | 35 ++++++ + src/luks/clevis-luks-unlock | 68 ++++++++++++ + src/luks/clevis-luks-unlock.in | 130 ---------------------- + src/luks/meson.build | 10 +- + src/luks/tests/meson.build | 40 +++++++ + src/luks/tests/tests-common-functions.in | 134 +++++++++++++++++++++-- + src/luks/tests/unlock-tang-luks1 | 83 ++++++++++++++ + src/luks/tests/unlock-tang-luks2 | 83 ++++++++++++++ + 8 files changed, 439 insertions(+), 144 deletions(-) + create mode 100755 src/luks/clevis-luks-unlock + delete mode 100755 src/luks/clevis-luks-unlock.in + create mode 100755 src/luks/tests/unlock-tang-luks1 + create mode 100755 src/luks/tests/unlock-tang-luks2 + +diff --git a/src/luks/clevis-luks-common-functions b/src/luks/clevis-luks-common-functions +index e27c444..d04fdb5 100644 +--- a/src/luks/clevis-luks-common-functions ++++ b/src/luks/clevis-luks-common-functions +@@ -281,3 +281,38 @@ clevis_luks_read_pins_from_slot() { + fi + printf "%s: %s\n" "${SLOT}" "${cfg}" + } ++ ++# clevis_luks_unlock_device() does the unlock of the device passed as ++# parameter and returns the decoded passphrase. ++clevis_luks_unlock_device() { ++ local DEV="${1}" ++ [ -z "${DEV}" ] && return 1 ++ ++ local used_slots ++ if ! used_slots=$(clevis_luks_used_slots "${DEV}") \ ++ || [ -z "${used_slots}" ]; then ++ return 1 ++ fi ++ ++ local slt jwe passphrase ++ for slt in ${used_slots}; do ++ if ! jwe="$(clevis_luks_read_slot "${DEV}" "${slt}" 2>/dev/null)" \ ++ || [ -z "${jwe}" ]; then ++ continue ++ fi ++ ++ if ! passphrase="$(clevis decrypt < <(echo -n "${jwe}"))" \ ++ || [ -z "${passphrase}" ]; then ++ continue ++ fi ++ ++ if ! cryptsetup luksOpen --test-passphrase "${DEV}" \ ++ --key-file <(echo -n "${passphrase}"); then ++ continue ++ fi ++ echo -n "${passphrase}" ++ return 0 ++ done ++ ++ return 1 ++} +diff --git a/src/luks/clevis-luks-unlock b/src/luks/clevis-luks-unlock +new file mode 100755 +index 0000000..580fde8 +--- /dev/null ++++ b/src/luks/clevis-luks-unlock +@@ -0,0 +1,68 @@ ++#!/bin/bash -e ++# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2016 Red Hat, Inc. ++# Author: Nathaniel McCallum ++# ++# 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 . ++# ++. clevis-luks-common-functions ++ ++SUMMARY="Unlocks a LUKS volume" ++ ++function usage() { ++ exec >&2 ++ echo ++ echo "Usage: clevis luks unlock -d DEV [-n NAME]" ++ echo ++ echo "$SUMMARY": ++ echo ++ echo " -d DEV The LUKS device on which to perform unlocking" ++ echo ++ echo " -n NAME The name of the unlocked device node" ++ echo ++ exit 2 ++} ++ ++if [ $# -eq 1 ] && [ "$1" == "--summary" ]; then ++ echo "$SUMMARY" ++ exit 0 ++fi ++ ++while getopts ":d:n:" o; do ++ case "$o" in ++ d) DEV="$OPTARG";; ++ n) NAME="$OPTARG";; ++ *) usage;; ++ esac ++done ++ ++if [ -z "$DEV" ]; then ++ echo "Did not specify a device!" >&2 ++ usage ++fi ++ ++if ! cryptsetup isLuks "$DEV"; then ++ echo "$DEV is not a LUKS device!" >&2 ++ exit 1 ++fi ++ ++NAME="${NAME:-luks-"$(cryptsetup luksUUID "$DEV")"}" ++ ++if ! pt=$(clevis_luks_unlock_device "${DEV}"); then ++ echo "${DEV} could not be opened." >&2 ++ exit 1 ++fi ++ ++cryptsetup open -d- "${DEV}" "${NAME}" < <(echo -n "${pt}") +diff --git a/src/luks/clevis-luks-unlock.in b/src/luks/clevis-luks-unlock.in +deleted file mode 100755 +index aa3134b..0000000 +--- a/src/luks/clevis-luks-unlock.in ++++ /dev/null +@@ -1,130 +0,0 @@ +-#!/bin/bash -e +-# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: +-# +-# Copyright (c) 2016 Red Hat, Inc. +-# Author: Nathaniel McCallum +-# +-# 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="Unlocks a LUKS volume" +-UUID=cb6e8904-81ff-40da-a84a-07ab9ab5715e +- +-# We require cryptsetup >= 2.0.4 to fully support LUKSv2. +-# Support is determined at build time. +-function luks2_supported() { +- return @OLD_CRYPTSETUP@ +-} +- +-function usage() { +- exec >&2 +- echo +- echo "Usage: clevis luks unlock -d DEV [-n NAME]" +- echo +- echo "$SUMMARY": +- echo +- echo " -d DEV The LUKS device on which to perform unlocking" +- echo +- echo " -n NAME The name of the unlocked device node" +- echo +- exit 2 +-} +- +-if [ $# -eq 1 ] && [ "$1" == "--summary" ]; then +- echo "$SUMMARY" +- exit 0 +-fi +- +-while getopts ":d:n:" o; do +- case "$o" in +- d) DEV="$OPTARG";; +- n) NAME="$OPTARG";; +- *) usage;; +- esac +-done +- +-if [ -z "$DEV" ]; then +- echo "Did not specify a device!" >&2 +- usage +-fi +- +-if ! cryptsetup isLuks "$DEV"; then +- echo "$DEV is not a LUKS device!" >&2 +- exit 1 +-fi +- +-if luks2_supported; then +- if cryptsetup isLuks --type luks1 "$DEV"; then +- luks_type="luks1" +- elif cryptsetup isLuks --type luks2 "$DEV";then +- luks_type="luks2" +- else +- echo "$DEV is not a supported LUKS device!" >&2 +- exit 1 +- fi +-else +- luks_type="luks1" +-fi +-NAME="${NAME:-luks-"$(cryptsetup luksUUID "$DEV")"}" +- +-luks1_decrypt() { +- luksmeta load "$@" \ +- | clevis decrypt +- +- local rc +- for rc in "${PIPESTATUS[@]}"; do +- [ $rc -eq 0 ] || return $rc +- done +- return 0 +-} +- +-luks2_decrypt() { +- # jose jwe fmt -c outputs extra \n, so clean it up +- cryptsetup token export "$@" \ +- | jose fmt -j- -Og jwe -o- \ +- | jose jwe fmt -i- -c \ +- | tr -d '\n' \ +- | clevis decrypt +- +- local rc +- for rc in "${PIPESTATUS[@]}"; do +- [ $rc -eq 0 ] || return $rc +- done +- return 0 +-} +- +-if [ "$luks_type" == "luks1" ]; then +- while read -r slot state uuid; do +- [ "$state" == "active" ] || continue +- [ "$uuid" == "$UUID" ] || continue +- +- pt="$(luks1_decrypt -d $DEV -s $slot -u $UUID)" \ +- || continue +- exec cryptsetup open -d- "$DEV" "$NAME" < <( +- echo -n "$pt" +- ) +- done < <(luksmeta show -d "$DEV") +- +-elif [ "$luks_type" == "luks2" ]; then +- while read -r id; do +- pt="$(luks2_decrypt --token-id "$id" "$DEV")" \ +- || continue +- exec cryptsetup open -d- "$DEV" "$NAME" < <( +- echo -n "$pt" +- ) +- done < <(cryptsetup luksDump "$DEV" | sed -rn 's|^\s+([0-9]+): clevis|\1|p') +-fi +- +-echo "$DEV could not be opened." >&2 +-exit 1 +diff --git a/src/luks/meson.build b/src/luks/meson.build +index bbba63f..0d24f8d 100644 +--- a/src/luks/meson.build ++++ b/src/luks/meson.build +@@ -21,9 +21,7 @@ clevis_luks_bind = configure_file(input: 'clevis-luks-bind.in', + clevis_luks_unbind = configure_file(input: 'clevis-luks-unbind.in', + output: 'clevis-luks-unbind', + configuration: luksmeta_data) +-clevis_luks_unlock = configure_file(input: 'clevis-luks-unlock.in', +- output: 'clevis-luks-unlock', +- configuration: luksmeta_data) ++ + if libcryptsetup.found() and luksmeta.found() and pwmake.found() + subdir('systemd') + subdir('udisks2') +@@ -31,18 +29,18 @@ if libcryptsetup.found() and luksmeta.found() and pwmake.found() + bins += clevis_luks_unbind + mans += join_paths(meson.current_source_dir(), 'clevis-luks-unbind.1') + +- bins += clevis_luks_unlock +- mans += join_paths(meson.current_source_dir(), 'clevis-luks-unlock.1') +- + bins += clevis_luks_bind + mans += join_paths(meson.current_source_dir(), 'clevis-luks-bind.1') + + mans += join_paths(meson.current_source_dir(), 'clevis-luks-unlockers.7') + + bins += join_paths(meson.current_source_dir(), 'clevis-luks-common-functions') ++ + bins += join_paths(meson.current_source_dir(), 'clevis-luks-list') + mans += join_paths(meson.current_source_dir(), 'clevis-luks-list.1') + ++ bins += join_paths(meson.current_source_dir(), 'clevis-luks-unlock') ++ mans += join_paths(meson.current_source_dir(), 'clevis-luks-unlock.1') + else + warning('Will not install LUKS support due to missing dependencies!') + endif +diff --git a/src/luks/tests/meson.build b/src/luks/tests/meson.build +index 2e0fb92..9a16b42 100644 +--- a/src/luks/tests/meson.build ++++ b/src/luks/tests/meson.build +@@ -1,6 +1,30 @@ + # We use jq for comparing the pin config in the clevis luks list tests. + jq = find_program('jq', required: false) + ++# we use systemd-socket-activate for running test tang servers. ++actv = find_program( ++ 'systemd-socket-activate', ++ 'systemd-activate', ++ required: false ++) ++ ++kgen = find_program( ++ join_paths(libexecdir, 'tangd-keygen'), ++ join_paths(get_option('prefix'), get_option('libdir'), 'tangd-keygen'), ++ join_paths(get_option('prefix'), get_option('libexecdir'), 'tangd-keygen'), ++ join_paths('/', 'usr', get_option('libdir'), 'tangd-keygen'), ++ join_paths('/', 'usr', get_option('libexecdir'), 'tangd-keygen'), ++ required: false ++) ++tang = find_program( ++ join_paths(libexecdir, 'tangd'), ++ join_paths(get_option('prefix'), get_option('libdir'), 'tangd'), ++ join_paths(get_option('prefix'), get_option('libexecdir'), 'tangd'), ++ join_paths('/', 'usr', get_option('libdir'), 'tangd'), ++ join_paths('/', 'usr', get_option('libexecdir'), 'tangd'), ++ required: false ++) ++ + common_functions = configure_file(input: 'tests-common-functions.in', + output: 'tests-common-functions', + configuration: luksmeta_data, +@@ -24,6 +48,14 @@ env.prepend('PATH', + separator: ':' + ) + ++has_tang = false ++if actv.found() and kgen.found() and tang.found() ++ has_tang = true ++ env.set('SD_ACTIVATE', actv.path()) ++ env.set('TANGD_KEYGEN', kgen.path()) ++ env.set('TANGD', tang.path()) ++endif ++ + test('bind-wrong-pass-luks1', find_program('bind-wrong-pass-luks1'), env: env) + test('bind-luks1', find_program('bind-luks1'), env: env) + test('unbind-unbound-slot-luks1', find_program('unbind-unbound-slot-luks1'), env: env) +@@ -42,6 +74,10 @@ else + warning('Will not run "clevis luks list" tests due to missing jq dependency') + endif + ++if has_tang ++ test('unlock-tang-luks1', find_program('unlock-tang-luks1'), env: env, timeout: 90) ++endif ++ + # LUKS2 tests go here, and they get included if we get support for it, based + # on the cryptsetup version. + # Binding LUKS2 takes longer, so timeout is increased for a few tests. +@@ -56,4 +92,8 @@ if luksmeta_data.get('OLD_CRYPTSETUP') == '0' + test('list-tang-luks2', find_program('list-tang-luks2'), env: env, timeout: 60) + test('list-sss-tang-luks2', find_program('list-sss-tang-luks2'), env: env, timeout: 60) + endif ++ ++ if has_tang ++ test('unlock-tang-luks2', find_program('unlock-tang-luks2'), env: env, timeout: 120) ++ endif + endif +diff --git a/src/luks/tests/tests-common-functions.in b/src/luks/tests/tests-common-functions.in +index 90420d1..7b3fdad 100755 +--- a/src/luks/tests/tests-common-functions.in ++++ b/src/luks/tests/tests-common-functions.in +@@ -56,7 +56,7 @@ new_device() { + + # Some builders fail if the cryptsetup steps are not ran as root, so let's + # skip the test now if not running as root. +- if [ $(id -u) != 0 ]; then ++ if [ "$(id -u)" != 0 ]; then + skip_test "WARNING: You must be root to run this test; test skipped." + fi + +@@ -74,9 +74,9 @@ new_device() { + return 0 + fi + +- fallocate -l16M "${DEV}" +- local extra_options='--pbkdf pbkdf2 --pbkdf-force-iterations 1000' +- cryptsetup luksFormat --type "${LUKS}" ${extra_options} --batch-mode \ ++ fallocate -l64M "${DEV}" ++ cryptsetup luksFormat --type "${LUKS}" --pbkdf pbkdf2 \ ++ --pbkdf-force-iterations 1000 --batch-mode \ + --force-password "${DEV}" <<< "${PASS}" + # Caching the just-formatted device for possible reuse. + cp -f "${DEV}" "${DEV_CACHED}" +@@ -90,7 +90,7 @@ new_device_keyfile() { + + # Some builders fail if the cryptsetup steps are not ran as root, so let's + # skip the test now if not running as root. +- if [ $(id -u) != 0 ]; then ++ if [ "$(id -u)" != 0 ]; then + skip_test "WARNING: You must be root to run this test; test skipped." + fi + +@@ -98,9 +98,9 @@ new_device_keyfile() { + error "Invalid keyfile (${KEYFILE})." + fi + +- fallocate -l16M "${DEV}" +- local extra_options='--pbkdf pbkdf2 --pbkdf-force-iterations 1000' +- cryptsetup luksFormat --type "${LUKS}" ${extra_options} --batch-mode \ ++ fallocate -l64M "${DEV}" ++ cryptsetup luksFormat --type "${LUKS}" --pbkdf pbkdf2 \ ++ --pbkdf-force-iterations 1000 --batch-mode \ + "${DEV}" "${KEYFILE}" + } + +@@ -112,4 +112,122 @@ pin_cfg_equal() { + <(jq -S . < <(echo -n "${cfg2}")) + } + ++# Get a random port to be used with a test tang server. ++get_random_port() { ++ shuf -i 1024-65535 -n 1 ++} ++ ++# Removes tang rotated keys from the test server. ++tang_remove_rotated_keys() { ++ local basedir="${1}" ++ ++ if [ -z "${basedir}" ]; then ++ echo "Please pass a valid base directory for tang" ++ return 1 ++ fi ++ ++ local db="${basedir}/db" ++ mkdir -p "${db}" ++ ++ pushd "${db}" ++ find . -name ".*.jwk" -exec rm -f {} \; ++ popd ++} ++ ++# Creates new keys for the test tang server. ++tang_new_keys() { ++ local basedir="${1}" ++ local rotate="${2}" ++ ++ if [ -z "${basedir}" ]; then ++ echo "Please pass a valid base directory for tang" ++ return 1 ++ fi ++ ++ [ -z "${TANGD_KEYGEN}" ] && skip_test "WARNING: TANGD_KEYGEN is not defined." ++ ++ local db="${basedir}/db" ++ mkdir -p "${db}" ++ ++ if [ -n "${rotate}" ]; then ++ pushd "${db}" ++ local k ++ k=$(find . -name "*.jwk" | wc -l) ++ if [ "${k}" -gt 0 ]; then ++ for k in *.jwk; do ++ mv -f -- "${k}" ".${k}" ++ done ++ fi ++ popd ++ fi ++ ++ "${TANGD_KEYGEN}" "${db}" ++ ++ return 0 ++} ++ ++# Start a test tang server. ++tang_run() { ++ local basedir="${1}" ++ local port="${2}" ++ ++ if [ -z "${basedir}" ]; then ++ echo "Please pass a valid base directory for tang" >&2 ++ return 1 ++ fi ++ ++ if [ -z "${port}" ]; then ++ echo "Please pass a valid port for tang" >&2 ++ return 1 ++ fi ++ ++ if ! tang_new_keys "${basedir}"; then ++ echo "Error creating new keys for tang server" >&2 ++ return 1 ++ fi ++ ++ local KEYS="${basedir}/db" ++ ++ local inetd='--inetd' ++ [ "${SD_ACTIVATE##*/}" = "systemd-activate" ] && inetd= ++ ++ local pid pidfile ++ pidfile="${basedir}/tang.pid" ++ ++ "${SD_ACTIVATE}" ${inetd} -l "${TANG_HOST}":"${port}" \ ++ -a "${TANGD}" "${KEYS}" & ++ pid=$! ++ echo "${pid}" > "${pidfile}" ++} ++ ++# Stop tang server. ++tang_stop() { ++ local basedir="${1}" ++ local pidfile="${basedir}/tang.pid" ++ [ -f "${pidfile}" ] || return 0 ++ ++ local pid ++ pid=$(<"${pidfile}") ++ kill "${pid}" ++} ++ ++# Wait for the tang server to be operational. ++tang_wait_until_ready() { ++ local port="${1}" ++ while ! curl --output /dev/null --silent --fail \ ++ http://"${TANG_HOST}":"${port}"/adv; do ++ sleep 0.1 ++ echo -n . >&2 ++ done ++} ++ ++# Get tang advertisement. ++tang_get_adv() { ++ local port="${1}" ++ local adv="${2}" ++ ++ curl -o "${adv}" http://"${TANG_HOST}":"${port}"/adv ++} ++ ++export TANG_HOST=127.0.0.1 + export DEFAULT_PASS='just-some-test-password-here' +diff --git a/src/luks/tests/unlock-tang-luks1 b/src/luks/tests/unlock-tang-luks1 +new file mode 100755 +index 0000000..841ba01 +--- /dev/null ++++ b/src/luks/tests/unlock-tang-luks1 +@@ -0,0 +1,83 @@ ++#!/bin/bash -ex ++# vim: set ts=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2020 Red Hat, Inc. ++# Author: Sergio Correia ++# ++# 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 . ++ ++TEST=$(basename "${0}") ++. tests-common-functions ++ ++. clevis-luks-common-functions ++ ++on_exit() { ++ [ ! -d "${TMP}" ] && return 0 ++ tang_stop "${TMP}" ++ rm -rf "${TMP}" ++} ++ ++trap 'on_exit' EXIT ++trap 'on_exit' ERR ++ ++TMP="$(mktemp -d)" ++ ++port=$(get_random_port) ++tang_run "${TMP}" "${port}" & ++tang_wait_until_ready "${port}" ++ ++url="http://${TANG_HOST}:${port}" ++adv="${TMP}/adv" ++tang_get_adv "${port}" "${adv}" ++ ++cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv") ++ ++# LUKS1. ++DEV="${TMP}/luks1-device" ++new_device "luks1" "${DEV}" ++ ++if ! clevis luks bind -f -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: Bind should have succeeded." ++fi ++ ++if ! clevis_luks_unlock_device "${DEV}"; then ++ error "${TEST}: we were unable to unlock ${DEV}." ++fi ++ ++# Let's rotate the tang keys and add another binding with the new key. ++tang_new_keys "${TMP}" "rotate-keys" ++ ++# Unlock should still work now. ++if ! clevis_luks_unlock_device "${DEV}"; then ++ error "${TEST}: we should still be able to unlock ${DEV}" ++fi ++ ++# Now let's remove the rotated keys. ++tang_remove_rotated_keys "${TMP}" ++ ++# Unlock should not work anymore. ++if clevis_luks_unlock_device "${DEV}"; then ++ error "${TEST}: we should not be able to unlock ${DEV}" ++fi ++ ++# Now let's add another binding with the new keys. ++tang_get_adv "${port}" "${adv}" # Updating the advertisement. ++if ! clevis luks bind -f -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: Bind should have succeeded." ++fi ++ ++# Unlock should work again, using the new keys. ++if ! clevis_luks_unlock_device "${DEV}"; then ++ error "${TEST}: we should be able to unlock ${DEV} with the new keys" ++fi +diff --git a/src/luks/tests/unlock-tang-luks2 b/src/luks/tests/unlock-tang-luks2 +new file mode 100755 +index 0000000..81822fb +--- /dev/null ++++ b/src/luks/tests/unlock-tang-luks2 +@@ -0,0 +1,83 @@ ++#!/bin/bash -ex ++# vim: set ts=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2020 Red Hat, Inc. ++# Author: Sergio Correia ++# ++# 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 . ++ ++TEST=$(basename "${0}") ++. tests-common-functions ++ ++. clevis-luks-common-functions ++ ++on_exit() { ++ [ ! -d "${TMP}" ] && return 0 ++ tang_stop "${TMP}" ++ rm -rf "${TMP}" ++} ++ ++trap 'on_exit' EXIT ++trap 'on_exit' ERR ++ ++TMP="$(mktemp -d)" ++ ++port=$(get_random_port) ++tang_run "${TMP}" "${port}" & ++tang_wait_until_ready "${port}" ++ ++url="http://${TANG_HOST}:${port}" ++adv="${TMP}/adv" ++tang_get_adv "${port}" "${adv}" ++ ++cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv") ++ ++# LUKS2. ++DEV="${TMP}/luks2-device" ++new_device "luks2" "${DEV}" ++ ++if ! clevis luks bind -f -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: Bind should have succeeded." ++fi ++ ++if ! clevis_luks_unlock_device "${DEV}"; then ++ error "${TEST}: we were unable to unlock ${DEV}." ++fi ++ ++# Let's rotate the tang keys and add another binding with the new key. ++tang_new_keys "${TMP}" "rotate-keys" ++ ++# Unlock should still work now. ++if ! clevis_luks_unlock_device "${DEV}"; then ++ error "${TEST}: we should still be able to unlock ${DEV}" ++fi ++ ++# Now let's remove the rotated keys. ++tang_remove_rotated_keys "${TMP}" ++ ++# Unlock should not work anymore. ++if clevis_luks_unlock_device "${DEV}"; then ++ error "${TEST}: we should not be able to unlock ${DEV}" ++fi ++ ++# Now let's add another binding with the new keys. ++tang_get_adv "${port}" "${adv}" # Updating the advertisement. ++if ! clevis luks bind -f -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: Bind should have succeeded." ++fi ++ ++# Unlock should work again, using the new keys. ++if ! clevis_luks_unlock_device "${DEV}"; then ++ error "${TEST}: we should be able to unlock ${DEV} with the new keys" ++fi +-- +2.18.4 + diff --git a/SOURCES/0003-Improve-error-message-when-bind-is-given-an-invalid-.patch b/SOURCES/0003-Improve-error-message-when-bind-is-given-an-invalid-.patch new file mode 100644 index 0000000..73f3470 --- /dev/null +++ b/SOURCES/0003-Improve-error-message-when-bind-is-given-an-invalid-.patch @@ -0,0 +1,57 @@ +From d393fbc256e22cc8019d18214e4d140d58f3302a Mon Sep 17 00:00:00 2001 +From: Sergio Correia +Date: Wed, 13 May 2020 23:51:04 -0300 +Subject: [PATCH 3/8] Improve error message when bind is given an invalid PIN + +--- + src/luks/clevis-luks-bind.in | 6 ++++++ + src/luks/clevis-luks-common-functions | 9 +++++++++ + 2 files changed, 15 insertions(+) + +diff --git a/src/luks/clevis-luks-bind.in b/src/luks/clevis-luks-bind.in +index a5d3c5f..89a5e22 100755 +--- a/src/luks/clevis-luks-bind.in ++++ b/src/luks/clevis-luks-bind.in +@@ -19,6 +19,8 @@ + # along with this program. If not, see . + # + ++. clevis-luks-common-functions ++ + SUMMARY="Binds a LUKS device using the specified policy" + UUID=cb6e8904-81ff-40da-a84a-07ab9ab5715e + +@@ -76,6 +78,10 @@ fi + if ! PIN="${@:$((OPTIND++)):1}" || [ -z "$PIN" ]; then + echo "Did not specify a pin!" >&2 + usage ++elif ! EXE=$(findexe clevis-encrypt-"${PIN}") \ ++ || [ -z "${EXE}" ]; then ++ echo "'$PIN' is not a valid pin!" >&2 ++ usage + fi + + if ! CFG="${@:$((OPTIND++)):1}" || [ -z "$CFG" ]; then +diff --git a/src/luks/clevis-luks-common-functions b/src/luks/clevis-luks-common-functions +index d04fdb5..36f0bfd 100644 +--- a/src/luks/clevis-luks-common-functions ++++ b/src/luks/clevis-luks-common-functions +@@ -108,6 +108,15 @@ clevis_luks_read_slot() { + echo "${DATA_CODED}" + } + ++# findexe() finds an executable. ++findexe() { ++ while read -r -d: path; do ++ [ -f "${path}/${1}" ] && [ -x "${path}/${1}" ] && \ ++ echo "${path}/${1}" && return 0 ++ done <<< "${PATH}:" ++ return 1 ++} ++ + # clevis_luks_used_slots() will return the list of used slots for a given LUKS + # device. + clevis_luks_used_slots() { +-- +2.18.4 + diff --git a/SOURCES/0004-Add-rd.neednet-1-to-cmdline-only-if-there-are-device.patch b/SOURCES/0004-Add-rd.neednet-1-to-cmdline-only-if-there-are-device.patch new file mode 100644 index 0000000..0f0af18 --- /dev/null +++ b/SOURCES/0004-Add-rd.neednet-1-to-cmdline-only-if-there-are-device.patch @@ -0,0 +1,53 @@ +From fc0cc6f159857e463aacababdc0735b0972d103c Mon Sep 17 00:00:00 2001 +From: Sergio Correia +Date: Wed, 13 May 2020 23:51:04 -0300 +Subject: [PATCH 4/8] Add rd.neednet=1 to cmdline only if there are devices + bound to tang + +--- + .../dracut/clevis-pin-tang/module-setup.sh.in | 21 +++++++++++++++++-- + 1 file changed, 19 insertions(+), 2 deletions(-) + +diff --git a/src/luks/systemd/dracut/clevis-pin-tang/module-setup.sh.in b/src/luks/systemd/dracut/clevis-pin-tang/module-setup.sh.in +index 1bb2ead..a4984dc 100755 +--- a/src/luks/systemd/dracut/clevis-pin-tang/module-setup.sh.in ++++ b/src/luks/systemd/dracut/clevis-pin-tang/module-setup.sh.in +@@ -18,8 +18,23 @@ + # along with this program. If not, see . + # + ++has_devices_bound_to_tang() { ++ local dev ++ for dev in $(lsblk -p -n -s -r \ ++ | awk '$6 == "crypt" { getline; print $1 }' | sort -u); do ++ if clevis luks list -d "${dev}" 2>/dev/null | grep -q tang; then ++ return 0 ++ fi ++ done ++ return 1 ++} ++ + depends() { +- echo clevis network ++ local deps="clevis" ++ if has_devices_bound_to_tang; then ++ deps=$(printf "%s network" "${deps}") ++ fi ++ echo "${deps}" + return 0 + } + +@@ -28,7 +43,9 @@ cmdline() { + } + + install() { +- cmdline > "${initdir}/etc/cmdline.d/99clevis-pin-tang.conf" ++ if has_devices_bound_to_tang; then ++ cmdline > "${initdir}/etc/cmdline.d/99clevis-pin-tang.conf" ++ fi + + inst_multiple \ + clevis-decrypt-tang \ +-- +2.18.4 + diff --git a/SOURCES/0005-Add-the-option-to-extract-luks-passphrase-used-for-b.patch b/SOURCES/0005-Add-the-option-to-extract-luks-passphrase-used-for-b.patch new file mode 100644 index 0000000..b3ed25b --- /dev/null +++ b/SOURCES/0005-Add-the-option-to-extract-luks-passphrase-used-for-b.patch @@ -0,0 +1,366 @@ +From e3641a7193adac1cea525c093f39679c2cfa22c9 Mon Sep 17 00:00:00 2001 +From: Sergio Correia +Date: Wed, 13 May 2020 23:53:38 -0300 +Subject: [PATCH 5/8] Add the option to extract luks passphrase used for + binding + +Usage: + +clevis luks pass -d /dev/sda1 -s 1 + +--- + src/luks/clevis-luks-pass | 69 +++++++++++++++++++++++++++++ + src/luks/clevis-luks-pass.1.adoc | 43 ++++++++++++++++++ + src/luks/meson.build | 3 ++ + src/luks/tests/meson.build | 11 +++++ + src/luks/tests/pass-tang-luks1 | 75 ++++++++++++++++++++++++++++++++ + src/luks/tests/pass-tang-luks2 | 75 ++++++++++++++++++++++++++++++++ + 6 files changed, 276 insertions(+) + create mode 100755 src/luks/clevis-luks-pass + create mode 100644 src/luks/clevis-luks-pass.1.adoc + create mode 100755 src/luks/tests/pass-tang-luks1 + create mode 100755 src/luks/tests/pass-tang-luks2 + +diff --git a/src/luks/clevis-luks-pass b/src/luks/clevis-luks-pass +new file mode 100755 +index 0000000..1ce8c4c +--- /dev/null ++++ b/src/luks/clevis-luks-pass +@@ -0,0 +1,69 @@ ++#!/bin/bash -e ++# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2019 Red Hat, Inc. ++# Author: Sergio Correia - LUKS2 support. ++# ++# 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 . ++# ++ ++. clevis-luks-common-functions ++ ++SUMMARY="Returns the LUKS passphrase used for binding a particular slot." ++ ++function usage() { ++ echo >&2 ++ echo "Usage: clevis luks pass -d DEV -s SLT" >&2 ++ echo >&2 ++ echo "$SUMMARY": >&2 ++ echo >&2 ++ echo " -d DEV The LUKS device to extract the LUKS passphrase used for binding" >&2 ++ echo >&2 ++ echo " -s SLOT The slot number to extract the LUKS passphrase" >&2 ++ echo >&2 ++ exit 1 ++} ++ ++if [ ${#} -eq 1 ] && [ "${1}" = "--summary" ]; then ++ echo "${SUMMARY}" ++ exit 0 ++fi ++ ++while getopts ":d:s:" o; do ++ case "$o" in ++ d) DEV=${OPTARG};; ++ s) SLT=${OPTARG};; ++ *) usage;; ++ esac ++done ++ ++if [ -z "${DEV}" ]; then ++ echo "Did not specify a device!" >&2 ++ usage ++fi ++ ++if [ -z "${SLT}" ]; then ++ echo "Did not specify a slot!" >&2 ++ usage ++fi ++ ++if ! jwe=$(clevis_luks_read_slot "${DEV}" "${SLT}" 2>/dev/null); then ++ echo "It was not possible to read slot ${SLT} from ${DEV}!" >&2 ++ exit 1 ++fi ++ ++if ! clevis decrypt < <(echo -n "${jwe}"); then ++ echo "It was not possible to decrypt the passphrase associated to slot ${SLT} in {DEV}!" >&2 ++ exit 1 ++fi +diff --git a/src/luks/clevis-luks-pass.1.adoc b/src/luks/clevis-luks-pass.1.adoc +new file mode 100644 +index 0000000..fa9526a +--- /dev/null ++++ b/src/luks/clevis-luks-pass.1.adoc +@@ -0,0 +1,43 @@ ++CLEVIS-LUKS-PASS(1) ++=================== ++:doctype: manpage ++ ++ ++== NAME ++ ++clevis-luks-pass - Extracts the passphrase used for binding a particular slot in a LUKS device ++ ++== SYNOPSIS ++ ++*clevis luks pass* -d DEV -s SLT ++ ++== OVERVIEW ++ ++The *clevis luks pass* command extracts the passphrase used for binding a particular slot in a LUKS device. ++For example: ++ ++ clevis luks pass -d /dev/sda1 -s 1 ++ ++== OPTIONS ++ ++* *-d* _DEV_ : ++ The LUKS device on which to extract a passphrase from ++ ++* *-s* _SLT_ : ++ The slot to use for extracting the passphrase ++ ++== EXAMPLE ++ ++ clevis luks pass -d /dev/sda1 -s 1 ++ ++ ++Note that the output of *clevis luks pass* might be non-printable, in which case it would be better to redirect its output to a file and use it as a key ++file together with cryptsetup. For instance: ++ ++ clevis luks pass -d /dev/sda1 -s 1 > slot1-passphrase ++ ++And the file slot1-passphrase will contain the passphrase associated with slot #1 in /dev/sda1. ++ ++== SEE ALSO ++ ++link:clevis-luks-unlock.1.adoc[*clevis-luks-unlock*(1)], +diff --git a/src/luks/meson.build b/src/luks/meson.build +index 0d24f8d..fda2ca8 100644 +--- a/src/luks/meson.build ++++ b/src/luks/meson.build +@@ -41,6 +41,9 @@ if libcryptsetup.found() and luksmeta.found() and pwmake.found() + + bins += join_paths(meson.current_source_dir(), 'clevis-luks-unlock') + mans += join_paths(meson.current_source_dir(), 'clevis-luks-unlock.1') ++ ++ bins += join_paths(meson.current_source_dir(), 'clevis-luks-pass') ++ mans += join_paths(meson.current_source_dir(), 'clevis-luks-pass.1') + else + warning('Will not install LUKS support due to missing dependencies!') + endif +diff --git a/src/luks/tests/meson.build b/src/luks/tests/meson.build +index 9a16b42..4757c4b 100644 +--- a/src/luks/tests/meson.build ++++ b/src/luks/tests/meson.build +@@ -1,3 +1,9 @@ ++actv = find_program( ++ 'systemd-socket-activate', ++ 'systemd-activate', ++ required: false ++) ++ + # We use jq for comparing the pin config in the clevis luks list tests. + jq = find_program('jq', required: false) + +@@ -45,8 +51,11 @@ env.prepend('PATH', + join_paths(meson.build_root(), 'src', 'pins', 'sss'), + join_paths(meson.build_root(), 'src', 'pins', 'tang'), + join_paths(meson.build_root(), 'src', 'pins', 'tpm2'), ++ libexecdir, ++ '/usr/libexec', + separator: ':' + ) ++env.set('SD_ACTIVATE', actv.path()) + + has_tang = false + if actv.found() and kgen.found() and tang.found() +@@ -77,6 +86,7 @@ endif + if has_tang + test('unlock-tang-luks1', find_program('unlock-tang-luks1'), env: env, timeout: 90) + endif ++test('pass-tang-luks1', find_program('pass-tang-luks1'), env: env) + + # LUKS2 tests go here, and they get included if we get support for it, based + # on the cryptsetup version. +@@ -96,4 +106,5 @@ if luksmeta_data.get('OLD_CRYPTSETUP') == '0' + if has_tang + test('unlock-tang-luks2', find_program('unlock-tang-luks2'), env: env, timeout: 120) + endif ++ test('pass-tang-luks2', find_program('pass-tang-luks2'), env: env, timeout: 60) + endif +diff --git a/src/luks/tests/pass-tang-luks1 b/src/luks/tests/pass-tang-luks1 +new file mode 100755 +index 0000000..05cdb3e +--- /dev/null ++++ b/src/luks/tests/pass-tang-luks1 +@@ -0,0 +1,75 @@ ++#!/bin/bash -x ++# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2019 Red Hat, Inc. ++# Author: Sergio Correia ++# ++# 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 . ++# ++ ++TEST="${0}" ++. tests-common-functions ++ ++function on_exit() { ++ if [ "$PID" ]; then kill $PID; wait $PID || true; fi ++ [ -d "$TMP" ] && rm -rf $TMP ++} ++ ++trap 'on_exit' EXIT ++trap 'exit' ERR ++ ++export TMP=$(mktemp -d) ++mkdir -p "${TMP}/db" ++ ++# Generate the server keys ++KEYS="$TMP/db" ++tangd-keygen $TMP/db sig exc ++if which tangd-update; then ++ mkdir -p "${TMP}/cache" ++ tangd-update "${TMP}/db" "${TMP}/cache" ++ KEYS="${TMP}/cache" ++fi ++ ++# Start the server. ++port=$(shuf -i 1024-65536 -n 1) ++"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" & ++export PID=$! ++sleep 0.25 ++ ++url="http://localhost:${port}" ++adv="${TMP}/adv" ++curl "${url}/adv" -o "${adv}" ++ ++cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv") ++ ++# LUKS1. ++DEV="${TMP}/luks1-device" ++new_device "luks1" "${DEV}" ++ ++if ! clevis luks bind -f -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: Bind should have succeeded." ++fi ++ ++#Now let's test the passphrase. ++SLT=1 ++PASS=$(clevis luks pass -d "${DEV}" -s "${SLT}") ++echo $PASS >&2 ++if ! cryptsetup luksOpen --test-passphrase ""${DEV} \ ++ --key-file <(clevis luks pass -d "${DEV}" -s "${SLT}"); then ++ error "Passphrase obtained from clevis luks pass failed." ++fi ++ ++kill -9 "${PID}" ++! wait "${PID}" ++unset PID +diff --git a/src/luks/tests/pass-tang-luks2 b/src/luks/tests/pass-tang-luks2 +new file mode 100755 +index 0000000..9123aa0 +--- /dev/null ++++ b/src/luks/tests/pass-tang-luks2 +@@ -0,0 +1,75 @@ ++#!/bin/bash -x ++# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2019 Red Hat, Inc. ++# Author: Sergio Correia ++# ++# 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 . ++# ++ ++TEST="${0}" ++. tests-common-functions ++ ++function on_exit() { ++ if [ "$PID" ]; then kill $PID; wait $PID || true; fi ++ [ -d "$TMP" ] && rm -rf $TMP ++} ++ ++trap 'on_exit' EXIT ++trap 'exit' ERR ++ ++export TMP=$(mktemp -d) ++mkdir -p "${TMP}/db" ++ ++# Generate the server keys ++KEYS="$TMP/db" ++tangd-keygen $TMP/db sig exc ++if which tangd-update; then ++ mkdir -p "${TMP}/cache" ++ tangd-update "${TMP}/db" "${TMP}/cache" ++ KEYS="${TMP}/cache" ++fi ++ ++# Start the server. ++port=$(shuf -i 1024-65536 -n 1) ++"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" & ++export PID=$! ++sleep 0.25 ++ ++url="http://localhost:${port}" ++adv="${TMP}/adv" ++curl "${url}/adv" -o "${adv}" ++ ++cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv") ++ ++# LUKS2. ++DEV="${TMP}/luks2-device" ++new_device "luks2" "${DEV}" ++ ++if ! clevis luks bind -f -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: Bind should have succeeded." ++fi ++ ++#Now let's test the passphrase. ++SLT=1 ++PASS=$(clevis luks pass -d "${DEV}" -s "${SLT}") ++echo $PASS >&2 ++if ! cryptsetup luksOpen --test-passphrase ""${DEV} \ ++ --key-file <(clevis luks pass -d "${DEV}" -s "${SLT}"); then ++ error "Passphrase obtained from clevis luks pass failed." ++fi ++ ++kill -9 "${PID}" ++! wait "${PID}" ++unset PID +-- +2.18.4 + diff --git a/SOURCES/0006-Add-clevis-luks-regen-command.patch b/SOURCES/0006-Add-clevis-luks-regen-command.patch new file mode 100644 index 0000000..bce58e3 --- /dev/null +++ b/SOURCES/0006-Add-clevis-luks-regen-command.patch @@ -0,0 +1,1387 @@ +From 158bdeda3ca961b0e615c8adfc58b61e1a1ba5c7 Mon Sep 17 00:00:00 2001 +From: Sergio Correia +Date: Wed, 13 May 2020 23:55:41 -0300 +Subject: [PATCH 6/8] Add clevis luks regen command + +The clevis luks regen command regenerates the LUKS binding for a given +device/slot, using the same configuration of the existing binding. + +Example: + +clevis luks list -d /dev/sda1 +1: tang '{"url":"http://tang.server"}' +2: tpm2 '{"hash":"sha256","key":"ecc"}' + +To rotate the binding in slot 1, we can use the following: +clevis luks regen -d /dev/sda1 -s 1 + +The new binding will use the existing configuration, namely: +'{"url":"http://tang.server"}', with the `tang' pin. +--- + src/luks/clevis-luks-common-functions | 230 +++++++++++++++++++++++ + src/luks/clevis-luks-pass | 5 +- + src/luks/clevis-luks-regen | 185 ++++++++++++++++++ + src/luks/clevis-luks-regen.1.adoc | 48 +++++ + src/luks/meson.build | 3 + + src/luks/tests/backup-restore-luks1 | 114 +++++++++++ + src/luks/tests/backup-restore-luks2 | 115 ++++++++++++ + src/luks/tests/meson.build | 6 + + src/luks/tests/meson.build.orig | 110 +++++++++++ + src/luks/tests/regen-inplace-luks1 | 98 ++++++++++ + src/luks/tests/regen-inplace-luks2 | 99 ++++++++++ + src/luks/tests/regen-not-inplace-luks1 | 95 ++++++++++ + src/luks/tests/regen-not-inplace-luks2 | 96 ++++++++++ + src/luks/tests/tests-common-functions.in | 26 +++ + 14 files changed, 1228 insertions(+), 2 deletions(-) + create mode 100755 src/luks/clevis-luks-regen + create mode 100644 src/luks/clevis-luks-regen.1.adoc + create mode 100755 src/luks/tests/backup-restore-luks1 + create mode 100755 src/luks/tests/backup-restore-luks2 + create mode 100644 src/luks/tests/meson.build.orig + create mode 100755 src/luks/tests/regen-inplace-luks1 + create mode 100755 src/luks/tests/regen-inplace-luks2 + create mode 100755 src/luks/tests/regen-not-inplace-luks1 + create mode 100755 src/luks/tests/regen-not-inplace-luks2 + +diff --git a/src/luks/clevis-luks-common-functions b/src/luks/clevis-luks-common-functions +index 36f0bfd..5b515ad 100644 +--- a/src/luks/clevis-luks-common-functions ++++ b/src/luks/clevis-luks-common-functions +@@ -325,3 +325,233 @@ clevis_luks_unlock_device() { + + return 1 + } ++ ++# Generate a key with the same entropy as the LUKS master key of a given ++# device. ++generate_key() { ++ local DEV="${1}" ++ ++ if [ -z "${DEV}" ]; then ++ echo "Please, specify a device." >&2 ++ return 1 ++ fi ++ ++ local dump ++ local filter ++ dump=$(cryptsetup luksDump "${DEV}") ++ if cryptsetup isLuks --type luks1 "${DEV}"; then ++ filter=$(sed -rn 's|MK bits:[ \t]*([0-9]+)|\1|p' <<< "${dump}") ++ elif cryptsetup isLuks --type luks2 "${DEV}"; then ++ filter=$(sed -rn 's|^\s+Key:\s+([0-9]+) bits\s*$|\1|p' <<< "${dump}") ++ else ++ echo "${DEV} is not a supported LUKS device!" >&2 ++ return 1 ++ fi ++ local bits ++ bits=$(sort -n <<< "${filter}" | tail -n 1) ++ pwmake "${bits}" ++} ++ ++# clevis_luks1_save_slot() works with LUKS1 devices and it saves a given JWE ++# to a specific device and slot. The last parameter indicates whether we ++# should overwrite existing metadata. ++clevis_luks1_save_slot() { ++ local DEV="${1}" ++ local SLOT="${2}" ++ local JWE="${3}" ++ local OVERWRITE="${4}" ++ ++ ! luksmeta test -d "${DEV}" && return 1 ++ ++ local UUID="cb6e8904-81ff-40da-a84a-07ab9ab5715e" ++ if luksmeta load -d "${DEV}" -s "${SLOT}" -u "${UUID}" >/dev/null 2>/dev/null; then ++ [ -z "${OVERWRITE}" ] && return 1 ++ if ! luksmeta wipe -f -d "${DEV}" -s "${SLOT}" -u "${UUID}"; then ++ echo "Error wiping slot ${SLOT} from ${DEV}" >&2 ++ return 1 ++ fi ++ fi ++ ++ if ! echo -n "${jwe}" | luksmeta save -d "${DEV}" -s "${SLOT}" -u "${UUID}"; then ++ echo "Error saving metadata to LUKSMeta slot ${SLOT} from ${DEV}" >&2 ++ return 1 ++ fi ++ return 0 ++} ++ ++# clevis_luks2_save_slot() works with LUKS2 devices and it saves a given JWE ++# to a specific device and slot. The last parameter indicates whether we ++# should overwrite existing metadata. ++clevis_luks2_save_slot() { ++ local DEV="${1}" ++ local SLOT="${2}" ++ local JWE="${3}" ++ local OVERWRITE="${4}" ++ ++ local dump token ++ dump="$(cryptsetup luksDump "${DEV}")" ++ if ! token="$(grep -E -B1 "^\s+Keyslot:\s+${SLOT}$" <<< "${dump}" \ ++ | sed -rn 's|^\s+([0-9]+): clevis|\1|p')"; then ++ echo "Error trying to read token from LUKS2 device ${DEV}, slot ${SLOT}" >&2 ++ return 1 ++ fi ++ ++ if [ -n "${token}" ]; then ++ [ -z "${OVERWRITE}" ] && return 1 ++ if ! cryptsetup token remove --token-id "${token}" "${DEV}"; then ++ echo "Error while removing token ${token} from LUKS2 device ${DEV}" >&2 ++ return 1 ++ fi ++ fi ++ ++ local metadata ++ metadata=$(printf '{"type":"clevis","keyslots":["%s"],"jwe":%s}' \ ++ "${SLOT}" "$(jose jwe fmt -i- <<< "${JWE}")") ++ if ! cryptsetup token import "${DEV}" <<< "${metadata}"; then ++ echo "Error saving metadata to LUKS2 header in device ${DEV}" >&2 ++ return 1 ++ fi ++ return 0 ++} ++ ++# clevis_luks_save_slot() saves a given JWE to a LUKS device+slot. It can also ++# overwrite existing metadata. ++clevis_luks_save_slot() { ++ local DEV="${1}" ++ local SLOT="${2}" ++ local JWE="${3}" ++ local OVERWRITE="${4}" ++ ++ if cryptsetup isLuks --type luks1 "${DEV}"; then ++ ! clevis_luks1_save_slot "${DEV}" "${SLOT}" "${JWE}" "${OVERWRITE}" \ ++ && return 1 ++ elif cryptsetup isLuks --type luks2 "${DEV}"; then ++ ! clevis_luks2_save_slot "${DEV}" "${SLOT}" "${JWE}" "${OVERWRITE}" \ ++ && return 1 ++ else ++ return 1 ++ fi ++ return 0 ++} ++ ++# clevis_luks1_backup_dev() backups the LUKSMeta slots from a LUKS device, ++# which can be restored with clevis_luks1_restore_dev(). ++clevis_luks1_backup_dev() { ++ local DEV="${1}" ++ local TMP="${2}" ++ ++ [ -z "${DEV}" ] && return 1 ++ [ -z "${TMP}" ] && return 1 ++ ++ local slots slt uuid jwe fname ++ readarray -t slots < <(cryptsetup luksDump "${DEV}" \ ++ | sed -rn 's|^Key Slot ([0-7]): ENABLED$|\1|p') ++ ++ for slt in "${slots[@]}"; do ++ if ! uuid=$(luksmeta show -d "${DEV}" -s "${slt}") \ ++ || [ -z "${uuid}" ]; then ++ continue ++ fi ++ if ! jwe=$(luksmeta load -d "${DEV}" -s "${slt}") \ ++ || [ -z "${jwe}" ]; then ++ continue ++ fi ++ ++ fname=$(printf "slot_%s_%s" "${slt}" "${uuid}") ++ printf "%s" "${jwe}" > "${TMP}/${fname}" ++ done ++ return 0 ++} ++ ++# clevis_luks1_restore_dev() takes care of restoring the LUKSMeta slots from ++# a LUKS device that was backup'ed by clevis_luks1_backup_dev(). ++clevis_luks1_restore_dev() { ++ local DEV="${1}" ++ local TMP="${2}" ++ ++ [ -z "${DEV}" ] && return 1 ++ [ -z "${TMP}" ] && return 1 ++ ++ local slt uuid jwe fname ++ for fname in "${TMP}"/slot_*; do ++ [ -f "${fname}" ] || break ++ if ! slt=$(echo "${fname}" | cut -d '_' -f 2) \ ++ || [ -z "${slt}" ]; then ++ continue ++ fi ++ if ! uuid=$(echo "${fname}" | cut -d '_' -f 3) \ ++ || [ -z "${uuid}" ]; then ++ continue ++ fi ++ if ! jwe=$(< "${fname}") || [ -z "${jwe}" ]; then ++ continue ++ fi ++ if ! clevis_luks1_save_slot "${DEV}" "${slt}" \ ++ "${jwe}" "overwrite"; then ++ echo "Error restoring LUKSmeta slot ${slt} from ${DEV}" >&2 ++ return 1 ++ fi ++ done ++ return 0 ++} ++ ++# clevis_luks_backup_dev() backups a particular LUKS device, which can then ++# be restored with clevis_luks_restore_dev(). ++clevis_luks_backup_dev() { ++ local DEV="${1}" ++ local TMP="${2}" ++ ++ [ -z "${DEV}" ] && return 1 ++ [ -z "${TMP}" ] && return 1 ++ ++ local HDR ++ HDR="${TMP}/$(basename "${DEV}").header" ++ if ! cryptsetup luksHeaderBackup "${DEV}" --batch-mode \ ++ --header-backup-file "${HDR}"; then ++ echo "Error backing up LUKS header from ${DEV}" >&2 ++ return 1 ++ fi ++ ++ # If LUKS1, we need to manually back up (and later restore) the ++ # LUKSmeta slots. For LUKS2, simply saving the header also saves ++ # the associated tokens. ++ if cryptsetup isLuks --type luks1 "${DEV}"; then ++ if ! clevis_luks1_backup_dev "${DEV}" "${TMP}"; then ++ return 1 ++ fi ++ fi ++ return 0 ++} ++ ++# clevis_luks_restore_dev() restores a given device that was backup'ed by ++# clevis_luks_backup_dev(). ++clevis_luks_restore_dev() { ++ local DEV="${1}" ++ local TMP="${2}" ++ ++ [ -z "${DEV}" ] && return 1 ++ [ -z "${TMP}" ] && return 1 ++ ++ local HDR ++ HDR="${TMP}/$(basename "${DEV}").header" ++ if [ ! -e "${HDR}" ]; then ++ echo "LUKS header backup does not exist" >&2 ++ return 1 ++ fi ++ ++ if ! cryptsetup luksHeaderRestore "${DEV}" --batch-mode \ ++ --header-backup-file "${HDR}"; then ++ echo "Error restoring LUKS header from ${DEV}" >&2 ++ return 1 ++ fi ++ ++ # If LUKS1, we need to manually back up (and later restore) the ++ # LUKSmeta slots. For LUKS2, simply saving the header also saves ++ # the associated tokens. ++ if cryptsetup isLuks --type luks1 "${DEV}"; then ++ if ! clevis_luks1_restore_dev "${DEV}" "${TMP}"; then ++ return 1 ++ fi ++ fi ++ return 0 ++} +diff --git a/src/luks/clevis-luks-pass b/src/luks/clevis-luks-pass +index 1ce8c4c..d31bc17 100755 +--- a/src/luks/clevis-luks-pass ++++ b/src/luks/clevis-luks-pass +@@ -63,7 +63,8 @@ if ! jwe=$(clevis_luks_read_slot "${DEV}" "${SLT}" 2>/dev/null); then + exit 1 + fi + +-if ! clevis decrypt < <(echo -n "${jwe}"); then +- echo "It was not possible to decrypt the passphrase associated to slot ${SLT} in {DEV}!" >&2 ++if ! passphrase=$(clevis decrypt < <(echo -n "${jwe}") 2>/dev/null); then ++ echo "It was not possible to decrypt the passphrase associated to slot ${SLT} in ${DEV}!" >&2 + exit 1 + fi ++echo -n "${passphrase}" +diff --git a/src/luks/clevis-luks-regen b/src/luks/clevis-luks-regen +new file mode 100755 +index 0000000..44fd673 +--- /dev/null ++++ b/src/luks/clevis-luks-regen +@@ -0,0 +1,185 @@ ++#!/usr/bin/bash ++# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2018 Red Hat, Inc. ++# Author: Radovan Sroka ++# Author: Sergio Correia ++# ++# 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 . ++# ++ ++. clevis-luks-common-functions ++ ++SUMMARY="Regenerate LUKS metadata" ++ ++if [ "$1" == "--summary" ]; then ++ echo "$SUMMARY" ++ exit 0 ++fi ++ ++function usage_and_exit () { ++ exec >&2 ++ echo "Usage: clevis luks regen -d DEV -s SLOT" ++ echo ++ echo "$SUMMARY" ++ echo ++ echo " -d DEV The LUKS device on which to perform rebinding" ++ echo ++ echo " -s SLT The LUKS slot to use" ++ echo ++ exit "${1}" ++} ++ ++on_exit() { ++ if [ ! -d "${TMP}" ] || ! rm -rf "${TMP}"; then ++ echo "Delete temporary files failed!" >&2 ++ echo "You need to clean up: ${TMP}" >&2 ++ exit 1 ++ fi ++} ++ ++while getopts ":hfd:s:" o; do ++ case "$o" in ++ d) DEV="$OPTARG";; ++ h) usage_and_exit 0;; ++ s) SLT="$OPTARG";; ++ *) usage_and_exit 1;; ++ esac ++done ++ ++if [ -z "$DEV" ]; then ++ echo "Did not specify a device!" >&2 ++ exit 1 ++fi ++ ++if [ -z "$SLT" ]; then ++ echo "Did not specify a slot!" >&2 ++ exit 1 ++fi ++ ++### ---------------------------------------------------------------------- ++if ! pin_cfg=$(clevis luks list -d "${DEV}" -s "${SLT}" 2>/dev/null); then ++ echo "Error obtaining current configuration of device ${DEV}:${SLT}" >&2 ++ exit 1 ++fi ++ ++PIN=$(echo "${pin_cfg}" | awk '{ print $2 }') ++CFG=$(echo "${pin_cfg}" | awk '{ print $3 }' | tr -d "'") ++ ++echo "Regenerating with:" ++echo "PIN: $PIN" ++echo "CONFIG: $CFG" ++ ++trap 'echo "Ignoring CONTROL-C!"' INT TERM ++ ++# Get the existing key. ++if ! existing_key=$(clevis luks pass -d "${DEV}" -s "${SLT}" 2>/dev/null); then ++ # We failed to obtain the passphrase for the slot -- perhaps ++ # it was rotated? --, so let's request user input. ++ read -r -s -p "Enter existing LUKS password: " existing_key; echo ++fi ++ ++# Check if the key is valid. ++if ! cryptsetup open --test-passphrase "${DEV}" <<< "${existing_key}"; then ++ exit 1 ++fi ++ ++# Check if we can do the update in-place, i.e., if the key we got is the one ++# for the slot we are interested in. ++in_place= ++if cryptsetup open --test-passphrase --key-slot "${SLT}" "${DEV}" \ ++ <<< "${existing_key}"; then ++ in_place=true ++fi ++ ++# Create new key. ++if ! new_passphrase=$(generate_key "${DEV}"); then ++ echo "Error generating new key for device ${DEV}" >&2 ++ exit 1 ++fi ++ ++# Reencrypt the new password. ++if ! jwe=$(clevis encrypt "${PIN}" "${CFG}" <<< "${new_passphrase}"); then ++ echo "Error using pin '${PIN}' with config '${CFG}'" >&2 ++ exit 1 ++fi ++ ++# Updating the metadata and the actual passphrase are destructive operations, ++# hence we will do a backup of the LUKS header and restore it later in case ++# we have issues performing these operations. ++if ! TMP="$(mktemp -d)"; then ++ echo "Creating a temporary dir for device backup/restore failed!" >&2 ++ exit 1 ++fi ++trap 'on_exit' EXIT ++ ++# Backup LUKS header. ++if ! clevis_luks_backup_dev "${DEV}" "${TMP}"; then ++ echo "Error while trying to back up LUKS header from ${DEV}" >&2 ++ exit 1 ++fi ++ ++restore_device() { ++ local DEV="${1}" ++ local TMP="${2}" ++ ++ if ! clevis_luks_restore_dev "${DEV}" "${TMP}"; then ++ echo "Error while trying to restore LUKS header from ${DEV}." >&2 ++ else ++ echo "LUKS header restored successfully." >&2 ++ fi ++} ++ ++# Update the key slot with the new key. If we have the key for this slot, ++# the change happens in-place. Otherwise, we kill the slot and re-add it. ++if [ -n "${in_place}" ]; then ++ if ! cryptsetup luksChangeKey "${DEV}" --key-slot "${SLT}" \ ++ <(echo -n "${new_passphrase}") <<< "${existing_key}"; then ++ echo "Error updating LUKS passphrase in ${DEV}:${SLT}" >&2 ++ restore_device "${DEV}" "${TMP}" ++ exit 1 ++ fi ++else ++ if ! cryptsetup luksKillSlot --batch-mode "${DEV}" "${SLT}"; then ++ echo "Error wiping slot ${SLT} from ${DEV}" >&2 ++ restore_device "${DEV}" "${TMP}" ++ exit 1 ++ fi ++ ++ if ! echo -n "${new_passphrase}" \ ++ | cryptsetup luksAddKey --key-slot "${SLT}" \ ++ --key-file <(echo -n "${existing_key}") "${DEV}"; then ++ echo "Error updating LUKS passphrase in ${DEV}:${SLT}." >&2 ++ restore_device "${DEV}" "${TMP}" ++ exit 1 ++ fi ++fi ++ ++# Update the metadata. ++if ! clevis_luks_save_slot "${DEV}" "${SLT}" "${jwe}" "overwrite"; then ++ echo "Error updating metadata in ${DEV}:${SLT}" >&2 ++ restore_device "${DEV}" "${TMP}" ++ exit 1 ++fi ++ ++# Now make sure that we can unlock this device after the change. ++# If we can't, undo the changes. ++if ! cryptsetup open --test-passphrase --key-slot "${SLT}" "${DEV}" 2>/dev/null \ ++ <<< $(clevis luks pass -d "${DEV}" -s "${SLT}" 2>/dev/null); then ++ echo "Invalid configuration detected after rebinding. Reverting changes." ++ restore_device "${DEV}" "${TMP}" ++ exit 1 ++fi ++ ++echo "Keys were succesfully rotated." +diff --git a/src/luks/clevis-luks-regen.1.adoc b/src/luks/clevis-luks-regen.1.adoc +new file mode 100644 +index 0000000..763fa1e +--- /dev/null ++++ b/src/luks/clevis-luks-regen.1.adoc +@@ -0,0 +1,48 @@ ++CLEVIS-LUKS-REGEN(1) ++===================== ++:doctype: manpage ++ ++ ++== NAME ++ ++clevis-luks-regen - Regenerates LUKS binding ++ ++== SYNOPSIS ++ ++*clevis luks regen* -d DEV -s SLT ++ ++== OVERVIEW ++ ++The *clevis luks regen* command regenerates the LUKS binding for a given slot in a LUKS device, using the same configuration of the ++existing binding. Its operation can be compared to performing *clevis luks unbind* and *clevis luks bind* for rebinding said slot and device. ++This is useful when rotating keys. ++ ++== OPTIONS ++ ++* *-d* _DEV_ : ++ The bound LUKS device ++ ++* *-s* _SLT_ : ++ The slot or key slot number for rebinding. Note that it requires that such slot is currently bound by clevis. ++ ++== EXAMPLE ++ ++ Let's start by using clevis luks list to see the current binding configuration in /dev/sda1: ++ ++ # clevis luks list -d /dev/sda1 ++ 1: tang '{"url":"http://tang.server"}' ++ 2: tpm2 '{"hash":"sha256","key":"ecc"}' ++ ++ We see that slot 1 in /dev/sda1 has a tang binding with the following configuration: ++ '{"url":"http://tang.server"}' ++ ++ Now let's do the rebinding of slot 1: ++ # clevis luks regen -d /dev/sda1 -s 1 ++ ++ After a successful operation, we will have the new binding using the same configuration that was already in place. ++ ++== SEE ALSO ++ ++link:clevis-luks-list.1.adoc[*clevis-luks-list*(1)] ++link:clevis-luks-bind.1.adoc[*clevis-luks-bind*(1)] ++link:clevis-luks-unbind.1.adoc[*clevis-luks-unbind*(1)] +diff --git a/src/luks/meson.build b/src/luks/meson.build +index fda2ca8..f21388d 100644 +--- a/src/luks/meson.build ++++ b/src/luks/meson.build +@@ -44,6 +44,9 @@ if libcryptsetup.found() and luksmeta.found() and pwmake.found() + + bins += join_paths(meson.current_source_dir(), 'clevis-luks-pass') + mans += join_paths(meson.current_source_dir(), 'clevis-luks-pass.1') ++ ++ bins += join_paths(meson.current_source_dir(), 'clevis-luks-regen') ++ mans += join_paths(meson.current_source_dir(), 'clevis-luks-regen.1') + else + warning('Will not install LUKS support due to missing dependencies!') + endif +diff --git a/src/luks/tests/backup-restore-luks1 b/src/luks/tests/backup-restore-luks1 +new file mode 100755 +index 0000000..733a4b6 +--- /dev/null ++++ b/src/luks/tests/backup-restore-luks1 +@@ -0,0 +1,114 @@ ++#!/bin/bash -x ++# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2020 Red Hat, Inc. ++# Author: Sergio Correia ++# ++# 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 . ++# ++ ++TEST="${0}" ++. tests-common-functions ++. clevis-luks-common-functions ++ ++function on_exit() { ++ if [ "$PID" ]; then kill $PID; wait $PID || true; fi ++ [ -d "$TMP" ] && rm -rf $TMP ++} ++ ++trap 'on_exit' EXIT ++trap 'exit' ERR ++ ++export TMP=$(mktemp -d) ++mkdir -p "${TMP}/db" ++ ++# Generate the server keys ++KEYS="$TMP/db" ++tangd-keygen $TMP/db sig exc ++if which tangd-update; then ++ mkdir -p "${TMP}/cache" ++ tangd-update "${TMP}/db" "${TMP}/cache" ++ KEYS="${TMP}/cache" ++fi ++ ++# Start the server. ++port=$(shuf -i 1024-65536 -n 1) ++"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" & ++export PID=$! ++sleep 0.25 ++ ++url="http://localhost:${port}" ++adv="${TMP}/adv" ++curl "${url}/adv" -o "${adv}" ++ ++cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv") ++ ++# LUKS1. ++DEV="${TMP}/luks1-device" ++new_device "luks1" "${DEV}" ++ ++SLT=5 ++if ! clevis luks bind -f -s "${SLT}" -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: Bind should have succeeded." ++fi ++ ++ORIG_DEV="${TMP}/device.orig" ++# Let's save it for comparison later. ++cp -f "${DEV}" "${ORIG_DEV}" ++ ++# Now let's backup and restore. ++if ! clevis_luks_backup_dev "${DEV}" "${TMP}"; then ++ error "${TEST}: backup of ${DEV} failed." ++fi ++ ++# Now let's remove both the binding and the initial passphrase. ++if ! clevis luks unbind -f -s "${SLT}" -d "${DEV}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: unbind of slot ${SLT} in ${DEV} failed." ++fi ++ ++if ! cryptsetup luksRemoveKey --batch-mode "${DEV}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: error removing the default password from ${DEV}." ++fi ++ ++# Making sure we have no slots enabled. ++enabled=$(cryptsetup luksDump "${DEV}" | grep ENABLED | wc -l) ++if [ "${enabled}" -ne 0 ]; then ++ error "${TEST}: we should not have any enabled (${enabled}) slots." ++fi ++ ++# Now we can restore it. ++if ! clevis_luks_restore_dev "${DEV}" "${TMP}"; then ++ error "${TEST}: error restoring ${DEV}." ++fi ++ ++# And compare whether they are the same. ++if ! cmp --silent "${ORIG_DEV}" "${DEV}"; then ++ error "${TEST}: the device differs from the original one after the restore." ++fi ++ ++# And making sure both the default passphrase and the binding work. ++if ! cryptsetup open --test-passphrase "${DEV}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: the default passphrase for ${DEV} did no work." ++fi ++ ++TEST_DEV="test-device-${RANDOM}" ++if ! clevis luks unlock -d "${DEV}" -n "${TEST_DEV}"; then ++ error "${TEST}: we were unable to unlock ${DEV}." ++fi ++ ++cryptsetup close "${TEST_DEV}" ++ ++kill -9 "${PID}" ++! wait "${PID}" ++unset PID +diff --git a/src/luks/tests/backup-restore-luks2 b/src/luks/tests/backup-restore-luks2 +new file mode 100755 +index 0000000..a3b8608 +--- /dev/null ++++ b/src/luks/tests/backup-restore-luks2 +@@ -0,0 +1,115 @@ ++#!/bin/bash -x ++# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2020 Red Hat, Inc. ++# Author: Sergio Correia ++# ++# 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 . ++# ++ ++TEST="${0}" ++. tests-common-functions ++. clevis-luks-common-functions ++ ++function on_exit() { ++ if [ "$PID" ]; then kill $PID; wait $PID || true; fi ++ [ -d "$TMP" ] && rm -rf $TMP ++} ++ ++trap 'on_exit' EXIT ++trap 'exit' ERR ++ ++export TMP=$(mktemp -d) ++mkdir -p "${TMP}/db" ++ ++# Generate the server keys ++KEYS="$TMP/db" ++tangd-keygen $TMP/db sig exc ++if which tangd-update; then ++ mkdir -p "${TMP}/cache" ++ tangd-update "${TMP}/db" "${TMP}/cache" ++ KEYS="${TMP}/cache" ++fi ++ ++# Start the server. ++port=$(shuf -i 1024-65536 -n 1) ++"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" & ++export PID=$! ++sleep 0.25 ++ ++url="http://localhost:${port}" ++adv="${TMP}/adv" ++curl "${url}/adv" -o "${adv}" ++ ++cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv") ++ ++# LUKS2. ++DEV="${TMP}/luks2-device" ++new_device "luks2" "${DEV}" ++ ++SLT=5 ++if ! clevis luks bind -f -s "${SLT}" -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: Bind should have succeeded." ++fi ++ ++ORIG_DEV="${TMP}/device.orig" ++# Let's save it for comparison later. ++cp -f "${DEV}" "${ORIG_DEV}" ++ ++# Now let's backup and restore. ++if ! clevis_luks_backup_dev "${DEV}" "${TMP}"; then ++ error "${TEST}: backup of ${DEV} failed." ++fi ++ ++# Now let's remove both the binding and the initial passphrase. ++if ! clevis luks unbind -f -s "${SLT}" -d "${DEV}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: unbind of slot ${SLT} in ${DEV} failed." ++fi ++ ++if ! cryptsetup luksRemoveKey --batch-mode "${DEV}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: error removing the default password from ${DEV}." ++fi ++ ++# Making sure we have no slots enabled. ++enabled=$(cryptsetup luksDump "${DEV}" \ ++ | sed -rn 's|^\s+([0-9]+): luks2$|\1|p' | wc -l) ++if [ "${enabled}" -ne 0 ]; then ++ error "${TEST}: we should not have any enabled (${enabled}) slots." ++fi ++ ++# Now we can restore it. ++if ! clevis_luks_restore_dev "${DEV}" "${TMP}"; then ++ error "${TEST}: error restoring ${DEV}." ++fi ++ ++# And compare whether they are the same. ++if ! cmp --silent "${ORIG_DEV}" "${DEV}"; then ++ error "${TEST}: the device differs from the original one after the restore." ++fi ++ ++# And making sure both the default passphrase and the binding work. ++if ! cryptsetup open --test-passphrase "${DEV}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: the default passphrase for ${DEV} did no work." ++fi ++ ++TEST_DEV="test-device-${RANDOM}" ++if ! clevis luks unlock -d "${DEV}" -n "${TEST_DEV}"; then ++ error "${TEST}: we were unable to unlock ${DEV}." ++fi ++ ++cryptsetup close "${TEST_DEV}" ++ ++kill -9 "${PID}" ++! wait "${PID}" ++unset PID +diff --git a/src/luks/tests/meson.build b/src/luks/tests/meson.build +index 4757c4b..dbef9bf 100644 +--- a/src/luks/tests/meson.build ++++ b/src/luks/tests/meson.build +@@ -87,6 +87,9 @@ if has_tang + test('unlock-tang-luks1', find_program('unlock-tang-luks1'), env: env, timeout: 90) + endif + test('pass-tang-luks1', find_program('pass-tang-luks1'), env: env) ++test('backup-restore-luks1', find_program('backup-restore-luks1'), env: env) ++test('regen-inplace-luks1', find_program('regen-inplace-luks1'), env: env, timeout: 90) ++test('regen-not-inplace-luks1', find_program('regen-not-inplace-luks1'), env: env, timeout: 90) + + # LUKS2 tests go here, and they get included if we get support for it, based + # on the cryptsetup version. +@@ -107,4 +110,7 @@ if luksmeta_data.get('OLD_CRYPTSETUP') == '0' + test('unlock-tang-luks2', find_program('unlock-tang-luks2'), env: env, timeout: 120) + endif + test('pass-tang-luks2', find_program('pass-tang-luks2'), env: env, timeout: 60) ++ test('backup-restore-luks2', find_program('backup-restore-luks2'), env:env, timeout: 90) ++ test('regen-inplace-luks2', find_program('regen-inplace-luks2'), env: env, timeout: 90) ++ test('regen-not-inplace-luks2', find_program('regen-not-inplace-luks2'), env: env, timeout: 90) + endif +diff --git a/src/luks/tests/meson.build.orig b/src/luks/tests/meson.build.orig +new file mode 100644 +index 0000000..4757c4b +--- /dev/null ++++ b/src/luks/tests/meson.build.orig +@@ -0,0 +1,110 @@ ++actv = find_program( ++ 'systemd-socket-activate', ++ 'systemd-activate', ++ required: false ++) ++ ++# We use jq for comparing the pin config in the clevis luks list tests. ++jq = find_program('jq', required: false) ++ ++# we use systemd-socket-activate for running test tang servers. ++actv = find_program( ++ 'systemd-socket-activate', ++ 'systemd-activate', ++ required: false ++) ++ ++kgen = find_program( ++ join_paths(libexecdir, 'tangd-keygen'), ++ join_paths(get_option('prefix'), get_option('libdir'), 'tangd-keygen'), ++ join_paths(get_option('prefix'), get_option('libexecdir'), 'tangd-keygen'), ++ join_paths('/', 'usr', get_option('libdir'), 'tangd-keygen'), ++ join_paths('/', 'usr', get_option('libexecdir'), 'tangd-keygen'), ++ required: false ++) ++tang = find_program( ++ join_paths(libexecdir, 'tangd'), ++ join_paths(get_option('prefix'), get_option('libdir'), 'tangd'), ++ join_paths(get_option('prefix'), get_option('libexecdir'), 'tangd'), ++ join_paths('/', 'usr', get_option('libdir'), 'tangd'), ++ join_paths('/', 'usr', get_option('libexecdir'), 'tangd'), ++ required: false ++) ++ ++common_functions = configure_file(input: 'tests-common-functions.in', ++ output: 'tests-common-functions', ++ configuration: luksmeta_data, ++ install: false ++) ++ ++env = environment() ++env.prepend('PATH', ++ join_paths(meson.source_root(), 'src'), ++ join_paths(meson.source_root(), 'src', 'luks'), ++ join_paths(meson.source_root(), 'src', 'pins', 'sss'), ++ join_paths(meson.source_root(), 'src', 'pins', 'tang'), ++ join_paths(meson.source_root(), 'src', 'pins', 'tpm2'), ++ meson.current_source_dir(), ++ meson.current_build_dir(), ++ join_paths(meson.build_root(), 'src'), ++ join_paths(meson.build_root(), 'src', 'luks'), ++ join_paths(meson.build_root(), 'src', 'pins', 'sss'), ++ join_paths(meson.build_root(), 'src', 'pins', 'tang'), ++ join_paths(meson.build_root(), 'src', 'pins', 'tpm2'), ++ libexecdir, ++ '/usr/libexec', ++ separator: ':' ++) ++env.set('SD_ACTIVATE', actv.path()) ++ ++has_tang = false ++if actv.found() and kgen.found() and tang.found() ++ has_tang = true ++ env.set('SD_ACTIVATE', actv.path()) ++ env.set('TANGD_KEYGEN', kgen.path()) ++ env.set('TANGD', tang.path()) ++endif ++ ++test('bind-wrong-pass-luks1', find_program('bind-wrong-pass-luks1'), env: env) ++test('bind-luks1', find_program('bind-luks1'), env: env) ++test('unbind-unbound-slot-luks1', find_program('unbind-unbound-slot-luks1'), env: env) ++test('unbind-luks1', find_program('unbind-luks1'), env: env) ++test('bind-key-file-non-interactive', find_program('bind-key-file-non-interactive-luks1'), env: env) ++test('bind-pass-with-newline', find_program('bind-pass-with-newline-luks1'), env: env) ++test('bind-pass-with-newline-keyfile', find_program('bind-pass-with-newline-keyfile-luks1'), env: env) ++# Bug #70. ++test('bind-already-used-luksmeta-slot', find_program('bind-already-used-luksmeta-slot'), env: env, timeout: 60) ++ ++if jq.found() ++ test('list-recursive-luks1', find_program('list-recursive-luks1'), env: env) ++ test('list-tang-luks1', find_program('list-tang-luks1'), env: env) ++ test('list-sss-tang-luks1', find_program('list-sss-tang-luks1'), env: env) ++else ++ warning('Will not run "clevis luks list" tests due to missing jq dependency') ++endif ++ ++if has_tang ++ test('unlock-tang-luks1', find_program('unlock-tang-luks1'), env: env, timeout: 90) ++endif ++test('pass-tang-luks1', find_program('pass-tang-luks1'), env: env) ++ ++# LUKS2 tests go here, and they get included if we get support for it, based ++# on the cryptsetup version. ++# Binding LUKS2 takes longer, so timeout is increased for a few tests. ++if luksmeta_data.get('OLD_CRYPTSETUP') == '0' ++ test('bind-wrong-pass-luks2', find_program('bind-wrong-pass-luks2'), env: env) ++ test('bind-luks2', find_program('bind-luks2'), env: env, timeout: 60) ++ test('unbind-unbound-slot-luks2', find_program('unbind-unbound-slot-luks2'), env: env) ++ test('unbind-luks2', find_program('unbind-luks2'), env: env, timeout: 60) ++ ++ if jq.found() ++ test('list-recursive-luks2', find_program('list-recursive-luks2'), env: env, timeout: 60) ++ test('list-tang-luks2', find_program('list-tang-luks2'), env: env, timeout: 60) ++ test('list-sss-tang-luks2', find_program('list-sss-tang-luks2'), env: env, timeout: 60) ++ endif ++ ++ if has_tang ++ test('unlock-tang-luks2', find_program('unlock-tang-luks2'), env: env, timeout: 120) ++ endif ++ test('pass-tang-luks2', find_program('pass-tang-luks2'), env: env, timeout: 60) ++endif +diff --git a/src/luks/tests/regen-inplace-luks1 b/src/luks/tests/regen-inplace-luks1 +new file mode 100755 +index 0000000..3a42ced +--- /dev/null ++++ b/src/luks/tests/regen-inplace-luks1 +@@ -0,0 +1,98 @@ ++#!/bin/bash -x ++# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2020 Red Hat, Inc. ++# Author: Sergio Correia ++# ++# 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 . ++# ++ ++TEST="${0}" ++. tests-common-functions ++ ++function on_exit() { ++ if [ "$PID" ]; then kill $PID; wait $PID || true; fi ++ [ -d "$TMP" ] && rm -rf $TMP ++} ++ ++trap 'on_exit' EXIT ++trap 'exit' ERR ++ ++export TMP=$(mktemp -d) ++mkdir -p "${TMP}/db" ++ ++# Generate the server keys ++KEYS="$TMP/db" ++tangd-keygen $TMP/db sig exc ++if which tangd-update; then ++ mkdir -p "${TMP}/cache" ++ tangd-update "${TMP}/db" "${TMP}/cache" ++ KEYS="${TMP}/cache" ++fi ++ ++# Start the server. ++port=$(shuf -i 1024-65536 -n 1) ++"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" & ++export PID=$! ++sleep 0.25 ++ ++url="http://localhost:${port}" ++adv="${TMP}/adv" ++curl "${url}/adv" -o "${adv}" ++ ++cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv") ++ ++# LUKS1. ++DEV="${TMP}/luks1-device" ++new_device "luks1" "${DEV}" ++ ++SLT=1 ++if ! clevis luks bind -f -s "${SLT}" -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: Bind should have succeeded." ++fi ++ ++# Now let's remove the initial passphrase. ++if ! cryptsetup luksRemoveKey --batch-mode "${DEV}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: error removing the default password from ${DEV}." ++fi ++ ++# Making sure we have a single slot enabled. ++enabled=$(cryptsetup luksDump "${DEV}" | grep ENABLED | wc -l) ++if [ "${enabled}" -ne 1 ]; then ++ error "${TEST}: we should have only one slot enabled (${enabled})." ++fi ++ ++old_key=$(clevis luks pass -d "${DEV}" -s "${SLT}") ++ ++# Now let's try regen. ++if ! clevis_regen "${DEV}" "${SLT}" "${DEFAULT_PASS}"; then ++ error "${TEST}: clevis luks regen failed" ++fi ++ ++new_key=$(clevis luks pass -d "${DEV}" -s "${SLT}") ++ ++if [ "${old_key}" = "${new_key}" ]; then ++ error "${TEST}: the passphrases should be different" ++fi ++ ++TEST_DEV="test-device-${RANDOM}" ++if ! clevis luks unlock -d "${DEV}" -n "${TEST_DEV}"; then ++ error "${TEST}: we were unable to unlock ${DEV}." ++fi ++ ++cryptsetup close "${TEST_DEV}" ++ ++kill -9 "${PID}" ++! wait "${PID}" ++unset PID +diff --git a/src/luks/tests/regen-inplace-luks2 b/src/luks/tests/regen-inplace-luks2 +new file mode 100755 +index 0000000..1cb7a29 +--- /dev/null ++++ b/src/luks/tests/regen-inplace-luks2 +@@ -0,0 +1,99 @@ ++#!/bin/bash -x ++# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2020 Red Hat, Inc. ++# Author: Sergio Correia ++# ++# 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 . ++# ++ ++TEST="${0}" ++. tests-common-functions ++ ++function on_exit() { ++ if [ "$PID" ]; then kill $PID; wait $PID || true; fi ++ [ -d "$TMP" ] && rm -rf $TMP ++} ++ ++trap 'on_exit' EXIT ++trap 'exit' ERR ++ ++export TMP=$(mktemp -d) ++mkdir -p "${TMP}/db" ++ ++# Generate the server keys ++KEYS="$TMP/db" ++tangd-keygen $TMP/db sig exc ++if which tangd-update; then ++ mkdir -p "${TMP}/cache" ++ tangd-update "${TMP}/db" "${TMP}/cache" ++ KEYS="${TMP}/cache" ++fi ++ ++# Start the server. ++port=$(shuf -i 1024-65536 -n 1) ++"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" & ++export PID=$! ++sleep 0.25 ++ ++url="http://localhost:${port}" ++adv="${TMP}/adv" ++curl "${url}/adv" -o "${adv}" ++ ++cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv") ++ ++# LUKS2. ++DEV="${TMP}/luks2-device" ++new_device "luks2" "${DEV}" ++ ++SLT=1 ++if ! clevis luks bind -f -s "${SLT}" -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: Bind should have succeeded." ++fi ++ ++# Now let's remove the initial passphrase. ++if ! cryptsetup luksRemoveKey --batch-mode "${DEV}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: error removing the default password from ${DEV}." ++fi ++ ++# Making sure we have a single slot enabled. ++enabled=$(cryptsetup luksDump "${DEV}" \ ++ | sed -rn 's|^\s+([0-9]+): luks2$|\1|p' | wc -l) ++if [ "${enabled}" -ne 1 ]; then ++ error "${TEST}: we should have only one slot enabled (${enabled})." ++fi ++ ++old_key=$(clevis luks pass -d "${DEV}" -s "${SLT}") ++ ++# Now let's try regen. ++if ! clevis_regen "${DEV}" "${SLT}" "${DEFAULT_PASS}"; then ++ error "${TEST}: clevis luks regen failed" ++fi ++ ++new_key=$(clevis luks pass -d "${DEV}" -s "${SLT}") ++ ++if [ "${old_key}" = "${new_key}" ]; then ++ error "${TEST}: the passphrases should be different" ++fi ++ ++TEST_DEV="test-device-${RANDOM}" ++if ! clevis luks unlock -d "${DEV}" -n "${TEST_DEV}"; then ++ error "${TEST}: we were unable to unlock ${DEV}." ++fi ++ ++cryptsetup close "${TEST_DEV}" ++ ++kill -9 "${PID}" ++! wait "${PID}" ++unset PID +diff --git a/src/luks/tests/regen-not-inplace-luks1 b/src/luks/tests/regen-not-inplace-luks1 +new file mode 100755 +index 0000000..1b65ca7 +--- /dev/null ++++ b/src/luks/tests/regen-not-inplace-luks1 +@@ -0,0 +1,95 @@ ++#!/bin/bash -x ++# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2020 Red Hat, Inc. ++# Author: Sergio Correia ++# ++# 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 . ++# ++ ++TEST="${0}" ++. tests-common-functions ++ ++function on_exit() { ++ if [ "$PID" ]; then kill $PID; wait $PID || true; fi ++ [ -d "$TMP" ] && rm -rf $TMP ++} ++ ++trap 'on_exit' EXIT ++trap 'exit' ERR ++ ++export TMP=$(mktemp -d) ++mkdir -p "${TMP}/db" ++ ++# Generate the server keys ++KEYS="$TMP/db" ++tangd-keygen $TMP/db sig exc ++if which tangd-update; then ++ mkdir -p "${TMP}/cache" ++ tangd-update "${TMP}/db" "${TMP}/cache" ++ KEYS="${TMP}/cache" ++fi ++ ++# Start the server. ++port=$(shuf -i 1024-65536 -n 1) ++"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" & ++export PID=$! ++sleep 0.25 ++ ++url="http://localhost:${port}" ++adv="${TMP}/adv" ++curl "${url}/adv" -o "${adv}" ++ ++cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv") ++ ++# LUKS1. ++DEV="${TMP}/luks1-device" ++new_device "luks1" "${DEV}" ++ ++SLT=1 ++if ! clevis luks bind -f -s "${SLT}" -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: Bind should have succeeded." ++fi ++ ++# Now let's rotate the keys in the server and remove the old ones, so that we ++# will be unable to unlock the volume using clevis and will have to provide ++# manually a password for clevis luks regen. ++rm -rf "${TMP}"/db/* ++tangd-keygen $TMP/db sig exc ++if which tangd-update; then ++ mkdir -p "${TMP}/cache" ++ tangd-update "${TMP}/db" "${TMP}/cache" ++fi ++ ++# Making sure we have two slots enabled. ++enabled=$(cryptsetup luksDump "${DEV}" | grep ENABLED | wc -l) ++if [ "${enabled}" -ne 2 ]; then ++ error "${TEST}: we should have two slots enabled (${enabled})." ++fi ++ ++# Now let's try regen. ++if ! clevis_regen "${DEV}" "${SLT}" "${DEFAULT_PASS}"; then ++ error "${TEST}: clevis luks regen failed" ++fi ++ ++TEST_DEV="test-device-${RANDOM}" ++if ! clevis luks unlock -d "${DEV}" -n "${TEST_DEV}"; then ++ error "${TEST}: we were unable to unlock ${DEV}." ++fi ++ ++cryptsetup close "${TEST_DEV}" ++ ++kill -9 "${PID}" ++! wait "${PID}" ++unset PID +diff --git a/src/luks/tests/regen-not-inplace-luks2 b/src/luks/tests/regen-not-inplace-luks2 +new file mode 100755 +index 0000000..dc91449 +--- /dev/null ++++ b/src/luks/tests/regen-not-inplace-luks2 +@@ -0,0 +1,96 @@ ++#!/bin/bash -x ++# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2020 Red Hat, Inc. ++# Author: Sergio Correia ++# ++# 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 . ++# ++ ++TEST="${0}" ++. tests-common-functions ++ ++function on_exit() { ++ if [ "$PID" ]; then kill $PID; wait $PID || true; fi ++ [ -d "$TMP" ] && rm -rf $TMP ++} ++ ++trap 'on_exit' EXIT ++trap 'exit' ERR ++ ++export TMP=$(mktemp -d) ++mkdir -p "${TMP}/db" ++ ++# Generate the server keys ++KEYS="$TMP/db" ++tangd-keygen $TMP/db sig exc ++if which tangd-update; then ++ mkdir -p "${TMP}/cache" ++ tangd-update "${TMP}/db" "${TMP}/cache" ++ KEYS="${TMP}/cache" ++fi ++ ++# Start the server. ++port=$(shuf -i 1024-65536 -n 1) ++"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" & ++export PID=$! ++sleep 0.25 ++ ++url="http://localhost:${port}" ++adv="${TMP}/adv" ++curl "${url}/adv" -o "${adv}" ++ ++cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv") ++ ++# LUKS2. ++DEV="${TMP}/luks2-device" ++new_device "luks2" "${DEV}" ++ ++SLT=1 ++if ! clevis luks bind -f -s "${SLT}" -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: Bind should have succeeded." ++fi ++ ++# Now let's rotate the keys in the server and remove the old ones, so that we ++# will be unable to unlock the volume using clevis and will have to provide ++# manually a password for clevis luks regen. ++rm -rf "${TMP}"/db/* ++tangd-keygen $TMP/db sig exc ++if which tangd-update; then ++ mkdir -p "${TMP}/cache" ++ tangd-update "${TMP}/db" "${TMP}/cache" ++fi ++ ++# Making sure we have two slots enabled. ++enabled=$(cryptsetup luksDump "${DEV}" \ ++ | sed -rn 's|^\s+([0-9]+): luks2$|\1|p' | wc -l) ++if [ "${enabled}" -ne 2 ]; then ++ error "${TEST}: we should have two slots enabled (${enabled})." ++fi ++ ++# Now let's try regen. ++if ! clevis_regen "${DEV}" "${SLT}" "${DEFAULT_PASS}"; then ++ error "${TEST}: clevis luks regen failed" ++fi ++ ++TEST_DEV="test-device-${RANDOM}" ++if ! clevis luks unlock -d "${DEV}" -n "${TEST_DEV}"; then ++ error "${TEST}: we were unable to unlock ${DEV}." ++fi ++ ++cryptsetup close "${TEST_DEV}" ++ ++kill -9 "${PID}" ++! wait "${PID}" ++unset PID +diff --git a/src/luks/tests/tests-common-functions.in b/src/luks/tests/tests-common-functions.in +index 7b3fdad..6101f28 100755 +--- a/src/luks/tests/tests-common-functions.in ++++ b/src/luks/tests/tests-common-functions.in +@@ -229,5 +229,31 @@ tang_get_adv() { + curl -o "${adv}" http://"${TANG_HOST}":"${port}"/adv + } + ++# Regenerate binding. ++clevis_regen() { ++ local DEV="${1}" ++ local SLT="${2}" ++ local PASS="${3}" ++ ++ expect -d << CLEVIS_REGEN ++ set timeout 120 ++ spawn sh -c "clevis luks regen -d $DEV -s $SLT" ++ expect { ++ "Enter existing LUKS password" { ++ send "$PASS\r" ++ exp_continue ++ } ++ "Do you wish to trust these keys" { ++ send "y\r" ++ exp_continue ++ } ++ expect eof ++ wait ++ } ++CLEVIS_REGEN ++ ret=$? ++ return "${ret}" ++} ++ + export TANG_HOST=127.0.0.1 + export DEFAULT_PASS='just-some-test-password-here' +-- +2.18.4 + diff --git a/SOURCES/0007-Add-clevis-luks-report.patch b/SOURCES/0007-Add-clevis-luks-report.patch new file mode 100644 index 0000000..962e700 --- /dev/null +++ b/SOURCES/0007-Add-clevis-luks-report.patch @@ -0,0 +1,464 @@ +From a85f50f789d69d9ca0a4096a64ac912f5967f97f Mon Sep 17 00:00:00 2001 +From: Sergio Correia +Date: Sun, 10 May 2020 15:32:50 -0300 +Subject: [PATCH 7/8] Add clevis luks report + +--- + src/luks/clevis-luks-report | 95 +++++++++++++++++++++++++++++ + src/luks/clevis-luks-report-compare | 71 +++++++++++++++++++++ + src/luks/clevis-luks-report-decode | 59 ++++++++++++++++++ + src/luks/clevis-luks-report-sss | 53 ++++++++++++++++ + src/luks/clevis-luks-report-tang | 67 ++++++++++++++++++++ + src/luks/clevis-luks-report.1.adoc | 41 +++++++++++++ + src/luks/meson.build | 7 +++ + 7 files changed, 393 insertions(+) + create mode 100755 src/luks/clevis-luks-report + create mode 100755 src/luks/clevis-luks-report-compare + create mode 100755 src/luks/clevis-luks-report-decode + create mode 100755 src/luks/clevis-luks-report-sss + create mode 100755 src/luks/clevis-luks-report-tang + create mode 100644 src/luks/clevis-luks-report.1.adoc + +diff --git a/src/luks/clevis-luks-report b/src/luks/clevis-luks-report +new file mode 100755 +index 0000000..f047256 +--- /dev/null ++++ b/src/luks/clevis-luks-report +@@ -0,0 +1,95 @@ ++#!/usr/bin/bash -e ++# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2018 Red Hat, Inc. ++# Author: Radovan Sroka ++# ++# 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 . ++# ++ ++. clevis-luks-common-functions ++ ++SUMMARY="Report any key rotation on the server side" ++ ++if [ "$1" == "--summary" ]; then ++ echo "$SUMMARY" ++ exit 0 ++fi ++ ++function usage_and_exit () { ++ echo >&2 ++ echo "Usage: clevis luks report [-qr] -d DEV -s SLOT" >&2 ++ echo >&2 ++ echo -e " -q\t Quiet mode" >&2 ++ echo -e " -r\t Regenerate luks metadata with \"clevis luks regen -d DEV -s SLOT\"" >&2 ++ echo >&2 ++ echo "$SUMMARY" >&2 ++ echo >&2 ++ exit "$1" ++} ++ ++while getopts "hd:s:rq" o; do ++ case "$o" in ++ d) DEV="$OPTARG";; ++ h) usage_and_exit 0;; ++ r) ROPT="regen";; ++ s) SLT="$OPTARG";; ++ q) QOPT="quiet";; ++ *) usage_and_exit 1;; ++ esac ++done ++ ++### get luks metadata ++ ++if [ -z "$DEV" ]; then ++ echo "Did not specify a device!" >&2 ++ exit 1 ++fi ++ ++if [ -z "$SLT" ]; then ++ echo "Did not specify a slot!" >&2 ++ exit 1 ++fi ++ ++if ! DATA_CODED=$(clevis_luks_read_slot "${DEV}" "${SLT}"); then ++ # Error message was already displayed by clevis_luks_read_slot(), ++ # at this point. ++ exit 1 ++fi ++ ++EXE="$(findexe clevis-luks-report-decode)" ++RESULT="$($EXE "${DATA_CODED}")" ++ ++if [ -n "$RESULT" ]; then ++ echo "$RESULT" ++ echo "Report detected that some keys were rotated." ++ if [ -z "$QOPT" ]; then ++ if [ -z "$ROPT" ]; then ++ read -r -p "Do you want to regenerate luks metadata with \"clevis luks regen -d $DEV -s $SLT\"? [ynYN] " ans < /dev/tty ++ [[ "$ans" =~ ^[yY]$ ]] && ROPT="regen" ++ fi ++ fi ++else ++ exit 0 ++fi ++ ++if [ "$ROPT" = "regen" ]; then ++ EXE="$(findexe clevis-luks-regen)" ++ exec "$EXE" -d "$DEV" -s "$SLT" ++else ++ if [ -n "${RESULT}" ]; then ++ # Keys were rotated. ++ exit 1 ++ fi ++fi +diff --git a/src/luks/clevis-luks-report-compare b/src/luks/clevis-luks-report-compare +new file mode 100755 +index 0000000..2ba5132 +--- /dev/null ++++ b/src/luks/clevis-luks-report-compare +@@ -0,0 +1,71 @@ ++#!/usr/bin/bash -e ++# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2018 Red Hat, Inc. ++# Author: Radovan Sroka ++# ++# 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="Compare two sets of keys" ++ ++if [ "$1" == "--summary" ]; then ++ echo "$SUMMARY" ++ exit 1 ++fi ++ ++if [ -z "$1" ]; then ++ echo "$0 missing the first argument!" ++ exit 1 ++fi ++ ++if [ -z "$2" ]; then ++ echo "$0 missing the second argument!" ++ exit 1 ++fi ++ ++ADV_KEYS="$1" # keys from advertisement ++LUKS_KEYS="$2" # keys from luks metadata ++ ++### iterate over adv keys and make thumbprints ++CNT=0 ++declare -a ADV_KEYS_ARRAY ++while res="$(jose fmt -j- -g keys -g"$CNT" -o- <<< "$ADV_KEYS")"; do ++ thp="$(echo "$res" | jose jwk thp -i-)" ++ ADV_KEYS_ARRAY["$CNT"]="$thp" ++ CNT=$(( CNT + 1 )) ++done ++ ++CNT=0 ++while key="$(jose fmt -j- -g keys -g"$CNT" -o- <<< "$LUKS_KEYS")"; do ++ thp="$(echo "$key" | jose jwk thp -i-)" ++ ++ FOUND=0 ++ for k in "${ADV_KEYS_ARRAY[@]}" ++ do ++ if [ "$k" = "$thp" ]; then ++ FOUND=1 ++ break ++ fi ++ done ++ ++ if [ "$FOUND" -eq "0" ]; then ++ echo "Key \"$thp\" is not in the advertisement and was probably rotated!" ++ echo "$key" ++ echo ++ fi ++ CNT=$(( CNT + 1 )) ++done ++ ++exit 0 +diff --git a/src/luks/clevis-luks-report-decode b/src/luks/clevis-luks-report-decode +new file mode 100755 +index 0000000..f39d1e9 +--- /dev/null ++++ b/src/luks/clevis-luks-report-decode +@@ -0,0 +1,59 @@ ++#!/usr/bin/bash -e ++# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2018 Red Hat, Inc. ++# Author: Radovan Sroka ++# ++# 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 . ++# ++ ++. clevis-luks-common-functions ++ ++SUMMARY="Decode luks header" ++ ++if [ "$1" == "--summary" ]; then ++ echo "$SUMMARY" ++ exit 1 ++fi ++ ++if [ -z "$1" ]; then ++ echo "$0 missing the first argument!" ++ exit 1 ++fi ++ ++DATA_CODED="$1" ++ ++if DATA_CODED="$(jose jwe fmt -i- <<< "$DATA_CODED")"; then ++ DATA_CODED="$(jose fmt -j- -g protected -u- <<< "$DATA_CODED")" ++ DATA_DECODED="$(jose b64 dec -i- <<< "$DATA_CODED")" ++else ++ echo "Error decoding JWE protected header!" >&2 ++ exit 1 ++fi ++ ++### get pin and url ++ ++if ! PIN="$(jose fmt -j- -g clevis -g pin -u- <<< "$DATA_DECODED")" || [ -z "$PIN" ]; then ++ echo "Pin wasn't found in luks metadata!" >&2 ++ exit 1 ++fi ++ ++if ! CONTENT="$(jose fmt -j- -g clevis -g "$PIN" -o- <<< "$DATA_DECODED")" || [ -z "$CONTENT" ]; then ++ echo "Content wasn't found!" >&2 ++ exit 1 ++fi ++ ++EXE="$(findexe clevis-luks-report-"$PIN")" ++ ++exec "$EXE" "$CONTENT" +diff --git a/src/luks/clevis-luks-report-sss b/src/luks/clevis-luks-report-sss +new file mode 100755 +index 0000000..1dba4c1 +--- /dev/null ++++ b/src/luks/clevis-luks-report-sss +@@ -0,0 +1,53 @@ ++#!/bin/bash -e ++# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2018 Red Hat, Inc. ++# Author: Radovan Sroka ++# ++# 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 . ++# ++ ++. clevis-luks-common-functions ++ ++SUMMARY="SSS report plugin" ++ ++if [ "$1" == "--summary" ]; then ++ echo "$SUMMARY" ++ exit 1 ++fi ++ ++if [ -z "$1" ]; then ++ echo "$0 missing the first argument!" >&2 ++ exit 1 ++fi ++ ++CONTENT="$1" # sss content ++ ++CNT=0 ++while DATA_CODED="$(jose fmt -j- -g jwe -g"$CNT" -u- <<< "$CONTENT")"; do ++ if [ -z "$DATA_CODED" ]; then ++ CNT=$(( CNT + 1 )) ++ continue # in some cases it can be empty string ++ fi ++ ++ EXE="$(findexe clevis-luks-report-decode)" ++ if ! $EXE "$DATA_CODED"; then ++ echo "Failed" >&2 ++ exit 1 ++ fi ++ ++ CNT=$(( CNT + 1 )) ++done ++ ++exit 0 +diff --git a/src/luks/clevis-luks-report-tang b/src/luks/clevis-luks-report-tang +new file mode 100755 +index 0000000..07f2a72 +--- /dev/null ++++ b/src/luks/clevis-luks-report-tang +@@ -0,0 +1,67 @@ ++#!/usr/bin/bash -e ++# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2018 Red Hat, Inc. ++# Author: Radovan Sroka ++# ++# 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 . ++# ++ ++. clevis-luks-common-functions ++ ++SUMMARY="Tang report plugin" ++ ++if [ "$1" == "--summary" ]; then ++ echo "$SUMMARY" ++ exit 1 ++fi ++ ++if [ -z "$1" ]; then ++ echo "$0 missing the first argument!" ++ exit 1 ++fi ++ ++CONTENT="$1" ++ ++### Get the advertisement ++if ! URL="$(jose fmt -j- -g url -u- <<< "$CONTENT")" || [ -z "$URL" ]; then ++ echo "URL was not found!" >&2 ++ exit 1 ++fi ++ ++if ! jws="$(curl -sfg "$URL/adv")"; then ++ echo "Unable to fetch advertisement: $URL/adv!" >&2 ++ exit 1 ++fi ++ ++if ! TANG_KEYS="$(jose fmt -j- -Og payload -SyOg keys -AUo- <<< "$jws")"; then ++ echo "Advertisement is malformed!" >&2 ++ exit 1 ++fi ++ ++### Check advertisement validity ++ver="$(jose jwk use -i- -r -u verify -o- <<< "$TANG_KEYS")" ++if ! jose jws ver -i "$jws" -k- -a <<< "$ver"; then ++ echo "Advertisement is missing signatures!" >&2 ++ exit 1 ++fi ++ ++if ! LUKS_KEYS="$(jose fmt -j- -g adv -o- <<< "$CONTENT")" || [ -z "$LUKS_KEYS" ]; then ++ echo "LUKS keys from LUKS metadata were not found!" >&2 ++ exit 1 ++fi ++ ++EXE="$(findexe clevis-luks-report-compare)" ++ ++exec "$EXE" "$TANG_KEYS" "$LUKS_KEYS" +diff --git a/src/luks/clevis-luks-report.1.adoc b/src/luks/clevis-luks-report.1.adoc +new file mode 100644 +index 0000000..cf42afe +--- /dev/null ++++ b/src/luks/clevis-luks-report.1.adoc +@@ -0,0 +1,41 @@ ++CLEVIS-LUKS-REPORT(1) ++===================== ++:doctype: manpage ++ ++ ++== NAME ++ ++clevis-luks-report - Reports whether a pin bound to a LUKS1 or LUKS2 volume has been rotated ++ ++== SYNOPSIS ++ ++*clevis luks report* -d DEV -s SLT ++ ++== OVERVIEW ++ ++The *clevis luks report* command checks a given slot of a LUKS device and reports whether the pin bound to it ++-- if any -- has been rotated. ++ ++== OPTIONS ++ ++* *-d* _DEV_ : ++ The bound LUKS device ++ ++* *-s* _SLT_ : ++ The slot or key slot number for the pin to be verified ++ ++* *-q* : ++ Quiet mode. If used, we will not prompt whether to regenerate data with *clevis luks regen* ++ ++* *-r* : ++ Regenerates LUKS metadata with *clevis luks regen -d DEV -s SLOT* ++ ++== EXAMPLE ++ ++ Check whether the pin bound to slot 1 in /dev/sda1 has been rotated: ++ ++ # clevis luks report -d /dev/sda1 -s 1 ++ ++== SEE ALSO ++ ++link:clevis-luks-regen.1.adoc[*clevis-luks-regen*(1)] +diff --git a/src/luks/meson.build b/src/luks/meson.build +index f21388d..ee588c3 100644 +--- a/src/luks/meson.build ++++ b/src/luks/meson.build +@@ -47,6 +47,13 @@ if libcryptsetup.found() and luksmeta.found() and pwmake.found() + + bins += join_paths(meson.current_source_dir(), 'clevis-luks-regen') + mans += join_paths(meson.current_source_dir(), 'clevis-luks-regen.1') ++ ++ bins += join_paths(meson.current_source_dir(), 'clevis-luks-report') ++ bins += join_paths(meson.current_source_dir(), 'clevis-luks-report-compare') ++ bins += join_paths(meson.current_source_dir(), 'clevis-luks-report-decode') ++ bins += join_paths(meson.current_source_dir(), 'clevis-luks-report-sss') ++ bins += join_paths(meson.current_source_dir(), 'clevis-luks-report-tang') ++ mans += join_paths(meson.current_source_dir(), 'clevis-luks-report.1') + else + warning('Will not install LUKS support due to missing dependencies!') + endif +-- +2.18.4 + diff --git a/SOURCES/0008-Use-one-clevis-luks-askpass-per-device.patch b/SOURCES/0008-Use-one-clevis-luks-askpass-per-device.patch new file mode 100644 index 0000000..b9f0532 --- /dev/null +++ b/SOURCES/0008-Use-one-clevis-luks-askpass-per-device.patch @@ -0,0 +1,339 @@ +From 3250784e99016d9f920892dbb1438b9e76fb210b Mon Sep 17 00:00:00 2001 +From: Sergio Correia +Date: Sun, 10 May 2020 15:57:23 -0300 +Subject: [PATCH 8/8] Use one clevis-luks-askpass per device + +This should improve the reliability of the boot unlocking, especially +when unlocking multiple devices upon boot. + +It also greatly simplifies the configuration, as there is no need to +enable any systemd units manually nor add _netdev to either fstab or +crypttab. +--- + src/luks/clevis-luks-common-functions | 8 ++ + src/luks/clevis-luks-unlockers.7.adoc | 16 +--- + src/luks/systemd/clevis-luks-askpass | 81 ++++++------------- + src/luks/systemd/clevis-luks-askpass.path | 10 --- + .../systemd/clevis-luks-askpass.service.in | 8 -- + src/luks/systemd/clevis-luks-askpass@.path | 12 +++ + .../systemd/clevis-luks-askpass@.service.in | 8 ++ + .../systemd/dracut/clevis/module-setup.sh.in | 23 ++++++ + src/luks/systemd/meson.build | 6 +- + 9 files changed, 80 insertions(+), 92 deletions(-) + delete mode 100644 src/luks/systemd/clevis-luks-askpass.path + delete mode 100644 src/luks/systemd/clevis-luks-askpass.service.in + create mode 100644 src/luks/systemd/clevis-luks-askpass@.path + create mode 100644 src/luks/systemd/clevis-luks-askpass@.service.in + +diff --git a/src/luks/clevis-luks-common-functions b/src/luks/clevis-luks-common-functions +index 5b515ad..c9d712a 100644 +--- a/src/luks/clevis-luks-common-functions ++++ b/src/luks/clevis-luks-common-functions +@@ -555,3 +555,11 @@ clevis_luks_restore_dev() { + fi + return 0 + } ++ ++# clevis_is_luks_device_by_uuid_open() checks whether the LUKS device with ++# given UUID is open. ++clevis_is_luks_device_by_uuid_open() { ++ local LUKS_UUID="${1}" ++ [ -z "${LUKS_UUID}" ] && return 1 ++ test -b /dev/disk/by-id/dm-uuid-*"${LUKS_UUID//-/}"* ++} +diff --git a/src/luks/clevis-luks-unlockers.7.adoc b/src/luks/clevis-luks-unlockers.7.adoc +index 161b73a..e8d47ba 100644 +--- a/src/luks/clevis-luks-unlockers.7.adoc ++++ b/src/luks/clevis-luks-unlockers.7.adoc +@@ -26,7 +26,7 @@ You can unlock a LUKS volume manually using the following command: + + For more information, see link:clevis-luks-unlock.1.adoc[*clevis-luks-unlock*(1)]. + +-== EARLY BOOT UNLOCKING ++== BOOT UNLOCKING + + If Clevis integration does not already ship in your initramfs, you may need to + rebuild your initramfs with this command: +@@ -34,23 +34,13 @@ rebuild your initramfs with this command: + $ sudo dracut -f + + Once Clevis is integrated into your initramfs, a simple reboot should unlock +-your root volume. Note, however, that early boot integration only works for the +-root volume. Non-root volumes should use the late boot unlocker. ++your clevis-bound volumes. Root volumes will be unlocked in early-boot, while the ++remaining volumes will be unlocked after dracut switch-root. + + Dracut will bring up your network using DHCP by default. If you need to specify + additional network parameters, such as static IP configuration, please consult + the dracut documentation. + +-== LATE BOOT UNLOCKING +- +-You can enable late boot unlocking by executing the following command: +- +- $ sudo systemctl enable clevis-luks-askpass.path +- +-After a reboot, Clevis will attempt to unlock all *_netdev* devices listed in +-*/etc/crypttab* when systemd prompts for their passwords. This implies that +-systemd support for *_netdev* is required. +- + == DESKTOP UNLOCKING + + When the udisks2 unlocker is installed, your GNOME desktop session should +diff --git a/src/luks/systemd/clevis-luks-askpass b/src/luks/systemd/clevis-luks-askpass +index 9fea6aa..20294e5 100755 +--- a/src/luks/systemd/clevis-luks-askpass ++++ b/src/luks/systemd/clevis-luks-askpass +@@ -19,96 +19,61 @@ + # along with this program. If not, see . + # + +-UUID=cb6e8904-81ff-40da-a84a-07ab9ab5715e ++. clevis-luks-common-functions + + shopt -s nullglob + + path=/run/systemd/ask-password +-while getopts ":lp:" o; do ++while getopts ":lp:u:" o; do + case "$o" in + l) loop=true;; + p) path="$OPTARG";; ++ u) device_uuid=$OPTARG;; ++ *) ;; + esac + done + +-luks1_decrypt() { +- luksmeta load "$@" \ +- | clevis decrypt +- +- local rc +- for rc in "${PIPESTATUS[@]}"; do +- [ $rc -eq 0 ] || return $rc +- done +- return 0 +-} +- +-luks2_jwe() { +- # jose jwe fmt -c outputs extra \n, so clean it up +- cryptsetup token export "$@" \ +- | jose fmt -j- -Og jwe -o- \ +- | jose jwe fmt -i- -c \ +- | tr -d '\n' +- +- local rc +- for rc in "${PIPESTATUS[@]}"; do +- [ $rc -eq 0 ] || return $rc +- done +- return 0 +-} +- + while true; do + todo=0 + + for question in "$path"/ask.*; do +- metadata=false + unlocked=false + d= + s= + +- while read line; do ++ while read -r line; do + case "$line" in + Id=cryptsetup:*) d="${line##Id=cryptsetup:}";; + Socket=*) s="${line##Socket=}";; + esac + done < "$question" + +- [ "$d" ] && [ "$s" ] || continue ++ [ -b "${d}" ] || continue ++ [ -S "${s}" ] || continue + +- if cryptsetup isLuks --type luks1 "$d"; then +- # 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 +- metadata=true +- +- if pt="$(luks1_decrypt -d "$d" -s "$slot" -u "$UUID")"; then +- echo -n "+$pt" | ncat -U -u --send-only "$s" +- unlocked=true +- break +- fi +- done < <(luksmeta show -d "$d") +- elif cryptsetup isLuks --type luks2 "$d"; then +- while read -r id; do +- jwe="$(luks2_jwe --token-id "$id" "$d")" \ +- || continue +- metadata=true ++ if [ -n "${device_uuid}" ]; then ++ uuid="$(cryptsetup luksUUID "${d}")" ++ [ "${uuid}" != "${device_uuid}" ] && todo=1 && continue ++ fi + +- if pt="$(echo -n "$jwe" | clevis decrypt)"; then +- echo -n "+$pt" | ncat -U -u --send-only "$s" +- unlocked=true +- break +- fi +- done < <(cryptsetup luksDump "$d" | sed -rn 's|^\s+([0-9]+): clevis|\1|p') ++ if pt="$(clevis_luks_unlock_device "${d}")"; then ++ echo -n "+$pt" | ncat -U -u --send-only "$s" ++ unlocked=true + fi + +- [ "$metadata" == true ] || continue ++ [ -n "${device_uuid}" ] && [ "${unlocked}" == true ] && break + [ "$unlocked" == true ] && continue + ((todo++)) + done + +- if [ $todo -eq 0 ] || [ "$loop" != true ]; then ++ if [ -n "${device_uuid}" ]; then ++ [ ! -b /dev/disk/by-uuid/"${device_uuid}" ] && break ++ if clevis_is_luks_device_by_uuid_open "${device_uuid}"; then ++ break ++ fi ++ fi ++ ++ if [ "$todo" -eq 0 ] || [ "$loop" != true ]; then + break; + fi + +diff --git a/src/luks/systemd/clevis-luks-askpass.path b/src/luks/systemd/clevis-luks-askpass.path +deleted file mode 100644 +index a4d01ba..0000000 +--- a/src/luks/systemd/clevis-luks-askpass.path ++++ /dev/null +@@ -1,10 +0,0 @@ +-[Unit] +-Description=Clevis systemd-ask-password Watcher +-Before=remote-fs-pre.target +-Wants=remote-fs-pre.target +- +-[Path] +-PathChanged=/run/systemd/ask-password +- +-[Install] +-WantedBy=remote-fs.target +diff --git a/src/luks/systemd/clevis-luks-askpass.service.in b/src/luks/systemd/clevis-luks-askpass.service.in +deleted file mode 100644 +index 2c6bbed..0000000 +--- a/src/luks/systemd/clevis-luks-askpass.service.in ++++ /dev/null +@@ -1,8 +0,0 @@ +-[Unit] +-Description=Clevis LUKS systemd-ask-password Responder +-Requires=network-online.target +-After=network-online.target +- +-[Service] +-Type=oneshot +-ExecStart=@libexecdir@/clevis-luks-askpass -l +diff --git a/src/luks/systemd/clevis-luks-askpass@.path b/src/luks/systemd/clevis-luks-askpass@.path +new file mode 100644 +index 0000000..3f23665 +--- /dev/null ++++ b/src/luks/systemd/clevis-luks-askpass@.path +@@ -0,0 +1,12 @@ ++[Unit] ++Description=Clevis systemd-ask-password Watcher for %i ++DefaultDependencies=no ++Conflicts=shutdown.target ++Before=basic.target shutdown.target ++ ++[Path] ++DirectoryNotEmpty=/run/systemd/ask-password ++MakeDirectory=yes ++ ++[Install] ++WantedBy=basic.target +diff --git a/src/luks/systemd/clevis-luks-askpass@.service.in b/src/luks/systemd/clevis-luks-askpass@.service.in +new file mode 100644 +index 0000000..4165ec5 +--- /dev/null ++++ b/src/luks/systemd/clevis-luks-askpass@.service.in +@@ -0,0 +1,8 @@ ++[Unit] ++Description=Clevis LUKS systemd-ask-password Responder for luks-%i ++DefaultDependencies=no ++Conflicts=shutdown.target ++Before=shutdown.target ++ ++[Service] ++ExecStart=@libexecdir@/clevis-luks-askpass -u %i +diff --git a/src/luks/systemd/dracut/clevis/module-setup.sh.in b/src/luks/systemd/dracut/clevis/module-setup.sh.in +index abc79b3..1a0d6f7 100755 +--- a/src/luks/systemd/dracut/clevis/module-setup.sh.in ++++ b/src/luks/systemd/dracut/clevis/module-setup.sh.in +@@ -23,6 +23,24 @@ depends() { + return 255 + } + ++configure_passwd_watchers() { ++ if ! command -v systemctl >/dev/null; then ++ return 1 ++ fi ++ ++ find /etc/systemd/system/ -name "clevis-luks-askpass*" -exec rm -f {} \; ++ ++ local uuid ++ for dev in $(lsblk -p -n -s -r \ ++ | awk '$6 == "crypt" { getline; print $1 }' | sort -u); do ++ uuid=$(cryptsetup luksUUID "${dev}") ++ ++ if clevis luks list -d "${dev}" >/dev/null 2>/dev/null; then ++ systemctl enable "clevis-luks-askpass@${uuid}.path" 2>/dev/null ++ fi ++ done ++} ++ + install() { + inst_hook initqueue/online 60 "$moddir/clevis-hook.sh" + inst_hook initqueue/settled 60 "$moddir/clevis-hook.sh" +@@ -30,6 +48,10 @@ install() { + inst_multiple \ + /etc/services \ + @libexecdir@/clevis-luks-askpass \ ++ clevis-luks-common-functions \ ++ head \ ++ grep \ ++ sed \ + clevis-decrypt \ + cryptsetup \ + luksmeta \ +@@ -38,5 +60,6 @@ install() { + jose \ + ncat + ++ configure_passwd_watchers + dracut_need_initqueue + } +diff --git a/src/luks/systemd/meson.build b/src/luks/systemd/meson.build +index 369e7f7..334e84c 100644 +--- a/src/luks/systemd/meson.build ++++ b/src/luks/systemd/meson.build +@@ -6,13 +6,13 @@ if systemd.found() + unitdir = systemd.get_pkgconfig_variable('systemdsystemunitdir') + + configure_file( +- input: 'clevis-luks-askpass.service.in', +- output: 'clevis-luks-askpass.service', ++ input: 'clevis-luks-askpass@.service.in', ++ output: 'clevis-luks-askpass@.service', + install_dir: unitdir, + configuration: data, + ) + +- install_data('clevis-luks-askpass.path', install_dir: unitdir) ++ install_data('clevis-luks-askpass@.path', install_dir: unitdir) + install_data('clevis-luks-askpass', install_dir: libexecdir) + else + warning('Will not install systemd support due to missing dependencies!') +-- +2.18.4 + diff --git a/SOURCES/0009-Introduce-y-assume-yes-argument-to-clevis-luks-bind.patch b/SOURCES/0009-Introduce-y-assume-yes-argument-to-clevis-luks-bind.patch new file mode 100644 index 0000000..0e54343 --- /dev/null +++ b/SOURCES/0009-Introduce-y-assume-yes-argument-to-clevis-luks-bind.patch @@ -0,0 +1,555 @@ +From 7b1639b2194a8bfbb0daedf1cbdfc4ebef5f6b31 Mon Sep 17 00:00:00 2001 +From: Sergio Correia +Date: Mon, 18 May 2020 08:36:17 -0300 +Subject: [PATCH] Introduce -y (assume yes) argument to clevis luks bind + +In order to simplify automated operations with e.g. ansible, +it would be helpful to have a way to automate the creation of +bindings with clevis. + +In simple scenarios, it's possible to download the advertisement +from a tang server and pass it in the binding configuration, to +do the binding offline, in the following way: + +curl -sfg http://tang.server/adv -o adv.jws + +clevis luks bind -d /dev/sda2 tang '{"url":"http://tang.server", "adv":"adv.jws}' + +However, for more complex scenarios using multiple servers with +the sss pin, it becomes a lot more complicated to do the same +thing and do the binding in an automated fashion. An alternative +would be to use expect (tcl), but it can also be complicated. + +In this commit we introduce -y as a parameter to clevis luks bind, +meanining _assume yes_. Essentially, this would make it so that +the user would not have to manually trust tang key(s) by typing +y/yes. + +Security-wise, it would be similar to downloading the advertisement +manually and passing it to tang as the "adv" configuration option, +something already supported. + +We already have a -f parameter, so we picked something different, +not to change existing behavior and possibly break existing scripts. +--- + src/luks/clevis-luks-bind.1.adoc | 7 +- + src/luks/clevis-luks-bind.in | 11 +++- + src/luks/clevis-luks-regen | 4 +- + src/luks/tests/assume-yes-luks1 | 81 ++++++++++++++++++++++++ + src/luks/tests/assume-yes-luks2 | 81 ++++++++++++++++++++++++ + src/luks/tests/meson.build | 2 + + src/pins/sss/clevis-encrypt-sss.1.adoc | 14 +++- + src/pins/sss/clevis-encrypt-sss.c | 30 ++++++--- + src/pins/tang/clevis-encrypt-tang | 35 ++++++---- + src/pins/tang/clevis-encrypt-tang.1.adoc | 11 +++- + 10 files changed, 246 insertions(+), 30 deletions(-) + create mode 100755 src/luks/tests/assume-yes-luks1 + create mode 100755 src/luks/tests/assume-yes-luks2 + +diff --git a/src/luks/clevis-luks-bind.1.adoc b/src/luks/clevis-luks-bind.1.adoc +index 336c0f4..438e517 100644 +--- a/src/luks/clevis-luks-bind.1.adoc ++++ b/src/luks/clevis-luks-bind.1.adoc +@@ -9,7 +9,7 @@ clevis-luks-bind - Bind a LUKS device using the specified policy + + == SYNOPSIS + +-*clevis luks bind* [-f] -d DEV [-s SLT] [-k KEY] PIN CFG ++*clevis luks bind* [-f] [-y] -d DEV [-s SLT] [-k KEY] PIN CFG + + == OVERVIEW + +@@ -34,6 +34,11 @@ Clevis LUKS unlockers. See link:clevis-luks-unlockers.7.adoc[*clevis-luks-unlock + * *-f* : + Do not prompt for LUKSMeta initialization + ++* *-y* : ++ Automatically answer yes for all questions. When using _tang_, it ++ causes the advertisement trust check to be skipped, which can be ++ useful in automated deployments ++ + * *-d* _DEV_ : + The LUKS device on which to perform binding + +diff --git a/src/luks/clevis-luks-bind.in b/src/luks/clevis-luks-bind.in +index 89a5e22..8b8b5ee 100755 +--- a/src/luks/clevis-luks-bind.in ++++ b/src/luks/clevis-luks-bind.in +@@ -33,12 +33,14 @@ function luks2_supported() { + function usage() { + exec >&2 + echo +- echo "Usage: clevis luks bind [-f] [-s SLT] [-k KEY] -d DEV PIN CFG" ++ echo "Usage: clevis luks bind [-f] [-y] [-s SLT] [-k KEY] -d DEV PIN CFG" + echo + echo "$SUMMARY": + echo + echo " -f Do not prompt for LUKSMeta initialization" + echo ++ echo " -y Automatically answer yes for all questions" ++ echo + echo " -d DEV The LUKS device on which to perform binding" + echo + echo " -s SLT The LUKS slot to use" +@@ -55,12 +57,15 @@ if [ $# -eq 1 ] && [ "$1" == "--summary" ]; then + fi + + FRC=() +-while getopts ":hfd:s:k:" o; do ++YES=() ++while getopts ":fyd:s:k:" o; do + case "$o" in + f) FRC+=(-f);; + d) DEV="$OPTARG";; + s) SLT="$OPTARG";; + k) KEY="$OPTARG";; ++ y) FRC+=(-f) ++ YES+=(-y);; + *) usage;; + esac + done +@@ -139,7 +144,7 @@ cryptsetup luksDump "$DEV" \ + )")" + + # Encrypt the new key +-jwe="$(echo -n "$key" | clevis encrypt "$PIN" "$CFG")" ++jwe="$(echo -n "$key" | clevis encrypt "$PIN" "$CFG" "${YES}")" + + # If necessary, initialize the LUKS volume + if [ "$luks_type" == "luks1" ] && ! luksmeta test -d "$DEV"; then +diff --git a/src/luks/clevis-luks-regen b/src/luks/clevis-luks-regen +index 44fd673..6071d85 100755 +--- a/src/luks/clevis-luks-regen ++++ b/src/luks/clevis-luks-regen +@@ -110,7 +110,7 @@ if ! new_passphrase=$(generate_key "${DEV}"); then + fi + + # Reencrypt the new password. +-if ! jwe=$(clevis encrypt "${PIN}" "${CFG}" <<< "${new_passphrase}"); then ++if ! jwe="$(clevis encrypt "${PIN}" "${CFG}" <<< "${new_passphrase}")"; then + echo "Error using pin '${PIN}' with config '${CFG}'" >&2 + exit 1 + fi +@@ -176,7 +176,7 @@ fi + # Now make sure that we can unlock this device after the change. + # If we can't, undo the changes. + if ! cryptsetup open --test-passphrase --key-slot "${SLT}" "${DEV}" 2>/dev/null \ +- <<< $(clevis luks pass -d "${DEV}" -s "${SLT}" 2>/dev/null); then ++ <<< "$(clevis luks pass -d "${DEV}" -s "${SLT}" 2>/dev/null)"; then + echo "Invalid configuration detected after rebinding. Reverting changes." + restore_device "${DEV}" "${TMP}" + exit 1 +diff --git a/src/luks/tests/assume-yes-luks1 b/src/luks/tests/assume-yes-luks1 +new file mode 100755 +index 0000000..ad9dea4 +--- /dev/null ++++ b/src/luks/tests/assume-yes-luks1 +@@ -0,0 +1,81 @@ ++#!/bin/bash -ex ++# vim: set ts=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2020 Red Hat, Inc. ++# Author: Sergio Correia ++# ++# 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 . ++ ++TEST=$(basename "${0}") ++. tests-common-functions ++ ++. clevis-luks-common-functions ++ ++on_exit() { ++ local d ++ for d in "${TMP}" "${TMP2}"; do ++ [ ! -d "${d}" ] && continue ++ tang_stop "${d}" ++ rm -rf "${d}" ++ done ++} ++ ++trap 'on_exit' EXIT ++trap 'on_exit' ERR ++ ++TMP="$(mktemp -d)" ++ ++port=$(get_random_port) ++tang_run "${TMP}" "${port}" & ++tang_wait_until_ready "${port}" ++ ++url="http://${TANG_HOST}:${port}" ++ ++cfg=$(printf '{"url":"%s"}' "$url") ++ ++# LUKS1. ++DEV="${TMP}/luks1-device" ++new_device "luks1" "${DEV}" ++ ++if ! clevis luks bind -y -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: Bind should have succeeded." ++fi ++ ++if ! clevis_luks_unlock_device "${DEV}"; then ++ error "${TEST}: we were unable to unlock ${DEV}." ++fi ++ ++# Let's use a second tang server to test the sss pin. ++TMP2="$(mktemp -d)" ++ ++port2=$(get_random_port) ++tang_run "${TMP2}" "${port2}" & ++tang_wait_until_ready "${port2}" ++ ++url2="http://${TANG_HOST}:${port2}" ++ ++cfg2=$(printf '{"t":1,"pins":{"tang":[{"url":"%s"},{"url":"%s"}]}}' \ ++ "${url1}" "${url2}") ++ ++# LUKS1. ++new_device "luks1" "${DEV}" ++# Now let's test the sss pin with the two test tang servers we deployed. ++if ! clevis luks bind -y -d "${DEV}" sss "${cfg2}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: Bind should have succeeded." ++fi ++ ++# Unlock should still work now. ++if ! clevis_luks_unlock_device "${DEV}"; then ++ error "${TEST}: we should still be able to unlock ${DEV}" ++fi +diff --git a/src/luks/tests/assume-yes-luks2 b/src/luks/tests/assume-yes-luks2 +new file mode 100755 +index 0000000..5c0edc3 +--- /dev/null ++++ b/src/luks/tests/assume-yes-luks2 +@@ -0,0 +1,81 @@ ++#!/bin/bash -ex ++# vim: set ts=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2020 Red Hat, Inc. ++# Author: Sergio Correia ++# ++# 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 . ++ ++TEST=$(basename "${0}") ++. tests-common-functions ++ ++. clevis-luks-common-functions ++ ++on_exit() { ++ local d ++ for d in "${TMP}" "${TMP2}"; do ++ [ ! -d "${d}" ] && continue ++ tang_stop "${d}" ++ rm -rf "${d}" ++ done ++} ++ ++trap 'on_exit' EXIT ++trap 'on_exit' ERR ++ ++TMP="$(mktemp -d)" ++ ++port=$(get_random_port) ++tang_run "${TMP}" "${port}" & ++tang_wait_until_ready "${port}" ++ ++url="http://${TANG_HOST}:${port}" ++ ++cfg=$(printf '{"url":"%s"}' "$url") ++ ++# LUKS2. ++DEV="${TMP}/luks2-device" ++new_device "luks2" "${DEV}" ++ ++if ! clevis luks bind -y -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: Bind should have succeeded." ++fi ++ ++if ! clevis_luks_unlock_device "${DEV}"; then ++ error "${TEST}: we were unable to unlock ${DEV}." ++fi ++ ++# Let's use a second tang server to test the sss pin. ++TMP2="$(mktemp -d)" ++ ++port2=$(get_random_port) ++tang_run "${TMP2}" "${port2}" & ++tang_wait_until_ready "${port2}" ++ ++url2="http://${TANG_HOST}:${port2}" ++ ++cfg2=$(printf '{"t":1,"pins":{"tang":[{"url":"%s"},{"url":"%s"}]}}' \ ++ "${url1}" "${url2}") ++ ++# LUKS2. ++new_device "luks2" "${DEV}" ++# Now let's test the sss pin with the two test tang servers we deployed. ++if ! clevis luks bind -y -d "${DEV}" sss "${cfg2}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: Bind should have succeeded." ++fi ++ ++# Unlock should still work now. ++if ! clevis_luks_unlock_device "${DEV}"; then ++ error "${TEST}: we should still be able to unlock ${DEV}" ++fi +diff --git a/src/luks/tests/meson.build b/src/luks/tests/meson.build +index dbef9bf..4795488 100644 +--- a/src/luks/tests/meson.build ++++ b/src/luks/tests/meson.build +@@ -85,6 +85,7 @@ endif + + if has_tang + test('unlock-tang-luks1', find_program('unlock-tang-luks1'), env: env, timeout: 90) ++ test('assume-yes-luks1', find_program('assume-yes-luks1'), env: env) + endif + test('pass-tang-luks1', find_program('pass-tang-luks1'), env: env) + test('backup-restore-luks1', find_program('backup-restore-luks1'), env: env) +@@ -108,6 +109,7 @@ if luksmeta_data.get('OLD_CRYPTSETUP') == '0' + + if has_tang + test('unlock-tang-luks2', find_program('unlock-tang-luks2'), env: env, timeout: 120) ++ test('assume-yes-luks2', find_program('assume-yes-luks2'), env: env, timeout: 60) + endif + test('pass-tang-luks2', find_program('pass-tang-luks2'), env: env, timeout: 60) + test('backup-restore-luks2', find_program('backup-restore-luks2'), env:env, timeout: 90) +diff --git a/src/pins/sss/clevis-encrypt-sss.1.adoc b/src/pins/sss/clevis-encrypt-sss.1.adoc +index 7144e7e..7152144 100644 +--- a/src/pins/sss/clevis-encrypt-sss.1.adoc ++++ b/src/pins/sss/clevis-encrypt-sss.1.adoc +@@ -5,11 +5,11 @@ CLEVIS-ENCRYPT-SSS(1) + + == NAME + +-clevis-encrypt-sss - Encrypts using a Shamir's Secret Sharing policy ++clevis-encrypt-sss - Encrypts using a Shamir's Secret Sharing policy + + == SYNOPSIS + +-*clevis encrypt sss* CONFIG < PT > JWE ++*clevis encrypt sss* CONFIG [-y] < PT > JWE + + == OVERVIEW + +@@ -52,6 +52,16 @@ The format of the *pins* property is as follows: + When the list version of the format is used, multiple pins of that type will + receive key fragments. + ++== OPTIONS ++ ++* *-y* : ++ Automatically answer yes for all questions. For the _tang_ pin, it will ++ skip the advertisement trust check, which can be useful in automated ++ deployments: ++ ++ $ cfg='{"t":1,"pins":{"tang":[{"url":...},{"url":...}]}}' ++ $ clevis encrypt sss "$cfg" -y < PT > JWE ++ + == SEE ALSO + + link:clevis-encrypt-tang.1.adoc[*clevis-encrypt-tang*(1)], +diff --git a/src/pins/sss/clevis-encrypt-sss.c b/src/pins/sss/clevis-encrypt-sss.c +index d6f2c2c..531e918 100644 +--- a/src/pins/sss/clevis-encrypt-sss.c ++++ b/src/pins/sss/clevis-encrypt-sss.c +@@ -86,9 +86,9 @@ npins(json_t *pins) + } + + static json_t * +-encrypt_frag(json_t *sss, const char *pin, const json_t *cfg) ++encrypt_frag(json_t *sss, const char *pin, const json_t *cfg, int assume_yes) + { +- char *args[] = { "clevis", "encrypt", (char *) pin, NULL, NULL }; ++ char *args[] = { "clevis", "encrypt", (char *) pin, NULL, NULL, NULL }; + json_auto_t *jwe = json_string(""); + str_auto_t *str = NULL; + uint8_t *pnt = NULL; +@@ -100,6 +100,10 @@ encrypt_frag(json_t *sss, const char *pin, const json_t *cfg) + if (!str) + return NULL; + ++ if (assume_yes) { ++ args[4] = "-y"; ++ } ++ + pnt = sss_point(sss, &pntl); + if (!pnt) + return NULL; +@@ -137,7 +141,7 @@ encrypt_frag(json_t *sss, const char *pin, const json_t *cfg) + } + + static json_t * +-encrypt_frags(json_int_t t, json_t *pins) ++encrypt_frags(json_int_t t, json_t *pins, int assume_yes) + { + const char *pname = NULL; + json_auto_t *sss = NULL; +@@ -172,7 +176,7 @@ encrypt_frags(json_int_t t, json_t *pins) + json_array_foreach(pcfgs, i, pcfg) { + json_auto_t *jwe = NULL; + +- jwe = encrypt_frag(sss, pname, pcfg); ++ jwe = encrypt_frag(sss, pname, pcfg, assume_yes); + if (!jwe) + return NULL; + +@@ -201,14 +205,24 @@ main(int argc, char *argv[]) + const char *iv = NULL; + json_t *pins = NULL; + json_int_t t = 1; ++ int assume_yes = 0; + + if (argc == 2 && strcmp(argv[1], "--summary") == 0) { + fprintf(stdout, "%s\n", SUMMARY); + return EXIT_SUCCESS; + } + +- if (isatty(STDIN_FILENO) || argc != 2) +- goto usage; ++ if (isatty(STDIN_FILENO) || argc != 2) { ++ if (argc != 3) { ++ goto usage; ++ } ++ ++ if (strcmp(argv[2], "-y") == 0) { ++ assume_yes = 1; ++ } else if (strlen(argv[2]) > 0) { ++ goto usage; ++ } ++ } + + /* Parse configuration. */ + cfg = json_loads(argv[1], 0, NULL); +@@ -228,7 +242,7 @@ main(int argc, char *argv[]) + return EXIT_FAILURE; + } + +- sss = encrypt_frags(t, pins); ++ sss = encrypt_frags(t, pins, assume_yes); + if (!sss) + return EXIT_FAILURE; + +@@ -287,7 +301,7 @@ main(int argc, char *argv[]) + + usage: + fprintf(stderr, "\n"); +- fprintf(stderr, "Usage: clevis encrypt sss CONFIG < PLAINTEXT > JWE\n"); ++ fprintf(stderr, "Usage: clevis encrypt sss CONFIG [-y] < PLAINTEXT > JWE\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "%s\n", SUMMARY); + fprintf(stderr, "\n"); +diff --git a/src/pins/tang/clevis-encrypt-tang b/src/pins/tang/clevis-encrypt-tang +index 378b25d..4a43f1f 100755 +--- a/src/pins/tang/clevis-encrypt-tang ++++ b/src/pins/tang/clevis-encrypt-tang +@@ -28,10 +28,14 @@ fi + if [ -t 0 ]; then + exec >&2 + echo +- echo "Usage: clevis encrypt tang CONFIG < PLAINTEXT > JWE" ++ echo "Usage: clevis encrypt tang CONFIG [-y] < PLAINTEXT > JWE" + echo + echo "$SUMMARY" + echo ++ echo " -y Use this option for skipping the advertisement" ++ echo " trust check. This can be useful in automated" ++ echo " deployments" ++ echo + echo "This command uses the following configuration properties:" + echo + echo " url: The base URL of the Tang server (REQUIRED)" +@@ -60,6 +64,9 @@ if ! cfg="$(jose fmt -j- -Oo- <<< "$1" 2>/dev/null)"; then + exit 1 + fi + ++trust= ++[ -n "${2}" ] && [ "${2}" == "-y" ] && trust=yes ++ + if ! url="$(jose fmt -j- -Og url -u- <<< "$cfg")"; then + echo "Missing the required 'url' property!" >&2 + exit 1 +@@ -100,18 +107,20 @@ if ! jose jws ver -i "$jws" -k- -a <<< "$ver"; then + fi + + ### Check advertisement trust +-if [ -z "$thp" ]; then +- echo "The advertisement contains the following signing keys:" >&2 +- echo >&2 +- jose jwk thp -i- <<< "$ver" >&2 +- echo >&2 +- read -r -p "Do you wish to trust these keys? [ynYN] " ans < /dev/tty +- [[ "$ans" =~ ^[yY]$ ]] || exit 1 +- +-elif [ "$thp" != "any" ] && \ +- ! jose jwk thp -i- -f "$thp" -o /dev/null <<< "$ver"; then +- echo "Trusted JWK '$thp' did not sign the advertisement!" >&2 +- exit 1 ++if [ -z "${trust}" ]; then ++ if [ -z "$thp" ]; then ++ echo "The advertisement contains the following signing keys:" >&2 ++ echo >&2 ++ jose jwk thp -i- <<< "$ver" >&2 ++ echo >&2 ++ read -r -p "Do you wish to trust these keys? [ynYN] " ans < /dev/tty ++ [[ "$ans" =~ ^[yY]$ ]] || exit 1 ++ ++ elif [ "$thp" != "any" ] && \ ++ ! jose jwk thp -i- -f "$thp" -o /dev/null <<< "$ver"; then ++ echo "Trusted JWK '$thp' did not sign the advertisement!" >&2 ++ exit 1 ++ fi + fi + + ### Perform encryption +diff --git a/src/pins/tang/clevis-encrypt-tang.1.adoc b/src/pins/tang/clevis-encrypt-tang.1.adoc +index 276575f..c34d109 100644 +--- a/src/pins/tang/clevis-encrypt-tang.1.adoc ++++ b/src/pins/tang/clevis-encrypt-tang.1.adoc +@@ -9,7 +9,7 @@ clevis-encrypt-tang - Encrypts using a Tang binding server policy + + == SYNOPSIS + +-*clevis encrypt tang* CONFIG < PT > JWE ++*clevis encrypt tang* CONFIG [-y] < PT > JWE + + == OVERVIEW + +@@ -76,6 +76,15 @@ This command uses the following configuration properties: + * *adv* (object) : + A trusted advertisement (raw JSON) + ++== OPTIONS ++ ++* *-y* : ++ Automatically answer yes for all questions. Use this option for skipping ++ the advertisement trust check. This can be useful in automated deployments: ++ ++ $ clevis encrypt tang '{"url":...}' -y < PT > JWE ++ ++ + == SEE ALSO + + link:clevis-decrypt.1.adoc[*clevis-decrypt*(1)] +-- +2.18.4 + diff --git a/SOURCES/0010-Add-clevis-luks-edit-command.patch b/SOURCES/0010-Add-clevis-luks-edit-command.patch new file mode 100644 index 0000000..f41161f --- /dev/null +++ b/SOURCES/0010-Add-clevis-luks-edit-command.patch @@ -0,0 +1,1036 @@ +From 46dc5904a9a6b0f1dcdb2511cc3f09671ef54b2a Mon Sep 17 00:00:00 2001 +From: Sergio Correia +Date: Wed, 20 May 2020 21:09:15 -0300 +Subject: [PATCH] Add clevis luks edit command + +--- + src/luks/clevis-luks-common-functions | 65 ++++-- + src/luks/clevis-luks-edit | 242 +++++++++++++++++++++++ + src/luks/clevis-luks-edit.1.adoc | 69 +++++++ + src/luks/clevis-luks-regen | 125 ++---------- + src/luks/meson.build | 3 + + src/luks/tests/edit-tang-luks1 | 106 ++++++++++ + src/luks/tests/edit-tang-luks2 | 106 ++++++++++ + src/luks/tests/meson.build | 28 ++- + src/luks/tests/regen-inplace-luks1 | 2 +- + src/luks/tests/regen-inplace-luks2 | 2 +- + src/luks/tests/regen-not-inplace-luks1 | 2 +- + src/luks/tests/regen-not-inplace-luks2 | 2 +- + src/luks/tests/tests-common-functions.in | 26 --- + src/luks/tests/unlock-tang-luks1 | 7 +- + src/luks/tests/unlock-tang-luks2 | 8 +- + 15 files changed, 606 insertions(+), 187 deletions(-) + create mode 100755 src/luks/clevis-luks-edit + create mode 100644 src/luks/clevis-luks-edit.1.adoc + create mode 100755 src/luks/tests/edit-tang-luks1 + create mode 100755 src/luks/tests/edit-tang-luks2 + +diff --git a/src/luks/clevis-luks-common-functions b/src/luks/clevis-luks-common-functions +index c9d712a..f3a875c 100644 +--- a/src/luks/clevis-luks-common-functions ++++ b/src/luks/clevis-luks-common-functions +@@ -141,11 +141,7 @@ clevis_luks_decode_jwe() { + local jwe="${1}" + + local coded +- if ! coded=$(jose jwe fmt -i- <<< "${jwe}"); then +- return 1 +- fi +- +- coded=$(jose fmt -j- -g protected -u- <<< "${coded}" | tr -d '"') ++ read -r -d . coded <<< "${jwe}" + jose b64 dec -i- <<< "${coded}" + } + +@@ -291,6 +287,46 @@ clevis_luks_read_pins_from_slot() { + printf "%s: %s\n" "${SLOT}" "${cfg}" + } + ++# clevis_luks_is_key_valid() checks whether the given key is valid to unlock ++# the given device. ++clevis_luks_is_key_valid() { ++ local DEV="${1}" ++ local KEY="${2}" ++ ++ if ! cryptsetup open --test-passphrase "${DEV}" \ ++ --key-file <(echo -n "${KEY}") 2>/dev/null; then ++ return 1 ++ fi ++ return 0 ++} ++ ++# clevis_luks_unlock_device_by_slot() does the unlock of the device and slot ++# passed as parameters and returns the decoded passphrase. ++clevis_luks_unlock_device_by_slot() { ++ local DEV="${1}" ++ local SLT="${2}" ++ ++ [ -z "${DEV}" ] && return 1 ++ [ -z "${SLT}" ] && return 1 ++ ++ local jwe passphrase ++ if ! jwe="$(clevis_luks_read_slot "${DEV}" "${SLT}" 2>/dev/null)" \ ++ || [ -z "${jwe}" ]; then ++ return 1 ++ fi ++ ++ if ! passphrase="$(clevis decrypt < <(echo -n "${jwe}") 2>/dev/null)" \ ++ || [ -z "${passphrase}" ]; then ++ return 1 ++ fi ++ ++ if ! clevis_luks_is_key_valid "${DEV}" "${passphrase}"; then ++ return 1 ++ fi ++ echo -n "${passphrase}" ++ return 0 ++} ++ + # clevis_luks_unlock_device() does the unlock of the device passed as + # parameter and returns the decoded passphrase. + clevis_luks_unlock_device() { +@@ -303,26 +339,15 @@ clevis_luks_unlock_device() { + return 1 + fi + +- local slt jwe passphrase ++ local slt pt + for slt in ${used_slots}; do +- if ! jwe="$(clevis_luks_read_slot "${DEV}" "${slt}" 2>/dev/null)" \ +- || [ -z "${jwe}" ]; then +- continue +- fi +- +- if ! passphrase="$(clevis decrypt < <(echo -n "${jwe}"))" \ +- || [ -z "${passphrase}" ]; then ++ if ! pt=$(clevis_luks_unlock_device_by_slot "${DEV}" "${slt}") \ ++ || [ -z "${pt}" ]; then + continue + fi +- +- if ! cryptsetup luksOpen --test-passphrase "${DEV}" \ +- --key-file <(echo -n "${passphrase}"); then +- continue +- fi +- echo -n "${passphrase}" ++ echo -n "${pt}" + return 0 + done +- + return 1 + } + +diff --git a/src/luks/clevis-luks-edit b/src/luks/clevis-luks-edit +new file mode 100755 +index 0000000..fc95f75 +--- /dev/null ++++ b/src/luks/clevis-luks-edit +@@ -0,0 +1,242 @@ ++#!/bin/bash -e ++# vim: set ts=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2020 Red Hat, Inc. ++# Author: Sergio Correia ++# ++# 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 . ++# ++ ++. clevis-luks-common-functions ++ ++SUMMARY="Edit a binding from a clevis-bound slot in a LUKS device" ++ ++usage() { ++ echo >&2 ++ echo "Usage: clevis luks edit [-f] -d DEV -s SLT [-c CONFIG]" >&2 ++ echo >&2 ++ echo "$SUMMARY": >&2 ++ echo >&2 ++ echo " -d DEV The LUKS device to edit clevis-bound pins" >&2 ++ echo >&2 ++ echo " -s SLOT The slot to use when editing the clevis binding" >&2 ++ echo >&2 ++ echo " -f Proceed with the edit operation even if the configuration is the same" >&2 ++ echo >&2 ++ echo " -c CONFIG The updated config to use" >&2 ++ echo >&2 ++ exit 1 ++} ++ ++on_exit() { ++ [ -d "$TMP" ] && rm -rf "${TMP}" ++} ++ ++validate_cfg() { ++ local json="${1}" ++ jose fmt -j- -O <<< "${json}" 2>/dev/null ++} ++ ++edit_cfg() { ++ local cfg_file="${1}" ++ local editor="${EDITOR:-vi}" ++ ++ "${editor}" "${cfg_file}" || true ++ if ! validate_cfg "$(<"${cfg_file}")"; then ++ local ans= ++ while true; do ++ read -r -p \ ++ "Malformed configuration. Would you like to edit again? [ynYN] " \ ++ ans < /dev/tty ++ ++ [[ "${ans}" =~ ^[nN]$ ]] && return 1 ++ [[ "${ans}" =~ ^[yY]$ ]] && break ++ done ++ edit_cfg "${cfg_file}" ++ fi ++ return 0 ++} ++ ++if [ "${#}" -eq 1 ] && [ "${1}" = "--summary" ]; then ++ echo "${SUMMARY}" ++ exit 0 ++fi ++ ++CFG= ++FRC= ++while getopts ":fd:s:c:" o; do ++ case "$o" in ++ d) DEV=${OPTARG};; ++ s) SLT=${OPTARG};; ++ c) CFG=${OPTARG};; ++ f) FRC=-f;; ++ *) usage;; ++ esac ++done ++ ++if [ -z "${DEV}" ]; then ++ echo "Did not specify a device!" >&2 ++ usage ++fi ++ ++if [ -z "${SLT}" ]; then ++ echo "Did not specify a slot!" >&2 ++ usage ++fi ++ ++if ! binding="$(clevis luks list -d "${DEV}" -s "${SLT}" 2>/dev/null)" \ ++ || [ -z "${binding}" ]; then ++ echo "Error retrieving current configuration from ${DEV}:${SLT}" >&2 ++ exit 1 ++fi ++ ++read -r _ pin cfg <<< "${binding}" ++# Remove single quotes. ++cfg=${cfg//\'} ++if ! pretty_cfg="$(jq . <<< "${cfg}")" || [ -z "${pretty_cfg}" ]; then ++ echo "Error reading the configuration from ${DEV}:${SLT}" >&2 ++ exit 1 ++fi ++ ++if ! TMP="$(mktemp -d)" || [ -z "${TMP}" ]; then ++ echo "Creating a temporary dir for editing binding failed" >&2 ++ exit 1 ++fi ++ ++trap 'on_exit' EXIT ++trap 'on_exit' ERR ++ ++if [ -z "${CFG}" ]; then ++ CFG_FILE="${TMP}/cfg" ++ echo "${pretty_cfg}" > "${CFG_FILE}" ++ if ! edit_cfg "${CFG_FILE}"; then ++ exit 1 ++ fi ++ ++ if ! new_cfg="$(jq . -S < "${CFG_FILE}")" || [ -z "${new_cfg}" ]; then ++ echo "Error reading the updated config for ${DEV}:${SLT}" >&2 ++ exit 1 ++ fi ++else ++ if ! validate_cfg "${CFG}"; then ++ echo "Invalid configuration given as parameter with -c" >&2 ++ exit 1 ++ fi ++ new_cfg="$(jq . -S <<< "${CFG}")" ++fi ++ ++if [ "${new_cfg}" = "$(jq -S . <<< "${pretty_cfg}")" ] && [ -z "${FRC}" ]; then ++ echo "No changes detected; exiting" >&2 ++ exit 1 ++fi ++ ++if ! jcfg="$(jose fmt -j- -Oo- <<< "${new_cfg}" 2>/dev/null)" \ ++ || [ -z "${jcfg}" ]; then ++ echo "Error preparing the configuration for the binding update" >&2 ++ exit 1 ++fi ++ ++if [ -z "${CFG}" ]; then ++ printf "Pin: %s\nNew config:\n%s\n" "${pin}" "${new_cfg}" ++ while true; do ++ read -r -p \ ++ "Would you like to proceed with the updated configuration? [ynYN] " \ ++ ans < /dev/tty ++ [[ "${ans}" =~ ^[nN]$ ]] && exit 0 ++ [[ "${ans}" =~ ^[yY]$ ]] && break ++ done ++fi ++ ++echo "Updating binding..." ++ ++# Create new key. ++if ! new_pass=$(generate_key "${DEV}"); then ++ echo "Error generating new key for device ${DEV}" >&2 ++ exit 1 ++fi ++ ++# Reencrypt the new password. ++if ! jwe="$(clevis encrypt "${pin}" "${jcfg}" -y <<< "${new_pass}")"; then ++ echo "Error using pin '${pin}' with config '${jcfg}'" >&2 ++ exit 1 ++fi ++ ++# Backup LUKS header. ++if ! clevis_luks_backup_dev "${DEV}" "${TMP}"; then ++ echo "Error while trying to back up LUKS header from ${DEV}" >&2 ++ exit 1 ++fi ++ ++# Get passphrase. ++if ! pt="$(clevis_luks_unlock_device "${DEV}")" \ ++ || [ -z "${pt}" ]; then ++ # Unable to retrieve a passphrase from the bindings, so let's query ++ # the user. ++ read -r -s -p "Enter existing LUKS password: " pt; echo ++ # Check if the key is valid. ++ if ! clevis_luks_is_key_valid "${DEV}" "${pt}"; then ++ echo "The key provided is not valid for ${DEV}" >&2 ++ exit 1 ++ fi ++fi ++ ++# Check if we can do the update in-place, i.e., if the key we got is the one ++# for the slot we are interested in. ++in_place= ++if cryptsetup open --test-passphrase --key-slot "${SLT}" "${DEV}" \ ++ <<< "${pt}"; then ++ in_place=true ++fi ++ ++# Update the key slot with the new key. If we have the key for this slot, ++# the change happens in-place. Otherwise, we kill the slot and re-add it. ++if [ -n "${in_place}" ]; then ++ if ! cryptsetup luksChangeKey "${DEV}" --key-slot "${SLT}" \ ++ <(echo -n "${new_pass}") <<< "${pt}"; then ++ echo "Error updating LUKS passphrase in ${DEV}:${SLT}" >&2 ++ clevis_luks_restore_dev "${DEV}" "${TMP}" ++ exit 1 ++ fi ++else ++ if ! cryptsetup luksKillSlot --batch-mode "${DEV}" "${SLT}"; then ++ echo "Error wiping slot ${SLT} from ${DEV}" >&2 ++ clevis_luks_restore_dev "${DEV}" "${TMP}" ++ exit 1 ++ fi ++ ++ if ! echo -n "${new_pass}" \ ++ | cryptsetup luksAddKey --key-slot "${SLT}" \ ++ --key-file <(echo -n "${pt}") "${DEV}"; then ++ echo "Error updating LUKS passphrase in ${DEV}:${SLT}." >&2 ++ clevis_luks_restore_dev "${DEV}" "${TMP}" ++ exit 1 ++ fi ++fi ++ ++# Update the metadata. ++if ! clevis_luks_save_slot "${DEV}" "${SLT}" "${jwe}" "overwrite"; then ++ echo "Error updating metadata in ${DEV}:${SLT}" >&2 ++ clevis_luks_restore_dev "${DEV}" "${TMP}" ++ exit 1 ++fi ++ ++# Make sure we can unlock the device with this keyslot. ++if ! clevis_luks_unlock_device_by_slot "${DEV}" "${SLT}" >/dev/null \ ++ 2>/dev/null; then ++ echo "Invalid configuration detected. Reverting changes." >&2 ++ clevis_luks_restore_dev "${DEV}" "${TMP}" ++ exit 1 ++fi ++ ++echo "Binding edited successfully." +diff --git a/src/luks/clevis-luks-edit.1.adoc b/src/luks/clevis-luks-edit.1.adoc +new file mode 100644 +index 0000000..454de89 +--- /dev/null ++++ b/src/luks/clevis-luks-edit.1.adoc +@@ -0,0 +1,69 @@ ++CLEVIS-LUKS-EDIT(1) ++=================== ++:doctype: manpage ++ ++ ++== NAME ++ ++clevis-luks-edit - Edit a binding from a clevis-bound slot in a LUKS device ++ ++== SYNOPSIS ++ ++*clevis luks edit* -d DEV -s SLT [-c CONFIG] ++ ++== OVERVIEW ++ ++The *clevis luks edit* command edits clevis bindings from a LUKS device. ++For example: ++ ++ clevis luks edit -d /dev/sda1 -s 1 ++ ++== OPTIONS ++ ++* *-d* _DEV_ : ++ The LUKS device to edit clevis-bound pins ++ ++* *-s* _SLT_ : ++ The slot to use when editing the clevis binding ++ ++* *-f* : ++ Proceed with the edit operation even if the config is the same as the current one ++ ++* *-c* _CONFIG_ : ++ The updated config to use ++ ++ ++== EXAMPLES ++ ++ clevis luks list -d /dev/sda1 ++ 1: tang '{"url":"addr"}' ++ ++As we can see in the example above, */dev/sda1* has one slots bound, in this case, to a _tang_ pin. ++ ++We can edit this binding by issuing the following command: ++ ++ clevis luks edit -d /dev/sda1 -s 1 ++ ++This will open a text editor -- the one set in the $EDITOR environment variable, or _vi_, as a fallback -- with the current ++configuration of this binding to be edited. In this case, we should have the following: ++ ++ { ++ "url": "addr" ++ } ++ ++Once at the editor, we can edit the pin configuration. For _tang_, we could edit the _url_, for instance. After completing the change, ++save the file and exit. The updated configuration will be validated for JSON, and if there are no errors, you will be shown the ++updated configuration and prompted whether to proceed. ++ ++By proceeding, the binding will be updated. There may be required to provide a valid LUKS passphrase for the device. ++ ++In the second example, we will update the same device and slot, but we will be providing the updated configuration as well: ++ ++ clevis luks edit -d /dev/sda1 -s 1 -c '{"url":"new-addr-here"}' ++ ++In this case, the binding update will be done in non-interactive mode, however it also may be required to provide a valud LUKS ++passphrase for the device. ++ ++== SEE ALSO ++ ++link:clevis-luks-list.1.adoc[*clevis-luks-list*(1)], +diff --git a/src/luks/clevis-luks-regen b/src/luks/clevis-luks-regen +index 6071d85..8f32e08 100755 +--- a/src/luks/clevis-luks-regen ++++ b/src/luks/clevis-luks-regen +@@ -41,15 +41,7 @@ function usage_and_exit () { + exit "${1}" + } + +-on_exit() { +- if [ ! -d "${TMP}" ] || ! rm -rf "${TMP}"; then +- echo "Delete temporary files failed!" >&2 +- echo "You need to clean up: ${TMP}" >&2 +- exit 1 +- fi +-} +- +-while getopts ":hfd:s:" o; do ++while getopts ":hd:s:" o; do + case "$o" in + d) DEV="$OPTARG";; + h) usage_and_exit 0;; +@@ -68,117 +60,22 @@ if [ -z "$SLT" ]; then + exit 1 + fi + +-### ---------------------------------------------------------------------- +-if ! pin_cfg=$(clevis luks list -d "${DEV}" -s "${SLT}" 2>/dev/null); then +- echo "Error obtaining current configuration of device ${DEV}:${SLT}" >&2 ++if ! binding="$(clevis luks list -d "${DEV}" -s "${SLT}" 2>/dev/null)" \ ++ || [ -z "${binding}" ]; then ++ echo "Error retrieving current configuration from ${DEV}:${SLT}" >&2 + exit 1 + fi + +-PIN=$(echo "${pin_cfg}" | awk '{ print $2 }') +-CFG=$(echo "${pin_cfg}" | awk '{ print $3 }' | tr -d "'") ++read -r _ pin cfg <<< "${binding}" + + echo "Regenerating with:" +-echo "PIN: $PIN" +-echo "CONFIG: $CFG" +- +-trap 'echo "Ignoring CONTROL-C!"' INT TERM +- +-# Get the existing key. +-if ! existing_key=$(clevis luks pass -d "${DEV}" -s "${SLT}" 2>/dev/null); then +- # We failed to obtain the passphrase for the slot -- perhaps +- # it was rotated? --, so let's request user input. +- read -r -s -p "Enter existing LUKS password: " existing_key; echo +-fi +- +-# Check if the key is valid. +-if ! cryptsetup open --test-passphrase "${DEV}" <<< "${existing_key}"; then +- exit 1 +-fi +- +-# Check if we can do the update in-place, i.e., if the key we got is the one +-# for the slot we are interested in. +-in_place= +-if cryptsetup open --test-passphrase --key-slot "${SLT}" "${DEV}" \ +- <<< "${existing_key}"; then +- in_place=true +-fi +- +-# Create new key. +-if ! new_passphrase=$(generate_key "${DEV}"); then +- echo "Error generating new key for device ${DEV}" >&2 +- exit 1 +-fi +- +-# Reencrypt the new password. +-if ! jwe="$(clevis encrypt "${PIN}" "${CFG}" <<< "${new_passphrase}")"; then +- echo "Error using pin '${PIN}' with config '${CFG}'" >&2 +- exit 1 +-fi +- +-# Updating the metadata and the actual passphrase are destructive operations, +-# hence we will do a backup of the LUKS header and restore it later in case +-# we have issues performing these operations. +-if ! TMP="$(mktemp -d)"; then +- echo "Creating a temporary dir for device backup/restore failed!" >&2 +- exit 1 +-fi +-trap 'on_exit' EXIT +- +-# Backup LUKS header. +-if ! clevis_luks_backup_dev "${DEV}" "${TMP}"; then +- echo "Error while trying to back up LUKS header from ${DEV}" >&2 +- exit 1 +-fi +- +-restore_device() { +- local DEV="${1}" +- local TMP="${2}" +- +- if ! clevis_luks_restore_dev "${DEV}" "${TMP}"; then +- echo "Error while trying to restore LUKS header from ${DEV}." >&2 +- else +- echo "LUKS header restored successfully." >&2 +- fi +-} +- +-# Update the key slot with the new key. If we have the key for this slot, +-# the change happens in-place. Otherwise, we kill the slot and re-add it. +-if [ -n "${in_place}" ]; then +- if ! cryptsetup luksChangeKey "${DEV}" --key-slot "${SLT}" \ +- <(echo -n "${new_passphrase}") <<< "${existing_key}"; then +- echo "Error updating LUKS passphrase in ${DEV}:${SLT}" >&2 +- restore_device "${DEV}" "${TMP}" +- exit 1 +- fi +-else +- if ! cryptsetup luksKillSlot --batch-mode "${DEV}" "${SLT}"; then +- echo "Error wiping slot ${SLT} from ${DEV}" >&2 +- restore_device "${DEV}" "${TMP}" +- exit 1 +- fi +- +- if ! echo -n "${new_passphrase}" \ +- | cryptsetup luksAddKey --key-slot "${SLT}" \ +- --key-file <(echo -n "${existing_key}") "${DEV}"; then +- echo "Error updating LUKS passphrase in ${DEV}:${SLT}." >&2 +- restore_device "${DEV}" "${TMP}" +- exit 1 +- fi +-fi +- +-# Update the metadata. +-if ! clevis_luks_save_slot "${DEV}" "${SLT}" "${jwe}" "overwrite"; then +- echo "Error updating metadata in ${DEV}:${SLT}" >&2 +- restore_device "${DEV}" "${TMP}" +- exit 1 +-fi ++echo "PIN: ${pin}" ++echo "CONFIG: ${cfg}" + +-# Now make sure that we can unlock this device after the change. +-# If we can't, undo the changes. +-if ! cryptsetup open --test-passphrase --key-slot "${SLT}" "${DEV}" 2>/dev/null \ +- <<< "$(clevis luks pass -d "${DEV}" -s "${SLT}" 2>/dev/null)"; then +- echo "Invalid configuration detected after rebinding. Reverting changes." +- restore_device "${DEV}" "${TMP}" ++# Remove single quotes. ++cfg=${cfg//\'} ++if ! clevis luks edit -f -d "${DEV}" -s "${SLT}" -c "${cfg}" >/dev/null; then ++ echo "Error rotating keys in ${DEV}:${SLT}" >&2 + exit 1 + fi + +diff --git a/src/luks/meson.build b/src/luks/meson.build +index ee588c3..ba02bd9 100644 +--- a/src/luks/meson.build ++++ b/src/luks/meson.build +@@ -54,6 +54,9 @@ if libcryptsetup.found() and luksmeta.found() and pwmake.found() + bins += join_paths(meson.current_source_dir(), 'clevis-luks-report-sss') + bins += join_paths(meson.current_source_dir(), 'clevis-luks-report-tang') + mans += join_paths(meson.current_source_dir(), 'clevis-luks-report.1') ++ ++ bins += join_paths(meson.current_source_dir(), 'clevis-luks-edit') ++ mans += join_paths(meson.current_source_dir(), 'clevis-luks-edit.1') + else + warning('Will not install LUKS support due to missing dependencies!') + endif +diff --git a/src/luks/tests/edit-tang-luks1 b/src/luks/tests/edit-tang-luks1 +new file mode 100755 +index 0000000..3d42d68 +--- /dev/null ++++ b/src/luks/tests/edit-tang-luks1 +@@ -0,0 +1,106 @@ ++#!/bin/bash -ex ++# vim: set ts=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2020 Red Hat, Inc. ++# Author: Sergio Correia ++# ++# 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 . ++ ++TEST=$(basename "${0}") ++. tests-common-functions ++ ++. clevis-luks-common-functions ++ ++on_exit() { ++ local d ++ for d in "${TMP}" "${TMP2}"; do ++ [ ! -d "${d}" ] && continue ++ tang_stop "${d}" ++ rm -rf "${d}" ++ done ++} ++ ++trap 'on_exit' EXIT ++trap 'on_exit' ERR ++ ++TMP="$(mktemp -d)" ++ ++port=$(get_random_port) ++tang_run "${TMP}" "${port}" & ++tang_wait_until_ready "${port}" ++ ++url="http://${TANG_HOST}:${port}" ++ ++cfg=$(printf '{"url":"%s"}' "${url}") ++ ++# LUKS1. ++DEV="${TMP}/luks1-device" ++new_device "luks1" "${DEV}" ++ ++if ! clevis luks bind -y -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: Bind should have succeeded." ++fi ++ ++# Now let's try to change the config but using the same one we already have. ++if clevis luks edit -d "${DEV}" -s 1 -c "${cfg}"; then ++ error "${TEST}: edit should not have succeeded because the config is the same." ++fi ++ ++# And now, just a broken config. ++new_cfg=$(printf '{"url&:"%s"}' "${url}") ++if clevis luks edit -d "${DEV}" -s 1 -c "${new_cfg}"; then ++ error "${TEST}: edit should have failed because of invalid JSON" ++fi ++ ++# Now let's have another tang instance running and change the config to use ++# the new one. ++port2=$(get_random_port) ++TMP2="$(mktemp -d)" ++tang_run "${TMP2}" "${port2}" & ++tang_wait_until_ready "${port2}" ++new_url="http://${TANG_HOST}:${port2}" ++new_cfg=$(printf '{"url":"%s"}' "${new_url}") ++ ++if ! clevis luks edit -d "${DEV}" -s 1 -c "${new_cfg}"; then ++ error "${TEST}: edit should have succeeded." ++fi ++ ++# And now let's use sss and start with a single tang server, then add a second ++# one. ++new_device "luks1" "${DEV}" ++cfg=$(printf '{"t":1,"pins":{"tang":[{"url":"%s"}]}}' "${url}") ++if ! clevis luks bind -y -d "${DEV}" sss "${cfg}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: Bind should have succeeded." ++fi ++new_cfg=$(printf '{"t":1,"pins":{"tang":[{"url":"%s"},{"url":"%s"}]}}' \ ++ "${url}" "${new_url}") ++ ++if ! clevis luks edit -d "${DEV}" -s 1 -c "${new_cfg}"; then ++ error "${TEST}: edit should have succeeded and added a new tang server" ++fi ++ ++# Now let's change the threshold to 2. ++new_cfg=$(printf '{"t":2,"pins":{"tang":[{"url":"%s"},{"url":"%s"}]}}' \ ++ "${url}" "${new_url}") ++ ++if ! clevis luks edit -d "${DEV}" -s 1 -c "${new_cfg}"; then ++ error "${TEST}: edit should have succeeded and added a new tang server" ++fi ++ ++# And finally, let's try a broken config, with a wrong threshold. ++new_cfg=$(printf '{"t":3,"pins":{"tang":[{"url":"%s"},{"url":"%s"}]}}' \ ++ "${url}" "${new_url}") ++if clevis luks edit -d "${DEV}" -s 1 -c "${new_cfg}"; then ++ error "${TEST}: edit should have failed because threshold > number of servers" ++fi +diff --git a/src/luks/tests/edit-tang-luks2 b/src/luks/tests/edit-tang-luks2 +new file mode 100755 +index 0000000..9000053 +--- /dev/null ++++ b/src/luks/tests/edit-tang-luks2 +@@ -0,0 +1,106 @@ ++#!/bin/bash -ex ++# vim: set ts=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: ++# ++# Copyright (c) 2020 Red Hat, Inc. ++# Author: Sergio Correia ++# ++# 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 . ++ ++TEST=$(basename "${0}") ++. tests-common-functions ++ ++. clevis-luks-common-functions ++ ++on_exit() { ++ local d ++ for d in "${TMP}" "${TMP2}"; do ++ [ ! -d "${d}" ] && continue ++ tang_stop "${d}" ++ rm -rf "${d}" ++ done ++} ++ ++trap 'on_exit' EXIT ++trap 'on_exit' ERR ++ ++TMP="$(mktemp -d)" ++ ++port=$(get_random_port) ++tang_run "${TMP}" "${port}" & ++tang_wait_until_ready "${port}" ++ ++url="http://${TANG_HOST}:${port}" ++ ++cfg=$(printf '{"url":"%s"}' "${url}") ++ ++# LUKS2. ++DEV="${TMP}/luks2-device" ++new_device "luks2" "${DEV}" ++ ++if ! clevis luks bind -y -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: Bind should have succeeded." ++fi ++ ++# Now let's try to change the config but using the same one we already have. ++if clevis luks edit -d "${DEV}" -s 1 -c "${cfg}"; then ++ error "${TEST}: edit should not have succeeded because the config is the same." ++fi ++ ++# And now, just a broken config. ++new_cfg=$(printf '{"url&:"%s"}' "${url}") ++if clevis luks edit -d "${DEV}" -s 1 -c "${new_cfg}"; then ++ error "${TEST}: edit should have failed because of invalid JSON" ++fi ++ ++# Now let's have another tang instance running and change the config to use ++# the new one. ++port2=$(get_random_port) ++TMP2="$(mktemp -d)" ++tang_run "${TMP2}" "${port2}" & ++tang_wait_until_ready "${port2}" ++new_url="http://${TANG_HOST}:${port2}" ++new_cfg=$(printf '{"url":"%s"}' "${new_url}") ++ ++if ! clevis luks edit -d "${DEV}" -s 1 -c "${new_cfg}"; then ++ error "${TEST}: edit should have succeeded." ++fi ++ ++# And now let's use sss and start with a single tang server, then add a second ++# one. ++new_device "luks2" "${DEV}" ++cfg=$(printf '{"t":1,"pins":{"tang":[{"url":"%s"}]}}' "${url}") ++if ! clevis luks bind -y -d "${DEV}" sss "${cfg}" <<< "${DEFAULT_PASS}"; then ++ error "${TEST}: Bind should have succeeded." ++fi ++new_cfg=$(printf '{"t":1,"pins":{"tang":[{"url":"%s"},{"url":"%s"}]}}' \ ++ "${url}" "${new_url}") ++ ++if ! clevis luks edit -d "${DEV}" -s 1 -c "${new_cfg}"; then ++ error "${TEST}: edit should have succeeded and added a new tang server" ++fi ++ ++# Now let's change the threshold to 2. ++new_cfg=$(printf '{"t":2,"pins":{"tang":[{"url":"%s"},{"url":"%s"}]}}' \ ++ "${url}" "${new_url}") ++ ++if ! clevis luks edit -d "${DEV}" -s 1 -c "${new_cfg}"; then ++ error "${TEST}: edit should have succeeded and added a new tang server" ++fi ++ ++# And finally, let's try a broken config, with a wrong threshold. ++new_cfg=$(printf '{"t":3,"pins":{"tang":[{"url":"%s"},{"url":"%s"}]}}' \ ++ "${url}" "${new_url}") ++if clevis luks edit -d "${DEV}" -s 1 -c "${new_cfg}"; then ++ error "${TEST}: edit should have failed because threshold > number of servers" ++fi +diff --git a/src/luks/tests/meson.build b/src/luks/tests/meson.build +index 4795488..67fa597 100644 +--- a/src/luks/tests/meson.build ++++ b/src/luks/tests/meson.build +@@ -4,8 +4,9 @@ actv = find_program( + required: false + ) + +-# We use jq for comparing the pin config in the clevis luks list tests. +-jq = find_program('jq', required: false) ++# We use jq for comparing the pin config in the clevis luks list tests ++# and also in clevis luks edit. ++jq = find_program('jq', required: true) + + # we use systemd-socket-activate for running test tang servers. + actv = find_program( +@@ -74,19 +75,16 @@ test('bind-pass-with-newline', find_program('bind-pass-with-newline-luks1'), env + test('bind-pass-with-newline-keyfile', find_program('bind-pass-with-newline-keyfile-luks1'), env: env) + # Bug #70. + test('bind-already-used-luksmeta-slot', find_program('bind-already-used-luksmeta-slot'), env: env, timeout: 60) +- +-if jq.found() +- test('list-recursive-luks1', find_program('list-recursive-luks1'), env: env) +- test('list-tang-luks1', find_program('list-tang-luks1'), env: env) +- test('list-sss-tang-luks1', find_program('list-sss-tang-luks1'), env: env) +-else +- warning('Will not run "clevis luks list" tests due to missing jq dependency') +-endif ++test('list-recursive-luks1', find_program('list-recursive-luks1'), env: env) ++test('list-tang-luks1', find_program('list-tang-luks1'), env: env) ++test('list-sss-tang-luks1', find_program('list-sss-tang-luks1'), env: env) + + if has_tang + test('unlock-tang-luks1', find_program('unlock-tang-luks1'), env: env, timeout: 90) + test('assume-yes-luks1', find_program('assume-yes-luks1'), env: env) ++ test('edit-tang-luks1', find_program('edit-tang-luks1'), env: env, timeout: 90) + endif ++ + test('pass-tang-luks1', find_program('pass-tang-luks1'), env: env) + test('backup-restore-luks1', find_program('backup-restore-luks1'), env: env) + test('regen-inplace-luks1', find_program('regen-inplace-luks1'), env: env, timeout: 90) +@@ -100,16 +98,14 @@ if luksmeta_data.get('OLD_CRYPTSETUP') == '0' + test('bind-luks2', find_program('bind-luks2'), env: env, timeout: 60) + test('unbind-unbound-slot-luks2', find_program('unbind-unbound-slot-luks2'), env: env) + test('unbind-luks2', find_program('unbind-luks2'), env: env, timeout: 60) +- +- if jq.found() +- test('list-recursive-luks2', find_program('list-recursive-luks2'), env: env, timeout: 60) +- test('list-tang-luks2', find_program('list-tang-luks2'), env: env, timeout: 60) +- test('list-sss-tang-luks2', find_program('list-sss-tang-luks2'), env: env, timeout: 60) +- endif ++ test('list-recursive-luks2', find_program('list-recursive-luks2'), env: env, timeout: 60) ++ test('list-tang-luks2', find_program('list-tang-luks2'), env: env, timeout: 60) ++ test('list-sss-tang-luks2', find_program('list-sss-tang-luks2'), env: env, timeout: 60) + + if has_tang + test('unlock-tang-luks2', find_program('unlock-tang-luks2'), env: env, timeout: 120) + test('assume-yes-luks2', find_program('assume-yes-luks2'), env: env, timeout: 60) ++ test('edit-tang-luks2', find_program('edit-tang-luks2'), env: env, timeout: 120) + endif + test('pass-tang-luks2', find_program('pass-tang-luks2'), env: env, timeout: 60) + test('backup-restore-luks2', find_program('backup-restore-luks2'), env:env, timeout: 90) +diff --git a/src/luks/tests/regen-inplace-luks1 b/src/luks/tests/regen-inplace-luks1 +index 3a42ced..32072c4 100755 +--- a/src/luks/tests/regen-inplace-luks1 ++++ b/src/luks/tests/regen-inplace-luks1 +@@ -76,7 +76,7 @@ fi + old_key=$(clevis luks pass -d "${DEV}" -s "${SLT}") + + # Now let's try regen. +-if ! clevis_regen "${DEV}" "${SLT}" "${DEFAULT_PASS}"; then ++if ! clevis luks regen -d "${DEV}" -s "${SLT}"; then + error "${TEST}: clevis luks regen failed" + fi + +diff --git a/src/luks/tests/regen-inplace-luks2 b/src/luks/tests/regen-inplace-luks2 +index 1cb7a29..d1bc182 100755 +--- a/src/luks/tests/regen-inplace-luks2 ++++ b/src/luks/tests/regen-inplace-luks2 +@@ -77,7 +77,7 @@ fi + old_key=$(clevis luks pass -d "${DEV}" -s "${SLT}") + + # Now let's try regen. +-if ! clevis_regen "${DEV}" "${SLT}" "${DEFAULT_PASS}"; then ++if ! clevis luks regen -d "${DEV}" -s "${SLT}"; then + error "${TEST}: clevis luks regen failed" + fi + +diff --git a/src/luks/tests/regen-not-inplace-luks1 b/src/luks/tests/regen-not-inplace-luks1 +index 1b65ca7..fba61dc 100755 +--- a/src/luks/tests/regen-not-inplace-luks1 ++++ b/src/luks/tests/regen-not-inplace-luks1 +@@ -79,7 +79,7 @@ if [ "${enabled}" -ne 2 ]; then + fi + + # Now let's try regen. +-if ! clevis_regen "${DEV}" "${SLT}" "${DEFAULT_PASS}"; then ++if ! clevis luks regen -d "${DEV}" -s "${SLT}" <<< "${DEFAULT_PASS}"; then + error "${TEST}: clevis luks regen failed" + fi + +diff --git a/src/luks/tests/regen-not-inplace-luks2 b/src/luks/tests/regen-not-inplace-luks2 +index dc91449..6e3b012 100755 +--- a/src/luks/tests/regen-not-inplace-luks2 ++++ b/src/luks/tests/regen-not-inplace-luks2 +@@ -80,7 +80,7 @@ if [ "${enabled}" -ne 2 ]; then + fi + + # Now let's try regen. +-if ! clevis_regen "${DEV}" "${SLT}" "${DEFAULT_PASS}"; then ++if ! clevis luks regen -d "${DEV}" -s "${SLT}" <<< "${DEFAULT_PASS}"; then + error "${TEST}: clevis luks regen failed" + fi + +diff --git a/src/luks/tests/tests-common-functions.in b/src/luks/tests/tests-common-functions.in +index 6101f28..7b3fdad 100755 +--- a/src/luks/tests/tests-common-functions.in ++++ b/src/luks/tests/tests-common-functions.in +@@ -229,31 +229,5 @@ tang_get_adv() { + curl -o "${adv}" http://"${TANG_HOST}":"${port}"/adv + } + +-# Regenerate binding. +-clevis_regen() { +- local DEV="${1}" +- local SLT="${2}" +- local PASS="${3}" +- +- expect -d << CLEVIS_REGEN +- set timeout 120 +- spawn sh -c "clevis luks regen -d $DEV -s $SLT" +- expect { +- "Enter existing LUKS password" { +- send "$PASS\r" +- exp_continue +- } +- "Do you wish to trust these keys" { +- send "y\r" +- exp_continue +- } +- expect eof +- wait +- } +-CLEVIS_REGEN +- ret=$? +- return "${ret}" +-} +- + export TANG_HOST=127.0.0.1 + export DEFAULT_PASS='just-some-test-password-here' +diff --git a/src/luks/tests/unlock-tang-luks1 b/src/luks/tests/unlock-tang-luks1 +index 841ba01..6ede47b 100755 +--- a/src/luks/tests/unlock-tang-luks1 ++++ b/src/luks/tests/unlock-tang-luks1 +@@ -31,6 +31,10 @@ on_exit() { + trap 'on_exit' EXIT + trap 'on_exit' ERR + ++# LUKS1. ++DEV="${TMP}/luks1-device" ++new_device "luks1" "${DEV}" ++ + TMP="$(mktemp -d)" + + port=$(get_random_port) +@@ -43,9 +47,6 @@ tang_get_adv "${port}" "${adv}" + + cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv") + +-# LUKS1. +-DEV="${TMP}/luks1-device" +-new_device "luks1" "${DEV}" + + if ! clevis luks bind -f -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then + error "${TEST}: Bind should have succeeded." +diff --git a/src/luks/tests/unlock-tang-luks2 b/src/luks/tests/unlock-tang-luks2 +index 81822fb..b32c5aa 100755 +--- a/src/luks/tests/unlock-tang-luks2 ++++ b/src/luks/tests/unlock-tang-luks2 +@@ -31,6 +31,10 @@ on_exit() { + trap 'on_exit' EXIT + trap 'on_exit' ERR + ++# LUKS2. ++DEV="${TMP}/luks2-device" ++new_device "luks2" "${DEV}" ++ + TMP="$(mktemp -d)" + + port=$(get_random_port) +@@ -43,10 +47,6 @@ tang_get_adv "${port}" "${adv}" + + cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv") + +-# LUKS2. +-DEV="${TMP}/luks2-device" +-new_device "luks2" "${DEV}" +- + if ! clevis luks bind -f -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then + error "${TEST}: Bind should have succeeded." + fi +-- +2.18.4 + diff --git a/SOURCES/Add-clevis-luks-report-regen.patch b/SOURCES/Add-clevis-luks-report-regen.patch deleted file mode 100644 index 274e651..0000000 --- a/SOURCES/Add-clevis-luks-report-regen.patch +++ /dev/null @@ -1,858 +0,0 @@ -From 70d3da5ce8d68e8ff258122592670eb70da0c839 Mon Sep 17 00:00:00 2001 -From: Sergio Correia -Date: Wed, 16 Oct 2019 09:14:58 -0300 -Subject: [PATCH 2/2] Add clevis luks report/regen - ---- - src/luks/clevis-luks-common-functions | 143 ++++++++++++++++++++ - src/luks/clevis-luks-regen | 186 ++++++++++++++++++++++++++ - src/luks/clevis-luks-regen.1.adoc | 36 +++++ - src/luks/clevis-luks-report | 95 +++++++++++++ - src/luks/clevis-luks-report-compare | 71 ++++++++++ - src/luks/clevis-luks-report-decode | 59 ++++++++ - src/luks/clevis-luks-report-sss | 53 ++++++++ - src/luks/clevis-luks-report-tang | 67 ++++++++++ - src/luks/clevis-luks-report.1.adoc | 41 ++++++ - src/luks/meson.build | 12 ++ - 10 files changed, 763 insertions(+) - create mode 100644 src/luks/clevis-luks-common-functions - create mode 100755 src/luks/clevis-luks-regen - create mode 100644 src/luks/clevis-luks-regen.1.adoc - create mode 100755 src/luks/clevis-luks-report - create mode 100755 src/luks/clevis-luks-report-compare - create mode 100755 src/luks/clevis-luks-report-decode - create mode 100755 src/luks/clevis-luks-report-sss - create mode 100755 src/luks/clevis-luks-report-tang - create mode 100644 src/luks/clevis-luks-report.1.adoc - -diff --git a/src/luks/clevis-luks-common-functions b/src/luks/clevis-luks-common-functions -new file mode 100644 -index 0000000..d676253 ---- /dev/null -+++ b/src/luks/clevis-luks-common-functions -@@ -0,0 +1,143 @@ -+#!/bin/bash -e -+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: -+# -+# Copyright (c) 2019 Red Hat, Inc. -+# Author: Sergio Correia -+# -+# 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 . -+# -+ -+# valid_slot() will check whether a given slot is possibly valid, i.e., if it -+# is a numeric value within the specified range. -+valid_slot() { -+ local SLT="${1}" -+ local MAX_SLOTS="${2}" -+ case "${SLT}" in -+ ''|*[!0-9]*) -+ return 1 -+ ;; -+ *) -+ # We got an integer, now let's make sure it is within the -+ # supported range. -+ if [ "${SLT}" -ge "${MAX_SLOTS}" ]; then -+ return 1 -+ fi -+ ;; -+ esac -+} -+ -+# clevis_luks_read_slot() will read a particular slot of a given device, which -+# should be either LUKS1 or LUKS2. Returns 1 in case of failure; 0 in case of -+# success. -+clevis_luks_read_slot() { -+ local DEV="${1}" -+ local SLT="${2}" -+ -+ if [ -z "${DEV}" ] || [ -z "${SLT}" ]; then -+ echo "Need both a device and a slot as arguments." >&2 -+ return 1 -+ fi -+ -+ local DATA_CODED='' -+ local MAX_LUKS1_SLOTS=8 -+ local MAX_LUKS2_SLOTS=32 -+ if cryptsetup isLuks --type luks1 "${DEV}"; then -+ if ! valid_slot "${SLT}" "${MAX_LUKS1_SLOTS}"; then -+ echo "Please, provide a valid key slot number; 0-7 for LUKS1" >&2 -+ return 1 -+ fi -+ -+ if ! luksmeta test -d "${DEV}"; then -+ echo "The ${DEV} device is not valid!" >&2 -+ return 1 -+ fi -+ -+ local uuid -+ # Pattern from luksmeta: active slot uuid. -+ read -r _ _ uuid <<< "$(luksmeta show -d "${DEV}" | grep "^${SLT} *")" -+ -+ if [ "${uuid}" = "empty" ]; then -+ echo "The LUKSMeta slot ${SLT} on device ${DEV} is already empty." >&2 -+ return 1 -+ fi -+ -+ if ! DATA_CODED="$(luksmeta load -d "${DEV}" -s "${SLT}")"; then -+ echo "Cannot load data from ${DEV} slot:${SLT}!" >&2 -+ return 1 -+ fi -+ elif cryptsetup isLuks --type luks2 "${DEV}"; then -+ if ! valid_slot "${SLT}" "${MAX_LUKS2_SLOTS}"; then -+ echo "Please, provide a valid key slot number; 0-31 for LUKS2" >&2 -+ return 1 -+ fi -+ -+ local token_id -+ token_id=$(cryptsetup luksDump "${DEV}" \ -+ | grep -E -B1 "^\s+Keyslot:\s+${SLT}$" \ -+ | head -n 1 | sed -rn 's|^\s+([0-9]+): clevis|\1|p') -+ if [ -z "${token_id}" ]; then -+ echo "Cannot load data from ${DEV} slot:${SLT}. No token found!" >&2 -+ return 1 -+ fi -+ -+ local token -+ token=$(cryptsetup token export --token-id "${token_id}" "${DEV}") -+ DATA_CODED=$(jose fmt -j- -Og jwe -o- <<< "${token}" \ -+ | jose jwe fmt -i- -c) -+ -+ if [ -z "${DATA_CODED}" ]; then -+ echo "Cannot load data from ${DEV} slot:${SLT}!" >&2 -+ return 1 -+ fi -+ else -+ echo "${DEV} is not a supported LUKS device!" >&2 -+ return 1 -+ fi -+ echo "${DATA_CODED}" -+} -+ -+# Generate a key with the same entropy as the LUKS Master key of a given -+# device. -+generate_key() { -+ local DEV="${1}" -+ -+ if [ -z "${DEV}" ]; then -+ echo "Please, specify a device." >&2 -+ return 1 -+ fi -+ -+ local dump -+ local filter -+ dump=$(cryptsetup luksDump "${DEV}") -+ if cryptsetup isLuks --type luks1 "${DEV}"; then -+ filter=$(sed -rn 's|MK bits:[ \t]*([0-9]+)|\1|p' <<< "${dump}") -+ elif cryptsetup isLuks --type luks2 "${DEV}"; then -+ filter=$(sed -rn 's|^\s+Key:\s+([0-9]+) bits\s*$|\1|p' <<< "${dump}") -+ else -+ echo "${DEV} is not a supported LUKS device!" >&2 -+ return 1 -+ fi -+ local bits -+ bits=$(sort -n <<< "${filter}" | tail -n 1) -+ pwmake "${bits}" -+} -+ -+findexe() { -+ while read -r -d: path; do -+ [ -f "${path}/${1}" ] && [ -x "${path}/${1}" ] && \ -+ echo "${path}/${1}" && return 0 -+ done <<< "${PATH}:" -+ return 1 -+} -+ -diff --git a/src/luks/clevis-luks-regen b/src/luks/clevis-luks-regen -new file mode 100755 -index 0000000..9535ba3 ---- /dev/null -+++ b/src/luks/clevis-luks-regen -@@ -0,0 +1,186 @@ -+#!/usr/bin/env bash -+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: -+# -+# Copyright (c) 2018 Red Hat, Inc. -+# Author: Radovan Sroka -+# -+# 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 . -+# -+ -+. clevis-luks-common-functions -+ -+SUMMARY="Regenerate LUKS metadata" -+ -+if [ "$1" == "--summary" ]; then -+ echo "$SUMMARY" -+ exit 0 -+fi -+ -+function usage_and_exit () { -+ echo >&2 -+ echo "Usage: clevis luks regen -d DEV -s SLOT" >&2 -+ echo >&2 -+ echo "$SUMMARY" >&2 -+ echo >&2 -+ exit "$1" -+} -+ -+if [ "$#" -ne "4" ]; then -+ usage_and_exit 1 -+fi -+ -+while getopts "hd:s:" o; do -+ case "$o" in -+ d) DEV="$OPTARG";; -+ h) usage_and_exit 0;; -+ s) SLT="$OPTARG";; -+ *) usage_and_exit 1;; -+ esac -+done -+ -+function decode_luks_header () { -+ if DATA_CODED="$(jose jwe fmt -i- <<< "$1")"; then -+ DATA_CODED="$(jose fmt -j- -g protected -u- <<< "$DATA_CODED")" -+ DATA_DECODED="$(jose b64 dec -i- <<< "$DATA_CODED")" -+ else -+ echo "Error decoding JWE protected header!" >&2 -+ exit 1 -+ fi -+ -+ echo "$DATA_DECODED" -+} -+ -+function generate_cfg () { -+ echo -n "{" -+ DATA="$(decode_luks_header "$1")" -+ -+ if ! P="$(jose fmt -j- -g clevis -g pin -u- <<< "$DATA")" || [ -z "$P" ]; then -+ echo "Pin wasn't found in LUKS metadata!" >&2 -+ exit 1 -+ fi -+ -+ if ! CONTENT="$(jose fmt -j- -g clevis -g "$P" -o- <<< "$DATA")" || [ -z "$CONTENT" ]; then -+ echo "Content was not found!" >&2 -+ fi -+ -+ # echo -n "\"$P\": [" -+ -+ if [ "$P" = "tang" ] || [ "$P" = "http" ]; then -+ URL="$(jose fmt -j- -g url -u- <<< "$CONTENT")" -+ echo -n "\"url\":\"$URL\"" -+ elif [ "$P" = "sss" ]; then -+ THRESHOLD="$(jose fmt -j- -g t -o- <<< "$CONTENT")" -+ if [ -n "$THRESHOLD" ]; then -+ echo -n "\"t\":$THRESHOLD," -+ fi -+ -+ echo -n "\"pins\":{" -+ -+ CNT=0 -+ PREV="" -+ while ITEM="$(jose fmt -j- -g jwe -g"$CNT" -u- <<< "$CONTENT")"; do -+ if [ -z "$ITEM" ]; then -+ CNT=$(( CNT + 1 )) -+ continue # in some cases it can be empty string -+ fi -+ -+ DD="$(decode_luks_header "$ITEM")" -+ -+ if ! PP="$(jose fmt -j- -g clevis -g pin -u- <<< "$DD")" || [ -z "$PP" ]; then -+ echo "Pin wasn't found in LUKS metadata!" >&2 -+ exit 1 -+ fi -+ -+ if [ "$CNT" -eq 0 ]; then -+ PREV="$PP" -+ echo -n "\"$PP\":[" -+ echo -n "$(generate_cfg "$ITEM")" -+ else -+ if ! [ "$PREV" = "$PP" ]; then -+ echo -n "],\"$PP\":[" -+ echo -n "$(generate_cfg "$ITEM")" -+ else -+ echo -n ",$(generate_cfg "$ITEM")" -+ fi -+ fi -+ -+ PREV="$PP" -+ CNT=$(( CNT + 1 )) -+ done -+ -+ echo -n "]}" -+ -+ else -+ echo "Unknown pin $P!" >&2 -+ exit 1 -+ fi -+ -+ echo -n "}" -+} -+ -+### get luks metadata -+ -+if [ -z "$DEV" ]; then -+ echo "Did not specify a device!" >&2 -+ exit 1 -+fi -+ -+if [ -z "$SLT" ]; then -+ echo "Did not specify a slot!" >&2 -+ exit 1 -+fi -+ -+if ! OLD_LUKS_CODED="$(clevis_luks_read_slot "$DEV" "$SLT")"; then -+ echo "Error reading metadata from LUKS device!" >&2 -+ exit 1 -+fi -+ -+### ---------------------------------------------------------------------- -+ -+DECODED="$(decode_luks_header "$OLD_LUKS_CODED")" -+ -+if ! PIN="$(jose fmt -j- -g clevis -g pin -u- <<< "$DECODED")" || [ -z "$PIN" ]; then -+ echo "Pin wasn't found in LUKS metadata!" >&2 -+ exit 1 -+fi -+ -+CFG="$(generate_cfg "$OLD_LUKS_CODED")" -+ -+### ---------------------------------------------------------------------- -+ -+echo "Regenerating with:" -+echo "PIN: $PIN" -+echo "CONFIG: $CFG" -+ -+trap 'echo "Ignoring CONTROL-C!"' INT TERM -+ -+# Get the existing key. -+read -r -s -p "Enter existing LUKS password: " existing_key; echo -+ -+# Check if the key is valid. -+if ! cryptsetup luksOpen --test-passphrase "${DEV}" <<< "${existing_key}"; then -+ exit 1 -+fi -+ -+if ! clevis luks unbind -d "${DEV}" -s "${SLT}" -f; then -+ echo "Error during unbind of rotated key from slot:$SLT in $DEV" >&2 -+ exit 1 -+fi -+ -+if ! clevis luks bind -d "${DEV}" -s "${SLT}" "${PIN}" "${CFG}" -k - <<< "${existing_key}"; then -+ echo "Error during bind of new key from slot:$SLT in $DEV" >&2 -+ exit 1 -+fi -+ -+echo "Keys were succesfully rotated." -diff --git a/src/luks/clevis-luks-regen.1.adoc b/src/luks/clevis-luks-regen.1.adoc -new file mode 100644 -index 0000000..3cd6b7c ---- /dev/null -+++ b/src/luks/clevis-luks-regen.1.adoc -@@ -0,0 +1,36 @@ -+CLEVIS-LUKS-REGEN(1) -+===================== -+:doctype: manpage -+ -+ -+== NAME -+ -+clevis-luks-regen - Regenerates LUKS metadata -+ -+== SYNOPSIS -+ -+*clevis luks regen* -d DEV -s SLT -+ -+== OVERVIEW -+ -+The *clevis luks regen* command regenerates the LUKS metadata for a given slot in a LUKS device. It effectively -+performs an operation equivalent to *clevis luks unbind* and *clevis luks bind* for rebinding said slot and device. -+ -+== OPTIONS -+ -+* *-d* _DEV_ : -+ The bound LUKS device -+ -+* *-s* _SLT_ : -+ The slot or key slot number for rebinding. Note that it requires that such slot is currently bound by clevis. -+ -+== EXAMPLE -+ -+ Regenerate the binding of slot 1 from /dev/sda1: -+ -+ # clevis luks regen -d /dev/sda1 -s 1 -+ -+== SEE ALSO -+ -+link:clevis-luks-bind.1.adoc[*clevis-luks-bind*(1)] -+link:clevis-luks-unbind.1.adoc[*clevis-luks-unbind*(1)] -diff --git a/src/luks/clevis-luks-report b/src/luks/clevis-luks-report -new file mode 100755 -index 0000000..f047256 ---- /dev/null -+++ b/src/luks/clevis-luks-report -@@ -0,0 +1,95 @@ -+#!/usr/bin/bash -e -+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: -+# -+# Copyright (c) 2018 Red Hat, Inc. -+# Author: Radovan Sroka -+# -+# 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 . -+# -+ -+. clevis-luks-common-functions -+ -+SUMMARY="Report any key rotation on the server side" -+ -+if [ "$1" == "--summary" ]; then -+ echo "$SUMMARY" -+ exit 0 -+fi -+ -+function usage_and_exit () { -+ echo >&2 -+ echo "Usage: clevis luks report [-qr] -d DEV -s SLOT" >&2 -+ echo >&2 -+ echo -e " -q\t Quiet mode" >&2 -+ echo -e " -r\t Regenerate luks metadata with \"clevis luks regen -d DEV -s SLOT\"" >&2 -+ echo >&2 -+ echo "$SUMMARY" >&2 -+ echo >&2 -+ exit "$1" -+} -+ -+while getopts "hd:s:rq" o; do -+ case "$o" in -+ d) DEV="$OPTARG";; -+ h) usage_and_exit 0;; -+ r) ROPT="regen";; -+ s) SLT="$OPTARG";; -+ q) QOPT="quiet";; -+ *) usage_and_exit 1;; -+ esac -+done -+ -+### get luks metadata -+ -+if [ -z "$DEV" ]; then -+ echo "Did not specify a device!" >&2 -+ exit 1 -+fi -+ -+if [ -z "$SLT" ]; then -+ echo "Did not specify a slot!" >&2 -+ exit 1 -+fi -+ -+if ! DATA_CODED=$(clevis_luks_read_slot "${DEV}" "${SLT}"); then -+ # Error message was already displayed by clevis_luks_read_slot(), -+ # at this point. -+ exit 1 -+fi -+ -+EXE="$(findexe clevis-luks-report-decode)" -+RESULT="$($EXE "${DATA_CODED}")" -+ -+if [ -n "$RESULT" ]; then -+ echo "$RESULT" -+ echo "Report detected that some keys were rotated." -+ if [ -z "$QOPT" ]; then -+ if [ -z "$ROPT" ]; then -+ read -r -p "Do you want to regenerate luks metadata with \"clevis luks regen -d $DEV -s $SLT\"? [ynYN] " ans < /dev/tty -+ [[ "$ans" =~ ^[yY]$ ]] && ROPT="regen" -+ fi -+ fi -+else -+ exit 0 -+fi -+ -+if [ "$ROPT" = "regen" ]; then -+ EXE="$(findexe clevis-luks-regen)" -+ exec "$EXE" -d "$DEV" -s "$SLT" -+else -+ if [ -n "${RESULT}" ]; then -+ # Keys were rotated. -+ exit 1 -+ fi -+fi -diff --git a/src/luks/clevis-luks-report-compare b/src/luks/clevis-luks-report-compare -new file mode 100755 -index 0000000..2ba5132 ---- /dev/null -+++ b/src/luks/clevis-luks-report-compare -@@ -0,0 +1,71 @@ -+#!/usr/bin/bash -e -+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: -+# -+# Copyright (c) 2018 Red Hat, Inc. -+# Author: Radovan Sroka -+# -+# 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="Compare two sets of keys" -+ -+if [ "$1" == "--summary" ]; then -+ echo "$SUMMARY" -+ exit 1 -+fi -+ -+if [ -z "$1" ]; then -+ echo "$0 missing the first argument!" -+ exit 1 -+fi -+ -+if [ -z "$2" ]; then -+ echo "$0 missing the second argument!" -+ exit 1 -+fi -+ -+ADV_KEYS="$1" # keys from advertisement -+LUKS_KEYS="$2" # keys from luks metadata -+ -+### iterate over adv keys and make thumbprints -+CNT=0 -+declare -a ADV_KEYS_ARRAY -+while res="$(jose fmt -j- -g keys -g"$CNT" -o- <<< "$ADV_KEYS")"; do -+ thp="$(echo "$res" | jose jwk thp -i-)" -+ ADV_KEYS_ARRAY["$CNT"]="$thp" -+ CNT=$(( CNT + 1 )) -+done -+ -+CNT=0 -+while key="$(jose fmt -j- -g keys -g"$CNT" -o- <<< "$LUKS_KEYS")"; do -+ thp="$(echo "$key" | jose jwk thp -i-)" -+ -+ FOUND=0 -+ for k in "${ADV_KEYS_ARRAY[@]}" -+ do -+ if [ "$k" = "$thp" ]; then -+ FOUND=1 -+ break -+ fi -+ done -+ -+ if [ "$FOUND" -eq "0" ]; then -+ echo "Key \"$thp\" is not in the advertisement and was probably rotated!" -+ echo "$key" -+ echo -+ fi -+ CNT=$(( CNT + 1 )) -+done -+ -+exit 0 -diff --git a/src/luks/clevis-luks-report-decode b/src/luks/clevis-luks-report-decode -new file mode 100755 -index 0000000..f39d1e9 ---- /dev/null -+++ b/src/luks/clevis-luks-report-decode -@@ -0,0 +1,59 @@ -+#!/usr/bin/bash -e -+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: -+# -+# Copyright (c) 2018 Red Hat, Inc. -+# Author: Radovan Sroka -+# -+# 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 . -+# -+ -+. clevis-luks-common-functions -+ -+SUMMARY="Decode luks header" -+ -+if [ "$1" == "--summary" ]; then -+ echo "$SUMMARY" -+ exit 1 -+fi -+ -+if [ -z "$1" ]; then -+ echo "$0 missing the first argument!" -+ exit 1 -+fi -+ -+DATA_CODED="$1" -+ -+if DATA_CODED="$(jose jwe fmt -i- <<< "$DATA_CODED")"; then -+ DATA_CODED="$(jose fmt -j- -g protected -u- <<< "$DATA_CODED")" -+ DATA_DECODED="$(jose b64 dec -i- <<< "$DATA_CODED")" -+else -+ echo "Error decoding JWE protected header!" >&2 -+ exit 1 -+fi -+ -+### get pin and url -+ -+if ! PIN="$(jose fmt -j- -g clevis -g pin -u- <<< "$DATA_DECODED")" || [ -z "$PIN" ]; then -+ echo "Pin wasn't found in luks metadata!" >&2 -+ exit 1 -+fi -+ -+if ! CONTENT="$(jose fmt -j- -g clevis -g "$PIN" -o- <<< "$DATA_DECODED")" || [ -z "$CONTENT" ]; then -+ echo "Content wasn't found!" >&2 -+ exit 1 -+fi -+ -+EXE="$(findexe clevis-luks-report-"$PIN")" -+ -+exec "$EXE" "$CONTENT" -diff --git a/src/luks/clevis-luks-report-sss b/src/luks/clevis-luks-report-sss -new file mode 100755 -index 0000000..1dba4c1 ---- /dev/null -+++ b/src/luks/clevis-luks-report-sss -@@ -0,0 +1,53 @@ -+#!/bin/bash -e -+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: -+# -+# Copyright (c) 2018 Red Hat, Inc. -+# Author: Radovan Sroka -+# -+# 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 . -+# -+ -+. clevis-luks-common-functions -+ -+SUMMARY="SSS report plugin" -+ -+if [ "$1" == "--summary" ]; then -+ echo "$SUMMARY" -+ exit 1 -+fi -+ -+if [ -z "$1" ]; then -+ echo "$0 missing the first argument!" >&2 -+ exit 1 -+fi -+ -+CONTENT="$1" # sss content -+ -+CNT=0 -+while DATA_CODED="$(jose fmt -j- -g jwe -g"$CNT" -u- <<< "$CONTENT")"; do -+ if [ -z "$DATA_CODED" ]; then -+ CNT=$(( CNT + 1 )) -+ continue # in some cases it can be empty string -+ fi -+ -+ EXE="$(findexe clevis-luks-report-decode)" -+ if ! $EXE "$DATA_CODED"; then -+ echo "Failed" >&2 -+ exit 1 -+ fi -+ -+ CNT=$(( CNT + 1 )) -+done -+ -+exit 0 -diff --git a/src/luks/clevis-luks-report-tang b/src/luks/clevis-luks-report-tang -new file mode 100755 -index 0000000..07f2a72 ---- /dev/null -+++ b/src/luks/clevis-luks-report-tang -@@ -0,0 +1,67 @@ -+#!/usr/bin/bash -e -+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: -+# -+# Copyright (c) 2018 Red Hat, Inc. -+# Author: Radovan Sroka -+# -+# 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 . -+# -+ -+. clevis-luks-common-functions -+ -+SUMMARY="Tang report plugin" -+ -+if [ "$1" == "--summary" ]; then -+ echo "$SUMMARY" -+ exit 1 -+fi -+ -+if [ -z "$1" ]; then -+ echo "$0 missing the first argument!" -+ exit 1 -+fi -+ -+CONTENT="$1" -+ -+### Get the advertisement -+if ! URL="$(jose fmt -j- -g url -u- <<< "$CONTENT")" || [ -z "$URL" ]; then -+ echo "URL was not found!" >&2 -+ exit 1 -+fi -+ -+if ! jws="$(curl -sfg "$URL/adv")"; then -+ echo "Unable to fetch advertisement: $URL/adv!" >&2 -+ exit 1 -+fi -+ -+if ! TANG_KEYS="$(jose fmt -j- -Og payload -SyOg keys -AUo- <<< "$jws")"; then -+ echo "Advertisement is malformed!" >&2 -+ exit 1 -+fi -+ -+### Check advertisement validity -+ver="$(jose jwk use -i- -r -u verify -o- <<< "$TANG_KEYS")" -+if ! jose jws ver -i "$jws" -k- -a <<< "$ver"; then -+ echo "Advertisement is missing signatures!" >&2 -+ exit 1 -+fi -+ -+if ! LUKS_KEYS="$(jose fmt -j- -g adv -o- <<< "$CONTENT")" || [ -z "$LUKS_KEYS" ]; then -+ echo "LUKS keys from LUKS metadata were not found!" >&2 -+ exit 1 -+fi -+ -+EXE="$(findexe clevis-luks-report-compare)" -+ -+exec "$EXE" "$TANG_KEYS" "$LUKS_KEYS" -diff --git a/src/luks/clevis-luks-report.1.adoc b/src/luks/clevis-luks-report.1.adoc -new file mode 100644 -index 0000000..cf42afe ---- /dev/null -+++ b/src/luks/clevis-luks-report.1.adoc -@@ -0,0 +1,41 @@ -+CLEVIS-LUKS-REPORT(1) -+===================== -+:doctype: manpage -+ -+ -+== NAME -+ -+clevis-luks-report - Reports whether a pin bound to a LUKS1 or LUKS2 volume has been rotated -+ -+== SYNOPSIS -+ -+*clevis luks report* -d DEV -s SLT -+ -+== OVERVIEW -+ -+The *clevis luks report* command checks a given slot of a LUKS device and reports whether the pin bound to it -+-- if any -- has been rotated. -+ -+== OPTIONS -+ -+* *-d* _DEV_ : -+ The bound LUKS device -+ -+* *-s* _SLT_ : -+ The slot or key slot number for the pin to be verified -+ -+* *-q* : -+ Quiet mode. If used, we will not prompt whether to regenerate data with *clevis luks regen* -+ -+* *-r* : -+ Regenerates LUKS metadata with *clevis luks regen -d DEV -s SLOT* -+ -+== EXAMPLE -+ -+ Check whether the pin bound to slot 1 in /dev/sda1 has been rotated: -+ -+ # clevis luks report -d /dev/sda1 -s 1 -+ -+== SEE ALSO -+ -+link:clevis-luks-regen.1.adoc[*clevis-luks-regen*(1)] -diff --git a/src/luks/meson.build b/src/luks/meson.build -index 1f64ab0..7c045c4 100644 ---- a/src/luks/meson.build -+++ b/src/luks/meson.build -@@ -15,6 +15,18 @@ if libcryptsetup.found() and luksmeta.found() and pwmake.found() - bins += join_paths(meson.current_source_dir(), 'clevis-luks-bind') - mans += join_paths(meson.current_source_dir(), 'clevis-luks-bind.1') - -+ bins += join_paths(meson.current_source_dir(), 'clevis-luks-common-functions') -+ -+ bins += join_paths(meson.current_source_dir(), 'clevis-luks-regen') -+ mans += join_paths(meson.current_source_dir(), 'clevis-luks-regen.1') -+ -+ bins += join_paths(meson.current_source_dir(), 'clevis-luks-report') -+ bins += join_paths(meson.current_source_dir(), 'clevis-luks-report-compare') -+ bins += join_paths(meson.current_source_dir(), 'clevis-luks-report-decode') -+ bins += join_paths(meson.current_source_dir(), 'clevis-luks-report-sss') -+ bins += join_paths(meson.current_source_dir(), 'clevis-luks-report-tang') -+ mans += join_paths(meson.current_source_dir(), 'clevis-luks-report.1') -+ - mans += join_paths(meson.current_source_dir(), 'clevis-luks-unlockers.7') - else - warning('Will not install LUKS support due to missing dependencies!') --- -2.21.0 - diff --git a/SOURCES/Add-device-TCTI-library-to-the-initramfs.patch b/SOURCES/Add-device-TCTI-library-to-the-initramfs.patch deleted file mode 100644 index 6757e60..0000000 --- a/SOURCES/Add-device-TCTI-library-to-the-initramfs.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 6826e5d31d6323eac5137404f0194bf2183b561c Mon Sep 17 00:00:00 2001 -From: Javier Martinez Canillas -Date: Wed, 7 Nov 2018 16:48:47 +0100 -Subject: [PATCH 3/3] Add device TCTI library to the initramfs - -The tpm2-tools don't dynamically link against the TCTI libraries anymore, -but instead dlopen() the correct library depending on the TCTI used. - -So dracut isn't able anymore to figure out automatically using ldd what -libraries are needed by the tpm2-tools. Since clevis uses the device TCTI -to access the TPM directly, add the libtss2-tcti-device.so to the initrd. - -Suggested-by: Federico Chiacchiaretta - -Fixes: ##74 ---- - src/luks/systemd/dracut/module-setup.sh.in | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/src/luks/systemd/dracut/module-setup.sh.in b/src/luks/systemd/dracut/module-setup.sh.in -index 41e7d6c9b002..990bf4aeed56 100755 ---- a/src/luks/systemd/dracut/module-setup.sh.in -+++ b/src/luks/systemd/dracut/module-setup.sh.in -@@ -65,6 +65,7 @@ install() { - tpm2_pcrlist \ - tpm2_unseal \ - tpm2_load -+ inst_libdir_file "libtss2-tcti-device.so*" - fi - - dracut_need_initqueue --- -2.19.1 - diff --git a/SOURCES/Add-rd.neednet-1-to-cmdline-only-if-there-are-device.patch b/SOURCES/Add-rd.neednet-1-to-cmdline-only-if-there-are-device.patch deleted file mode 100644 index a73b797..0000000 --- a/SOURCES/Add-rd.neednet-1-to-cmdline-only-if-there-are-device.patch +++ /dev/null @@ -1,57 +0,0 @@ -From 25009adea66d3bf9b73f128273de28e532b03281 Mon Sep 17 00:00:00 2001 -From: Sergio Correia -Date: Sat, 30 Nov 2019 18:23:09 -0500 -Subject: [PATCH] Add rd.neednet=1 to cmdline only if there are devices bound - to tang - ---- - src/luks/systemd/dracut/module-setup.sh.in | 24 ++++++++++++++++++---- - 1 file changed, 20 insertions(+), 4 deletions(-) - -diff --git a/src/luks/systemd/dracut/module-setup.sh.in b/src/luks/systemd/dracut/module-setup.sh.in -index 990bf4a..841f7a8 100755 ---- a/src/luks/systemd/dracut/module-setup.sh.in -+++ b/src/luks/systemd/dracut/module-setup.sh.in -@@ -18,19 +18,35 @@ - # along with this program. If not, see . - # - -+is_bound_to_tang() { -+ local dev -+ for dev in $(lsblk -p -n -s -r \ -+ | awk '$6 == "crypt" { getline; print $1 }' | sort -u); do -+ if clevis luks list -d "${dev}" 2>/dev/null | grep -q tang; then -+ return 0 -+ fi -+ done -+ return 1 -+} -+ - depends() { -- echo crypt systemd network -- return 0 -+ local depends="crypt systemd" -+ if is_bound_to_tang; then -+ depends=$(printf "%s network" "${depends}") -+ fi -+ echo "${depends}" - } - --cmdline() { -+tang_cmdline() { - echo "rd.neednet=1" - } - - install() { - local ret=0 - -- cmdline > "${initdir}/etc/cmdline.d/99clevis.conf" -+ if is_bound_to_tang; then -+ tang_cmdline > "${initdir}/etc/cmdline.d/99clevis.conf" -+ fi - - inst_hook initqueue/online 60 "$moddir/clevis-hook.sh" - inst_hook initqueue/settled 60 "$moddir/clevis-hook.sh" --- -2.18.1 - diff --git a/SOURCES/Add-support-for-listing-existing-PBD-policies-in-pla.patch b/SOURCES/Add-support-for-listing-existing-PBD-policies-in-pla.patch deleted file mode 100644 index 4f6c2bf..0000000 --- a/SOURCES/Add-support-for-listing-existing-PBD-policies-in-pla.patch +++ /dev/null @@ -1,1009 +0,0 @@ -From a128130755bcd893ccf1d70b52c13fbaf29613c9 Mon Sep 17 00:00:00 2001 -From: Sergio Correia -Date: Sat, 30 Nov 2019 14:26:59 -0500 -Subject: [PATCH] Add clevis luks list command - -Usage: -clevis luks list -d DEV [-s SLT] - -Examples: - -clevis luks list -d device -1: sss '{"t":1,"pins":{"tang":[{"url":"addr1"},{"url":"addr2"}],"tpm2":[{"hash":"sha256","key":"ecc"}],"sss":{"t":1,"pins":{"tang":[{"url":"addr3"}]}}}}' -2: tang '{"url":"addr"}' -3: tpm2 '{"hash":"sha256","key":"ecc","pcr_bank":"sha1","pcr_ids":"7"}' - -clevis luks list -d device -s 3 -3: tpm2 '{"hash":"sha256","key":"ecc","pcr_bank":"sha1","pcr_ids":"7"}' ---- - src/luks/clevis-luks-common-functions | 173 ++++++++++++++++++++++++++ - src/luks/clevis-luks-list | 77 ++++++++++++ - src/luks/clevis-luks-list.1.adoc | 58 +++++++++ - src/luks/meson.build | 8 +- - src/luks/tests/list-recursive-luks1 | 85 +++++++++++++ - src/luks/tests/list-recursive-luks2 | 85 +++++++++++++ - src/luks/tests/list-sss-tang-luks1 | 77 ++++++++++++ - src/luks/tests/list-sss-tang-luks2 | 77 ++++++++++++ - src/luks/tests/list-tang-luks1 | 64 ++++++++++ - src/luks/tests/list-tang-luks2 | 64 ++++++++++ - src/luks/tests/meson.build | 36 ++++++ - src/luks/tests/tests-common-functions | 76 +++++++++++ - 12 files changed, 879 insertions(+), 1 deletion(-) - create mode 100755 src/luks/clevis-luks-list - create mode 100644 src/luks/clevis-luks-list.1.adoc - create mode 100755 src/luks/tests/list-recursive-luks1 - create mode 100755 src/luks/tests/list-recursive-luks2 - create mode 100755 src/luks/tests/list-sss-tang-luks1 - create mode 100755 src/luks/tests/list-sss-tang-luks2 - create mode 100755 src/luks/tests/list-tang-luks1 - create mode 100755 src/luks/tests/list-tang-luks2 - create mode 100644 src/luks/tests/meson.build - create mode 100644 src/luks/tests/tests-common-functions - -diff --git a/src/luks/clevis-luks-common-functions b/src/luks/clevis-luks-common-functions -index d676253..9ba1812 100644 ---- a/src/luks/clevis-luks-common-functions -+++ b/src/luks/clevis-luks-common-functions -@@ -141,3 +141,176 @@ findexe() { - return 1 - } - -+# clevis_luks_used_slots() will return the list of used slots for a given LUKS -+# device. -+clevis_luks_used_slots() { -+ local DEV="${1}" -+ -+ local slots -+ if cryptsetup isLuks --type luks1 "${DEV}"; then -+ readarray -t slots < <(cryptsetup luksDump "${DEV}" \ -+ | sed -rn 's|^Key Slot ([0-7]): ENABLED$|\1|p') -+ elif cryptsetup isLuks --type luks2 "${DEV}"; then -+ readarray -t slots < <(cryptsetup luksDump "${DEV}" \ -+ | sed -rn 's|^\s+([0-9]+): luks2$|\1|p') -+ else -+ echo "${DEV} is not a supported LUKS device!" >&2 -+ return 1 -+ fi -+ echo "${slots[@]}" -+} -+ -+# clevis_luks_decode_jwe() will decode a given JWE. -+clevis_luks_decode_jwe() { -+ local jwe="${1}" -+ -+ local coded -+ if ! coded=$(jose jwe fmt -i- <<< "${jwe}"); then -+ return 1 -+ fi -+ -+ coded=$(jose fmt -j- -g protected -u- <<< "${coded}" | tr -d '"') -+ jose b64 dec -i- <<< "${coded}" -+} -+ -+# clevis_luks_print_pin_config() will print the config of a given pin; i.e. -+# for tang it will display the associated url address, and for tpm2, the -+# properties in place, like the hash, for instance. -+clevis_luks_print_pin_config() { -+ local P="${1}" -+ local decoded="${2}" -+ -+ local content -+ if ! content="$(jose fmt -j- -g clevis -g "${P}" -o- <<< "${decoded}")" \ -+ || [[ -z "${content}" ]]; then -+ return 1 -+ fi -+ -+ local pin= -+ case "${P}" in -+ tang) -+ local url -+ url="$(jose fmt -j- -g url -u- <<< "${content}")" -+ pin=$(printf '{"url":"%s"}' "${url}") -+ printf "tang '%s'" "${pin}" -+ ;; -+ tpm2) -+ # Valid properties for tpm2 pin are the following: -+ # hash, key, pcr_bank, pcr_ids, pcr_digest. -+ local key -+ local value -+ for key in 'hash' 'key' 'pcr_bank' 'pcr_ids' 'pcr_digest'; do -+ if value=$(jose fmt -j- -g "${key}" -u- <<< "${content}"); then -+ pin=$(printf '%s,"%s":"%s"' "${pin}" "${key}" "${value}") -+ fi -+ done -+ # Remove possible leading comma. -+ pin=${pin/#,/} -+ printf "tpm2 '{%s}'" "${pin}" -+ ;; -+ sss) -+ local threshold -+ threshold=$(jose fmt -j- -Og t -o- <<< "${content}") -+ clevis_luks_process_sss_pin "${content}" "${threshold}" -+ ;; -+ *) -+ printf "unknown pin '%s'" "${P}" -+ ;; -+ esac -+} -+ -+# clevis_luks_decode_pin_config() will receive a JWE and extract a pin config -+# from it. -+clevis_luks_decode_pin_config() { -+ local jwe="${1}" -+ -+ local decoded -+ if ! decoded=$(clevis_luks_decode_jwe "${jwe}"); then -+ return 1 -+ fi -+ -+ local P -+ if ! P=$(jose fmt -j- -Og clevis -g pin -u- <<< "${decoded}"); then -+ return 1 -+ fi -+ -+ clevis_luks_print_pin_config "${P}" "${decoded}" -+} -+ -+# clevis_luks_join_sss_cfg() will receive a list of configurations for a given -+# pin and returns it as list, in the format PIN [cfg1, cfg2, ..., cfgN]. -+clevis_luks_join_sss_cfg() { -+ local pin="${1}" -+ local cfg="${2}" -+ cfg=$(echo "${cfg}" | tr -d "'" | sed -e 's/^,//') -+ printf '"%s":[%s]' "${pin}" "${cfg}" -+} -+ -+# clevis_luks_process_sss_pin() will receive a JWE with information on the sss -+# pin config, and also its associated threshold, and will extract the info. -+clevis_luks_process_sss_pin() { -+ local jwe="${1}" -+ local threshold="${2}" -+ -+ local sss_tang -+ local sss_tpm2 -+ local sss -+ local pin_cfg -+ local pin -+ local cfg -+ -+ local coded -+ for coded in $(jose fmt -j- -Og jwe -Af- <<< "${jwe}"| tr -d '"'); do -+ if ! pin_cfg="$(clevis_luks_decode_pin_config "${coded}")"; then -+ continue -+ fi -+ read -r pin cfg <<< "${pin_cfg}" -+ case "${pin}" in -+ tang) -+ sss_tang="${sss_tang},${cfg}" -+ ;; -+ tpm2) -+ sss_tpm2="${sss_tpm2},${cfg}" -+ ;; -+ sss) -+ sss=$(echo "${cfg}" | tr -d "'") -+ ;; -+ esac -+ done -+ -+ cfg= -+ if [[ -n "${sss_tang}" ]]; then -+ cfg=$(clevis_luks_join_sss_cfg "tang" "${sss_tang}") -+ fi -+ -+ if [[ -n "${sss_tpm2}" ]]; then -+ cfg="${cfg},"$(clevis_luks_join_sss_cfg "tpm2" "${sss_tpm2}") -+ fi -+ -+ if [[ -n "${sss}" ]]; then -+ cfg=$(printf '%s,"sss":%s' "${cfg}" "${sss}") -+ fi -+ -+ # Remove possible leading comma. -+ cfg=${cfg/#,/} -+ pin=$(printf '{"t":%d,"pins":{%s}}' "${threshold}" "${cfg}") -+ printf "sss '%s'" "${pin}" -+} -+ -+# clevis_luks_read_pins_from_slot() will receive a given device and slot and -+# will then output its associated policy configuration. -+clevis_luks_read_pins_from_slot() { -+ local DEV="${1}" -+ local SLOT="${2}" -+ -+ local jwe -+ if ! jwe=$(clevis_luks_read_slot "${DEV}" "${SLOT}" 2>/dev/null); then -+ return 1 -+ fi -+ -+ local cfg -+ if ! cfg="$(clevis_luks_decode_pin_config "${jwe}")"; then -+ return 1 -+ fi -+ printf "%s: %s\n" "${SLOT}" "${cfg}" -+} -diff --git a/src/luks/clevis-luks-list b/src/luks/clevis-luks-list -new file mode 100755 -index 0000000..58678c4 ---- /dev/null -+++ b/src/luks/clevis-luks-list -@@ -0,0 +1,77 @@ -+#!/bin/bash -e -+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: -+# -+# Copyright (c) 2017-2019 Red Hat, Inc. -+# Author: Javier Martinez Canillas -+# Author: Sergio Correia - LUKS2 support. -+# -+# 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 . -+# -+ -+. clevis-luks-common-functions -+ -+SUMMARY="Lists pins bound to a LUKSv1 or LUKSv2 device" -+ -+function usage() { -+ echo >&2 -+ echo "Usage: clevis luks list -d DEV [-s SLT]" >&2 -+ echo >&2 -+ echo "$SUMMARY": >&2 -+ echo >&2 -+ echo " -d DEV The LUKS device to list bound pins" >&2 -+ echo >&2 -+ echo " -s SLOT The slot number to list" >&2 -+ echo >&2 -+ exit 1 -+} -+ -+if [ ${#} -eq 1 ] && [ "${1}" = "--summary" ]; then -+ echo "${SUMMARY}" -+ exit 0 -+fi -+ -+while getopts ":d:s:" o; do -+ case "$o" in -+ d) DEV=${OPTARG};; -+ s) SLT=${OPTARG};; -+ *) usage;; -+ esac -+done -+ -+if [ -z "${DEV}" ]; then -+ echo "Did not specify a device!" >&2 -+ usage -+fi -+ -+if cryptsetup isLuks --type luks1 "${DEV}"; then -+ if ! luksmeta test -d "${DEV}" 2>/dev/null; then -+ echo "The ${DEV} device is not valid!" >&2 -+ exit 1 -+ fi -+fi -+ -+if [ -n "${SLT}" ]; then -+ clevis_luks_read_pins_from_slot "${DEV}" "${SLT}" -+else -+ if ! slots=$(clevis_luks_used_slots "${DEV}"); then -+ echo "No used slots detected for device ${DEV}!" >&2 -+ exit 1 -+ fi -+ -+ for s in ${slots}; do -+ if ! clevis_luks_read_pins_from_slot "${DEV}" "${s}"; then -+ continue -+ fi -+ done -+fi -diff --git a/src/luks/clevis-luks-list.1.adoc b/src/luks/clevis-luks-list.1.adoc -new file mode 100644 -index 0000000..2e84f05 ---- /dev/null -+++ b/src/luks/clevis-luks-list.1.adoc -@@ -0,0 +1,58 @@ -+CLEVIS-LUKS-LIST(1) -+=================== -+:doctype: manpage -+ -+ -+== NAME -+ -+clevis-luks-list - Lists pins bound to a LUKS device -+ -+== SYNOPSIS -+ -+*clevis luks list* -d DEV [-s SLT] -+ -+== OVERVIEW -+ -+The *clevis luks list* command list the pins bound to LUKS device. -+For example: -+ -+ clevis luks list -d /dev/sda1 -+ -+== OPTIONS -+ -+* *-d* _DEV_ : -+ The LUKS device on which to list bound pins -+ -+* *-s* _SLT_ : -+ The slot to use for listing the pin from -+ -+== EXAMPLES -+ -+ clevis luks list -d /dev/sda1 -+ 1: sss '{"t":1,"pins":{"tang":[{"url":"addr1"},{"url":"addr2"}],"tpm2":[{"hash":"sha256","key":"ecc"}],"sss":{"t":1,"pins":{"tang":[{"url":"addr3"}]}}}}' -+ 2: tang '{"url":"addr"}' -+ 3: tpm2 '{"hash":"sha256","key":"ecc","pcr_bank":"sha1","pcr_ids":"7"}' -+ -+As we can see in the example above, */dev/sda1* has three slots bound each with a different pin. -+- Slot #1 is bound with the _sss_ pin, and uses also tang and tpm2 pins in its policy. -+- Slot #2 is bound using the _tang_ pin -+- Slot #3 is bound with the _tpm2_ pin -+ -+Note that the output of *clevis luks list* can be used with the *clevis luks bind* command, such as: -+ -+ clevis luks bind -d /dev/sda1 tpm2 '{"hash":"sha256","key":"ecc","pcr_bank":"sha1","pcr_ids":"7"}' -+ -+And we will bind another slot with a policy similar to the one we have in slot #3. -+Also note that if you are interested in a particular slot, you can pass the _-s SLT_ argument to *clevis luks list*: -+ -+ clevis luks list -d /dev/sda1 -s 2 -+ 2: tang '{"url":"addr"}' -+ -+In the above example, we listed only the pin bound to slot #2. -+ -+== SEE ALSO -+ -+link:clevis-luks-bind.1.adoc[*clevis-luks-bind*(1)], -+link:clevis-encrypt-tang.1.adoc[*clevis-encrypt-tang*(1)], -+link:clevis-encrypt-tpm2.1.adoc[*clevis-encrypt-tpm2*(1)], -+link:clevis-encrypt-sss.1.adoc[*clevis-encrypt-sss*(1)], -diff --git a/src/luks/meson.build b/src/luks/meson.build -index 7c045c4..51d82fb 100644 ---- a/src/luks/meson.build -+++ b/src/luks/meson.build -@@ -20,6 +20,9 @@ if libcryptsetup.found() and luksmeta.found() and pwmake.found() - bins += join_paths(meson.current_source_dir(), 'clevis-luks-regen') - mans += join_paths(meson.current_source_dir(), 'clevis-luks-regen.1') - -+ bins += join_paths(meson.current_source_dir(), 'clevis-luks-list') -+ mans += join_paths(meson.current_source_dir(), 'clevis-luks-list.1') -+ - bins += join_paths(meson.current_source_dir(), 'clevis-luks-report') - bins += join_paths(meson.current_source_dir(), 'clevis-luks-report-compare') - bins += join_paths(meson.current_source_dir(), 'clevis-luks-report-decode') -@@ -30,4 +33,7 @@ if libcryptsetup.found() and luksmeta.found() and pwmake.found() - mans += join_paths(meson.current_source_dir(), 'clevis-luks-unlockers.7') - else - warning('Will not install LUKS support due to missing dependencies!') --endif -\ No newline at end of file -+endif -+ -+# Tests. -+subdir('tests') -diff --git a/src/luks/tests/list-recursive-luks1 b/src/luks/tests/list-recursive-luks1 -new file mode 100755 -index 0000000..d9eaa3a ---- /dev/null -+++ b/src/luks/tests/list-recursive-luks1 -@@ -0,0 +1,85 @@ -+#!/bin/bash -ex -+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: -+# -+# Copyright (c) 2019 Red Hat, Inc. -+# Author: Sergio Correia -+# -+# 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 . -+# -+ -+TEST=$(basename "${0}") -+. tests-common-functions -+ -+on_exit() { -+ [ -d "${TMP}" ] && rm -rf "${TMP}" -+} -+ -+trap 'on_exit' EXIT -+trap 'exit' ERR -+ -+TMP="$(mktemp -d)" -+ -+ADV="${TMP}/adv.jws" -+create_tang_adv "${ADV}" -+PIN="sss" -+CFG=$(printf ' -+{ -+ "t": 1, -+ "pins": { -+ "sss": { -+ "t": 1, -+ "pins": { -+ "sss": { -+ "t": 1, -+ "pins": { -+ "tang": [ -+ { -+ "url": "ADDR","adv": "%s" -+ } -+ ] -+ } -+ } -+ } -+ } -+ } -+} -+' "${ADV}") -+ -+# LUKS1. -+DEV="${TMP}/luks1-device" -+UUID="cb6e8904-81ff-40da-a84a-07ab9ab5715e" -+new_device "luks1" "${DEV}" -+ -+if ! clevis luks bind -f -d "${DEV}" "${PIN}" "${CFG}" <<< "${DEFAULT_PASS}"; then -+ error "${TEST}: Binding is expected to succeed when given a correct (${DEFAULT_PASS}) password." -+fi -+ -+SLT=1 -+if ! read -r slot pin cfg < <(clevis luks list -d "${DEV}" -s "${SLT}"); then -+ error "${TEST}: clevis luks list is expected to succeed for device(${DEV}) and slot (${SLT})" -+fi -+ -+if [[ "${slot}" != "${SLT}:" ]]; then -+ error "${TEST}: slot (${slot}) is expected to be ${SLT}" -+fi -+ -+if [[ "${pin}" != "${PIN}" ]]; then -+ error "${TEST}: pin (${pin}) is expected to be '${PIN}'" -+fi -+ -+to_remove_from_cfg=$(printf ',"adv": "%s"' "${ADV}") -+cfg_for_cmp=${cfg//"${to_remove_from_cfg}"/} -+if ! pin_cfg_equal "${cfg}" "${cfg_for_cmp}"; then -+ error "${TEST}: config obtained from clevis luks list (${cfg}) is expected to match the one used to bind the test (${cfg_for_cmp})" -+fi -diff --git a/src/luks/tests/list-recursive-luks2 b/src/luks/tests/list-recursive-luks2 -new file mode 100755 -index 0000000..80a8278 ---- /dev/null -+++ b/src/luks/tests/list-recursive-luks2 -@@ -0,0 +1,85 @@ -+#!/bin/bash -ex -+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: -+# -+# Copyright (c) 2019 Red Hat, Inc. -+# Author: Sergio Correia -+# -+# 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 . -+# -+ -+TEST=$(basename "${0}") -+. tests-common-functions -+ -+on_exit() { -+ [ -d "${TMP}" ] && rm -rf "${TMP}" -+} -+ -+trap 'on_exit' EXIT -+trap 'exit' ERR -+ -+TMP="$(mktemp -d)" -+ -+ADV="${TMP}/adv.jws" -+create_tang_adv "${ADV}" -+PIN="sss" -+CFG=$(printf ' -+{ -+ "t": 1, -+ "pins": { -+ "sss": { -+ "t": 1, -+ "pins": { -+ "sss": { -+ "t": 1, -+ "pins": { -+ "tang": [ -+ { -+ "url": "ADDR","adv": "%s" -+ } -+ ] -+ } -+ } -+ } -+ } -+ } -+} -+' "${ADV}") -+ -+# LUKS2. -+DEV="${TMP}/luks1-device" -+UUID="cb6e8904-81ff-40da-a84a-07ab9ab5715e" -+new_device "luks2" "${DEV}" -+ -+if ! clevis luks bind -f -d "${DEV}" "${PIN}" "${CFG}" <<< "${DEFAULT_PASS}"; then -+ error "${TEST}: Binding is expected to succeed when given a correct (${DEFAULT_PASS}) password." -+fi -+ -+SLT=1 -+if ! read -r slot pin cfg < <(clevis luks list -d "${DEV}" -s "${SLT}"); then -+ error "${TEST}: clevis luks list is expected to succeed for device(${DEV}) and slot (${SLT})" -+fi -+ -+if [[ "${slot}" != "${SLT}:" ]]; then -+ error "${TEST}: slot (${slot}) is expected to be ${SLT}" -+fi -+ -+if [[ "${pin}" != "${PIN}" ]]; then -+ error "${TEST}: pin (${pin}) is expected to be '${PIN}'" -+fi -+ -+to_remove_from_cfg=$(printf ',"adv": "%s"' "${ADV}") -+cfg_for_cmp=${cfg//"${to_remove_from_cfg}"/} -+if ! pin_cfg_equal "${cfg}" "${cfg_for_cmp}"; then -+ error "${TEST}: config obtained from clevis luks list (${cfg}) is expected to match the one used to bind the test (${cfg_for_cmp})" -+fi -diff --git a/src/luks/tests/list-sss-tang-luks1 b/src/luks/tests/list-sss-tang-luks1 -new file mode 100755 -index 0000000..086fa35 ---- /dev/null -+++ b/src/luks/tests/list-sss-tang-luks1 -@@ -0,0 +1,77 @@ -+#!/bin/bash -ex -+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: -+# -+# Copyright (c) 2019 Red Hat, Inc. -+# Author: Sergio Correia -+# -+# 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 . -+# -+ -+TEST=$(basename "${0}") -+. tests-common-functions -+ -+on_exit() { -+ [ -d "${TMP}" ] && rm -rf "${TMP}" -+} -+ -+trap 'on_exit' EXIT -+trap 'exit' ERR -+ -+TMP="$(mktemp -d)" -+ -+ADV="${TMP}/adv.jws" -+create_tang_adv "${ADV}" -+PIN="sss" -+CFG=$(printf ' -+{ -+ "t": 2, -+ "pins": { -+ "tang": [ -+ {"url":"ADDR1","adv":"%s"}, -+ {"url":"ADDR2","adv":"%s"}, -+ {"url":"ADDR3","adv":"%s"}, -+ {"url":"ADDR4","adv":"%s"}, -+ {"url":"ADDR5","adv":"%s"} -+ ] -+ } -+} -+' "${ADV}" "${ADV}" "${ADV}" "${ADV}" "${ADV}") -+ -+# LUKS1. -+DEV="${TMP}/luks1-device" -+UUID="cb6e8904-81ff-40da-a84a-07ab9ab5715e" -+new_device "luks1" "${DEV}" -+ -+if ! clevis luks bind -f -d "${DEV}" ${PIN} "${CFG}" <<< "${DEFAULT_PASS}"; then -+ error "${TEST}: Binding is expected to succeed when given a correct (${DEFAULT_PASS}) password." -+fi -+ -+SLT=1 -+if ! read -r slot pin cfg < <(clevis luks list -d "${DEV}" -s "${SLT}"); then -+ error "${TEST}: clevis luks list is expected to succeed for device(${DEV}) and slot (${SLT})" -+fi -+ -+if [[ "${slot}" != "${SLT}:" ]]; then -+ error "${TEST}: slot (${slot}) is expected to be ${SLT}" -+fi -+ -+if [[ "${pin}" != "${PIN}" ]]; then -+ error "${TEST}: pin (${pin}) is expected to be '${PIN}'" -+fi -+ -+to_remove_from_cfg=$(printf ',"adv": "%s"' "${ADV}") -+cfg_for_cmp=${cfg//"${to_remove_from_cfg}"/} -+if ! pin_cfg_equal "${cfg}" "${cfg_for_cmp}"; then -+ error "${TEST}: config obtained from clevis luks list (${cfg}) is expected to match the one used to bind the test (${cfg_for_cmp})" -+fi -diff --git a/src/luks/tests/list-sss-tang-luks2 b/src/luks/tests/list-sss-tang-luks2 -new file mode 100755 -index 0000000..ea4cfbb ---- /dev/null -+++ b/src/luks/tests/list-sss-tang-luks2 -@@ -0,0 +1,77 @@ -+#!/bin/bash -ex -+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: -+# -+# Copyright (c) 2019 Red Hat, Inc. -+# Author: Sergio Correia -+# -+# 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 . -+# -+ -+TEST=$(basename "${0}") -+. tests-common-functions -+ -+on_exit() { -+ [ -d "${TMP}" ] && rm -rf "${TMP}" -+} -+ -+trap 'on_exit' EXIT -+trap 'exit' ERR -+ -+TMP="$(mktemp -d)" -+ -+ADV="${TMP}/adv.jws" -+create_tang_adv "${ADV}" -+PIN="sss" -+CFG=$(printf ' -+{ -+ "t": 2, -+ "pins": { -+ "tang": [ -+ {"url":"ADDR1","adv":"%s"}, -+ {"url":"ADDR2","adv":"%s"}, -+ {"url":"ADDR3","adv":"%s"}, -+ {"url":"ADDR4","adv":"%s"}, -+ {"url":"ADDR5","adv":"%s"} -+ ] -+ } -+} -+' "${ADV}" "${ADV}" "${ADV}" "${ADV}" "${ADV}") -+ -+# LUKS2. -+DEV="${TMP}/luks1-device" -+UUID="cb6e8904-81ff-40da-a84a-07ab9ab5715e" -+new_device "luks2" "${DEV}" -+ -+if ! clevis luks bind -f -d "${DEV}" ${PIN} "${CFG}" <<< "${DEFAULT_PASS}"; then -+ error "${TEST}: Binding is expected to succeed when given a correct (${DEFAULT_PASS}) password." -+fi -+ -+SLT=1 -+if ! read -r slot pin cfg < <(clevis luks list -d "${DEV}" -s "${SLT}"); then -+ error "${TEST}: clevis luks list is expected to succeed for device(${DEV}) and slot (${SLT})" -+fi -+ -+if [[ "${slot}" != "${SLT}:" ]]; then -+ error "${TEST}: slot (${slot}) is expected to be ${SLT}" -+fi -+ -+if [[ "${pin}" != "${PIN}" ]]; then -+ error "${TEST}: pin (${pin}) is expected to be '${PIN}'" -+fi -+ -+to_remove_from_cfg=$(printf ',"adv": "%s"' "${ADV}") -+cfg_for_cmp=${cfg//"${to_remove_from_cfg}"/} -+if ! pin_cfg_equal "${cfg}" "${cfg_for_cmp}"; then -+ error "${TEST}: config obtained from clevis luks list (${cfg}) is expected to match the one used to bind the test (${cfg_for_cmp})" -+fi -diff --git a/src/luks/tests/list-tang-luks1 b/src/luks/tests/list-tang-luks1 -new file mode 100755 -index 0000000..c526693 ---- /dev/null -+++ b/src/luks/tests/list-tang-luks1 -@@ -0,0 +1,64 @@ -+#!/bin/bash -ex -+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: -+# -+# Copyright (c) 2019 Red Hat, Inc. -+# Author: Sergio Correia -+# -+# 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 . -+# -+ -+TEST=$(basename "${0}") -+. tests-common-functions -+ -+on_exit() { -+ [ -d "${TMP}" ] && rm -rf "${TMP}" -+} -+ -+trap 'on_exit' EXIT -+trap 'exit' ERR -+ -+TMP="$(mktemp -d)" -+ -+ADV="${TMP}/adv.jws" -+create_tang_adv "${ADV}" -+PIN="tang" -+CFG=$(printf '{"url": "ADDR","adv": "%s"}' "${ADV}") -+ -+# LUKS1. -+DEV="${TMP}/luks1-device" -+UUID="cb6e8904-81ff-40da-a84a-07ab9ab5715e" -+new_device "luks1" "${DEV}" -+ -+if ! clevis luks bind -f -d "${DEV}" "${PIN}" "${CFG}" <<< "${DEFAULT_PASS}"; then -+ error "${TEST}: Binding is expected to succeed when given a correct (${DEFAULT_PASS}) password." -+fi -+ -+SLT=1 -+if ! read -r slot pin cfg < <(clevis luks list -d "${DEV}" -s "${SLT}"); then -+ error "${TEST}: clevis luks list is expected to succeed for device(${DEV}) and slot (${SLT})" -+fi -+ -+if [[ "${slot}" != "${SLT}:" ]]; then -+ error "${TEST}: slot (${slot}) is expected to be ${SLT}" -+fi -+ -+if [[ "${pin}" != "${PIN}" ]]; then -+ error "${TEST}: pin (${pin}) is expected to be '${PIN}'" -+fi -+ -+to_remove_from_cfg=$(printf ',"adv": "%s"' "${ADV}") -+cfg_for_cmp=${cfg//"${to_remove_from_cfg}"/} -+if ! pin_cfg_equal "${cfg}" "${cfg_for_cmp}"; then -+ error "${TEST}: config obtained from clevis luks list (${cfg}) is expected to match the one used to bind the test (${cfg_for_cmp})" -+fi -diff --git a/src/luks/tests/list-tang-luks2 b/src/luks/tests/list-tang-luks2 -new file mode 100755 -index 0000000..d4d4849 ---- /dev/null -+++ b/src/luks/tests/list-tang-luks2 -@@ -0,0 +1,64 @@ -+#!/bin/bash -ex -+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: -+# -+# Copyright (c) 2019 Red Hat, Inc. -+# Author: Sergio Correia -+# -+# 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 . -+# -+ -+TEST=$(basename "${0}") -+. tests-common-functions -+ -+on_exit() { -+ [ -d "${TMP}" ] && rm -rf "${TMP}" -+} -+ -+trap 'on_exit' EXIT -+trap 'exit' ERR -+ -+TMP="$(mktemp -d)" -+ -+ADV="${TMP}/adv.jws" -+create_tang_adv "${ADV}" -+PIN="tang" -+CFG=$(printf '{"url": "ADDR","adv": "%s"}' "${ADV}") -+ -+# LUKS2. -+DEV="${TMP}/luks1-device" -+UUID="cb6e8904-81ff-40da-a84a-07ab9ab5715e" -+new_device "luks2" "${DEV}" -+ -+if ! clevis luks bind -f -d "${DEV}" "${PIN}" "${CFG}" <<< "${DEFAULT_PASS}"; then -+ error "${TEST}: Binding is expected to succeed when given a correct (${DEFAULT_PASS}) password." -+fi -+ -+SLT=1 -+if ! read -r slot pin cfg < <(clevis luks list -d "${DEV}" -s "${SLT}"); then -+ error "${TEST}: clevis luks list is expected to succeed for device(${DEV}) and slot (${SLT})" -+fi -+ -+if [[ "${slot}" != "${SLT}:" ]]; then -+ error "${TEST}: slot (${slot}) is expected to be ${SLT}" -+fi -+ -+if [[ "${pin}" != "${PIN}" ]]; then -+ error "${TEST}: pin (${pin}) is expected to be '${PIN}'" -+fi -+ -+to_remove_from_cfg=$(printf ',"adv": "%s"' "${ADV}") -+cfg_for_cmp=${cfg//"${to_remove_from_cfg}"/} -+if ! pin_cfg_equal "${cfg}" "${cfg_for_cmp}"; then -+ error "${TEST}: config obtained from clevis luks list (${cfg}) is expected to match the one used to bind the test (${cfg_for_cmp})" -+fi -diff --git a/src/luks/tests/meson.build b/src/luks/tests/meson.build -new file mode 100644 -index 0000000..6513eaa ---- /dev/null -+++ b/src/luks/tests/meson.build -@@ -0,0 +1,36 @@ -+# We use jq for comparing the pin config in the clevis luks list tests. -+jq = find_program('jq', required: false) -+ -+env = environment() -+env.prepend('PATH', -+ join_paths(meson.source_root(), 'src'), -+ join_paths(meson.source_root(), 'src', 'luks'), -+ join_paths(meson.source_root(), 'src', 'pins', 'sss'), -+ join_paths(meson.source_root(), 'src', 'pins', 'tang'), -+ join_paths(meson.source_root(), 'src', 'pins', 'tpm2'), -+ meson.current_source_dir(), -+ meson.current_build_dir(), -+ join_paths(meson.build_root(), 'src'), -+ join_paths(meson.build_root(), 'src', 'luks'), -+ join_paths(meson.build_root(), 'src', 'pins', 'sss'), -+ join_paths(meson.build_root(), 'src', 'pins', 'tang'), -+ join_paths(meson.build_root(), 'src', 'pins', 'tpm2'), -+ separator: ':' -+) -+ -+if jq.found() -+ test('list-recursive-luks1', find_program('list-recursive-luks1'), env: env) -+ test('list-tang-luks1', find_program('list-tang-luks1'), env: env) -+ test('list-sss-tang-luks1', find_program('list-sss-tang-luks1'), env: env) -+else -+ warning('Will not run "clevis luks list" tests due to missing jq dependency') -+endif -+ -+# LUKS2 tests go here, and they get included if we get support for it, based -+# on the cryptsetup version. -+# Binding LUKS2 takes longer, so timeout is increased for a few tests. -+if jq.found() -+ test('list-recursive-luks2', find_program('list-recursive-luks2'), env: env, timeout: 60) -+ test('list-tang-luks2', find_program('list-tang-luks2'), env: env, timeout: 60) -+ test('list-sss-tang-luks2', find_program('list-sss-tang-luks2'), env: env, timeout: 60) -+endif -diff --git a/src/luks/tests/tests-common-functions b/src/luks/tests/tests-common-functions -new file mode 100644 -index 0000000..b65a84a ---- /dev/null -+++ b/src/luks/tests/tests-common-functions -@@ -0,0 +1,76 @@ -+#!/bin/bash -ex -+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: -+# -+# Copyright (c) 2019 Red Hat, Inc. -+# Author: Sergio Correia -+# -+# 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 . -+# -+ -+# We require cryptsetup >= 2.0.4 to fully support LUKSv2. -+# Support is determined at build time. -+luks2_supported() { -+ # In RHEL8 we support LUKS2. -+ return 0 -+} -+ -+# Creates a tang adv to be used in the test. -+create_tang_adv() { -+ local adv="${1}" -+ local SIG="${TMP}/sig.jwk" -+ jose jwk gen -i '{"alg":"ES512"}' > "${SIG}" -+ -+ local EXC="${TMP}/exc.jwk" -+ jose jwk gen -i '{"alg":"ECMR"}' > "${EXC}" -+ -+ local TEMPLATE='{"protected":{"cty":"jwk-set+json"}}' -+ jose jwk pub -s -i "${SIG}" -i "${EXC}" \ -+ | jose jws sig -I- -s "${TEMPLATE}" -k "${SIG}" -o "${adv}" -+} -+ -+ -+# Creates a new LUKS1 or LUKS2 device to be used. -+new_device() { -+ local LUKS="${1}" -+ local DEV="${2}" -+ -+ local DEV_CACHED="${TMP}/${LUKS}.cached" -+ -+ # Let's reuse an existing device, if there is one. -+ if [ -f "${DEV_CACHED}" ]; then -+ echo "Reusing cached ${LUKS} device..." -+ cp -f "${DEV_CACHED}" "${DEV}" -+ return 0 -+ fi -+ -+ fallocate -l16M "${DEV}" -+ cryptsetup luksFormat --type "${LUKS}" --batch-mode --force-password "${DEV}" <<< "${DEFAULT_PASS}" -+ # Caching the just-formatted device for possible reuse. -+ cp -f "${DEV}" "${DEV_CACHED}" -+} -+ -+error() { -+ echo "${1}" >&2 -+ exit 1 -+} -+ -+pin_cfg_equal() { -+ local cfg1="${1}" -+ local cfg2="${1}" -+ -+ diff <(jq -S . < <(echo -n "${cfg1}")) \ -+ <(jq -S . < <(echo -n "${cfg2}")) -+} -+ -+export DEFAULT_PASS='just-some-test-password-here' --- -2.18.1 - diff --git a/SOURCES/Add-the-option-to-extract-luks-passphrase-used-for-b.patch b/SOURCES/Add-the-option-to-extract-luks-passphrase-used-for-b.patch deleted file mode 100644 index 4f2465d..0000000 --- a/SOURCES/Add-the-option-to-extract-luks-passphrase-used-for-b.patch +++ /dev/null @@ -1,364 +0,0 @@ -From 69556d143544a72f84e9daf25924e3ae5132ce1a Mon Sep 17 00:00:00 2001 -From: Sergio Correia -Date: Sat, 30 Nov 2019 14:58:43 -0500 -Subject: [PATCH] Add the option to extract luks passphrase used for binding - -Usage: - -clevis luks pass -d /dev/sda1 -s 1 - ---- - src/luks/clevis-luks-pass | 69 +++++++++++++++++++++++++++++ - src/luks/clevis-luks-pass.1.adoc | 43 ++++++++++++++++++ - src/luks/meson.build | 3 ++ - src/luks/tests/meson.build | 11 +++++ - src/luks/tests/pass-tang-luks1 | 75 ++++++++++++++++++++++++++++++++ - src/luks/tests/pass-tang-luks2 | 75 ++++++++++++++++++++++++++++++++ - 6 files changed, 276 insertions(+) - create mode 100755 src/luks/clevis-luks-pass - create mode 100644 src/luks/clevis-luks-pass.1.adoc - create mode 100755 src/luks/tests/pass-tang-luks1 - create mode 100755 src/luks/tests/pass-tang-luks2 - -diff --git a/src/luks/clevis-luks-pass b/src/luks/clevis-luks-pass -new file mode 100755 -index 0000000..1ce8c4c ---- /dev/null -+++ b/src/luks/clevis-luks-pass -@@ -0,0 +1,69 @@ -+#!/bin/bash -e -+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: -+# -+# Copyright (c) 2019 Red Hat, Inc. -+# Author: Sergio Correia - LUKS2 support. -+# -+# 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 . -+# -+ -+. clevis-luks-common-functions -+ -+SUMMARY="Returns the LUKS passphrase used for binding a particular slot." -+ -+function usage() { -+ echo >&2 -+ echo "Usage: clevis luks pass -d DEV -s SLT" >&2 -+ echo >&2 -+ echo "$SUMMARY": >&2 -+ echo >&2 -+ echo " -d DEV The LUKS device to extract the LUKS passphrase used for binding" >&2 -+ echo >&2 -+ echo " -s SLOT The slot number to extract the LUKS passphrase" >&2 -+ echo >&2 -+ exit 1 -+} -+ -+if [ ${#} -eq 1 ] && [ "${1}" = "--summary" ]; then -+ echo "${SUMMARY}" -+ exit 0 -+fi -+ -+while getopts ":d:s:" o; do -+ case "$o" in -+ d) DEV=${OPTARG};; -+ s) SLT=${OPTARG};; -+ *) usage;; -+ esac -+done -+ -+if [ -z "${DEV}" ]; then -+ echo "Did not specify a device!" >&2 -+ usage -+fi -+ -+if [ -z "${SLT}" ]; then -+ echo "Did not specify a slot!" >&2 -+ usage -+fi -+ -+if ! jwe=$(clevis_luks_read_slot "${DEV}" "${SLT}" 2>/dev/null); then -+ echo "It was not possible to read slot ${SLT} from ${DEV}!" >&2 -+ exit 1 -+fi -+ -+if ! clevis decrypt < <(echo -n "${jwe}"); then -+ echo "It was not possible to decrypt the passphrase associated to slot ${SLT} in {DEV}!" >&2 -+ exit 1 -+fi -diff --git a/src/luks/clevis-luks-pass.1.adoc b/src/luks/clevis-luks-pass.1.adoc -new file mode 100644 -index 0000000..fa9526a ---- /dev/null -+++ b/src/luks/clevis-luks-pass.1.adoc -@@ -0,0 +1,43 @@ -+CLEVIS-LUKS-PASS(1) -+=================== -+:doctype: manpage -+ -+ -+== NAME -+ -+clevis-luks-pass - Extracts the passphrase used for binding a particular slot in a LUKS device -+ -+== SYNOPSIS -+ -+*clevis luks pass* -d DEV -s SLT -+ -+== OVERVIEW -+ -+The *clevis luks pass* command extracts the passphrase used for binding a particular slot in a LUKS device. -+For example: -+ -+ clevis luks pass -d /dev/sda1 -s 1 -+ -+== OPTIONS -+ -+* *-d* _DEV_ : -+ The LUKS device on which to extract a passphrase from -+ -+* *-s* _SLT_ : -+ The slot to use for extracting the passphrase -+ -+== EXAMPLE -+ -+ clevis luks pass -d /dev/sda1 -s 1 -+ -+ -+Note that the output of *clevis luks pass* might be non-printable, in which case it would be better to redirect its output to a file and use it as a key -+file together with cryptsetup. For instance: -+ -+ clevis luks pass -d /dev/sda1 -s 1 > slot1-passphrase -+ -+And the file slot1-passphrase will contain the passphrase associated with slot #1 in /dev/sda1. -+ -+== SEE ALSO -+ -+link:clevis-luks-unlock.1.adoc[*clevis-luks-unlock*(1)], -diff --git a/src/luks/meson.build b/src/luks/meson.build -index 51d82fb..b2dd724 100644 ---- a/src/luks/meson.build -+++ b/src/luks/meson.build -@@ -23,6 +23,9 @@ if libcryptsetup.found() and luksmeta.found() and pwmake.found() - bins += join_paths(meson.current_source_dir(), 'clevis-luks-list') - mans += join_paths(meson.current_source_dir(), 'clevis-luks-list.1') - -+ bins += join_paths(meson.current_source_dir(), 'clevis-luks-pass') -+ mans += join_paths(meson.current_source_dir(), 'clevis-luks-pass.1') -+ - bins += join_paths(meson.current_source_dir(), 'clevis-luks-report') - bins += join_paths(meson.current_source_dir(), 'clevis-luks-report-compare') - bins += join_paths(meson.current_source_dir(), 'clevis-luks-report-decode') -diff --git a/src/luks/tests/meson.build b/src/luks/tests/meson.build -index 6513eaa..248d2ea 100644 ---- a/src/luks/tests/meson.build -+++ b/src/luks/tests/meson.build -@@ -1,3 +1,9 @@ -+actv = find_program( -+ 'systemd-socket-activate', -+ 'systemd-activate', -+ required: false -+) -+ - # We use jq for comparing the pin config in the clevis luks list tests. - jq = find_program('jq', required: false) - -@@ -15,8 +21,11 @@ env.prepend('PATH', - join_paths(meson.build_root(), 'src', 'pins', 'sss'), - join_paths(meson.build_root(), 'src', 'pins', 'tang'), - join_paths(meson.build_root(), 'src', 'pins', 'tpm2'), -+ libexecdir, -+ '/usr/libexec', - separator: ':' - ) -+env.set('SD_ACTIVATE', actv.path()) - - if jq.found() - test('list-recursive-luks1', find_program('list-recursive-luks1'), env: env) -@@ -25,6 +34,7 @@ if jq.found() - else - warning('Will not run "clevis luks list" tests due to missing jq dependency') - endif -+test('pass-tang-luks1', find_program('pass-tang-luks1'), env: env) - - # LUKS2 tests go here, and they get included if we get support for it, based - # on the cryptsetup version. -@@ -34,3 +44,4 @@ if jq.found() - test('list-tang-luks2', find_program('list-tang-luks2'), env: env, timeout: 60) - test('list-sss-tang-luks2', find_program('list-sss-tang-luks2'), env: env, timeout: 60) - endif -+test('pass-tang-luks2', find_program('pass-tang-luks2'), env: env, timeout: 60) -diff --git a/src/luks/tests/pass-tang-luks1 b/src/luks/tests/pass-tang-luks1 -new file mode 100755 -index 0000000..05cdb3e ---- /dev/null -+++ b/src/luks/tests/pass-tang-luks1 -@@ -0,0 +1,75 @@ -+#!/bin/bash -x -+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: -+# -+# Copyright (c) 2019 Red Hat, Inc. -+# Author: Sergio Correia -+# -+# 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 . -+# -+ -+TEST="${0}" -+. tests-common-functions -+ -+function on_exit() { -+ if [ "$PID" ]; then kill $PID; wait $PID || true; fi -+ [ -d "$TMP" ] && rm -rf $TMP -+} -+ -+trap 'on_exit' EXIT -+trap 'exit' ERR -+ -+export TMP=$(mktemp -d) -+mkdir -p "${TMP}/db" -+ -+# Generate the server keys -+KEYS="$TMP/db" -+tangd-keygen $TMP/db sig exc -+if which tangd-update; then -+ mkdir -p "${TMP}/cache" -+ tangd-update "${TMP}/db" "${TMP}/cache" -+ KEYS="${TMP}/cache" -+fi -+ -+# Start the server. -+port=$(shuf -i 1024-65536 -n 1) -+"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" & -+export PID=$! -+sleep 0.25 -+ -+url="http://localhost:${port}" -+adv="${TMP}/adv" -+curl "${url}/adv" -o "${adv}" -+ -+cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv") -+ -+# LUKS1. -+DEV="${TMP}/luks1-device" -+new_device "luks1" "${DEV}" -+ -+if ! clevis luks bind -f -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then -+ error "${TEST}: Bind should have succeeded." -+fi -+ -+#Now let's test the passphrase. -+SLT=1 -+PASS=$(clevis luks pass -d "${DEV}" -s "${SLT}") -+echo $PASS >&2 -+if ! cryptsetup luksOpen --test-passphrase ""${DEV} \ -+ --key-file <(clevis luks pass -d "${DEV}" -s "${SLT}"); then -+ error "Passphrase obtained from clevis luks pass failed." -+fi -+ -+kill -9 "${PID}" -+! wait "${PID}" -+unset PID -diff --git a/src/luks/tests/pass-tang-luks2 b/src/luks/tests/pass-tang-luks2 -new file mode 100755 -index 0000000..9123aa0 ---- /dev/null -+++ b/src/luks/tests/pass-tang-luks2 -@@ -0,0 +1,75 @@ -+#!/bin/bash -x -+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: -+# -+# Copyright (c) 2019 Red Hat, Inc. -+# Author: Sergio Correia -+# -+# 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 . -+# -+ -+TEST="${0}" -+. tests-common-functions -+ -+function on_exit() { -+ if [ "$PID" ]; then kill $PID; wait $PID || true; fi -+ [ -d "$TMP" ] && rm -rf $TMP -+} -+ -+trap 'on_exit' EXIT -+trap 'exit' ERR -+ -+export TMP=$(mktemp -d) -+mkdir -p "${TMP}/db" -+ -+# Generate the server keys -+KEYS="$TMP/db" -+tangd-keygen $TMP/db sig exc -+if which tangd-update; then -+ mkdir -p "${TMP}/cache" -+ tangd-update "${TMP}/db" "${TMP}/cache" -+ KEYS="${TMP}/cache" -+fi -+ -+# Start the server. -+port=$(shuf -i 1024-65536 -n 1) -+"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" & -+export PID=$! -+sleep 0.25 -+ -+url="http://localhost:${port}" -+adv="${TMP}/adv" -+curl "${url}/adv" -o "${adv}" -+ -+cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv") -+ -+# LUKS2. -+DEV="${TMP}/luks2-device" -+new_device "luks2" "${DEV}" -+ -+if ! clevis luks bind -f -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then -+ error "${TEST}: Bind should have succeeded." -+fi -+ -+#Now let's test the passphrase. -+SLT=1 -+PASS=$(clevis luks pass -d "${DEV}" -s "${SLT}") -+echo $PASS >&2 -+if ! cryptsetup luksOpen --test-passphrase ""${DEV} \ -+ --key-file <(clevis luks pass -d "${DEV}" -s "${SLT}"); then -+ error "Passphrase obtained from clevis luks pass failed." -+fi -+ -+kill -9 "${PID}" -+! wait "${PID}" -+unset PID --- -2.18.1 - diff --git a/SOURCES/Adjust-pin-tang-test-to-account-for-newer-tang-witho.patch b/SOURCES/Adjust-pin-tang-test-to-account-for-newer-tang-witho.patch deleted file mode 100644 index 20e6137..0000000 --- a/SOURCES/Adjust-pin-tang-test-to-account-for-newer-tang-witho.patch +++ /dev/null @@ -1,77 +0,0 @@ -From 53ecfcf6d934206e3daef4ed3515a0d6f098e276 Mon Sep 17 00:00:00 2001 -From: Sergio Correia -Date: Wed, 16 Oct 2019 11:40:33 -0300 -Subject: [PATCH 1/2] Adjust pin-tang test to account for newer tang without - tangd-update - ---- - src/pins/tang/meson.build | 9 ++------- - src/pins/tang/pin-tang | 11 ++++++++--- - 2 files changed, 10 insertions(+), 10 deletions(-) - -diff --git a/src/pins/tang/meson.build b/src/pins/tang/meson.build -index 110d72d..061a79f 100644 ---- a/src/pins/tang/meson.build -+++ b/src/pins/tang/meson.build -@@ -8,11 +8,6 @@ kgen = find_program( - '/usr/libexec/tangd-keygen', - required: false - ) --updt = find_program( -- join_paths(libexecdir, 'tangd-update'), -- '/usr/libexec/tangd-update', -- required: false --) - tang = find_program( - join_paths(libexecdir, 'tangd'), - '/usr/libexec/tangd', -@@ -25,7 +20,7 @@ if curl.found() - bins += join_paths(meson.current_source_dir(), 'clevis-encrypt-tang') - mans += join_paths(meson.current_source_dir(), 'clevis-encrypt-tang.1') - -- if actv.found() and kgen.found() and updt.found() and tang.found() -+ if actv.found() and kgen.found() and tang.found() - env = environment() - env.set('SD_ACTIVATE', actv.path()) - env.append('PATH', -@@ -42,4 +37,4 @@ if curl.found() - endif - else - warning('Will not install tang pin due to missing dependencies!') --endif -\ No newline at end of file -+endif -diff --git a/src/pins/tang/pin-tang b/src/pins/tang/pin-tang -index f420818..9dcc2da 100755 ---- a/src/pins/tang/pin-tang -+++ b/src/pins/tang/pin-tang -@@ -31,18 +31,23 @@ mkdir -p $TMP/db - mkdir -p $TMP/cache - - # Generate the server keys -+KEYS=$TMP/db - tangd-keygen $TMP/db sig exc --tangd-update $TMP/db $TMP/cache -+if which tangd-update; then -+ tangd-update $TMP/db $TMP/cache -+ KEYS=$TMP/cache -+fi - - # Start the server - port=`shuf -i 1024-65536 -n 1` --$SD_ACTIVATE --inetd -l 127.0.0.1:$port -a tangd $TMP/cache & -+$SD_ACTIVATE --inetd -l 127.0.0.1:$port -a tangd $KEYS & - export PID=$! - sleep 0.25 - - thp=`jose jwk thp -i "$TMP/db/sig.jwk"` --adv="$TMP/cache/default.jws" - url="http://localhost:${port}" -+adv="$TMP/adv" -+curl "$url/adv" -o $adv - - cfg=`printf '{"url":"%s","adv":"%s"}' "$url" "$adv"` - enc=`echo -n "hi" | clevis encrypt tang "$cfg"` --- -2.21.0 - diff --git a/SOURCES/Check-key-derivation-key-is-available.patch b/SOURCES/Check-key-derivation-key-is-available.patch deleted file mode 100644 index bc1be6f..0000000 --- a/SOURCES/Check-key-derivation-key-is-available.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 8b707e8bfcbfd073579ee553b982b4784490f5ea Mon Sep 17 00:00:00 2001 -From: Daniel Kopecek -Date: Wed, 5 Dec 2018 13:18:59 +0100 -Subject: [PATCH] clevis-encrypt-tang: check key derivation key is available - before encryption - ---- - src/pins/tang/clevis-encrypt-tang | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/src/pins/tang/clevis-encrypt-tang b/src/pins/tang/clevis-encrypt-tang -index e65a7d1..7fc55ca 100755 ---- a/src/pins/tang/clevis-encrypt-tang -+++ b/src/pins/tang/clevis-encrypt-tang -@@ -114,7 +114,11 @@ elif [ "$thp" != "any" ] && \ - fi - - ### Perform encryption --enc=`jose jwk use -i- -r -u deriveKey -o- <<< "$jwks"` -+if ! enc=`jose jwk use -i- -r -u deriveKey -o- <<< "$jwks"`; then -+ echo "Key derivation key not available!" >&2 -+ exit 1 -+fi -+ - jose fmt -j "$enc" -Og keys -A || enc="{\"keys\":[$enc]}" - - for jwk in `jose fmt -j- -Og keys -Af- <<< "$enc"`; do --- -2.13.6 - diff --git a/SOURCES/Delete-remaining-references-to-the-removed-http-pin.patch b/SOURCES/Delete-remaining-references-to-the-removed-http-pin.patch deleted file mode 100644 index 36f1361..0000000 --- a/SOURCES/Delete-remaining-references-to-the-removed-http-pin.patch +++ /dev/null @@ -1,132 +0,0 @@ -From 1e344dbf6a60fcd2c60a4b8512be455e112d8398 Mon Sep 17 00:00:00 2001 -From: Javier Martinez Canillas -Date: Wed, 7 Nov 2018 14:53:08 +0100 -Subject: [PATCH 1/3] Delete remaining references to the removed http pin - -Commit 800d73185d7f ("Remove HTTP pin") removed the clevis http pin, but -there are still references of it in the docs and also the dracut module. - -This was causing dracut to fail building the initramfs due the following: - -dracut-install: ERROR: installing 'clevis-decrypt-http' - -Suggested-by: Dominick Grift - -Fixes: #73 ---- - README.md | 21 --------------------- - src/clevis.1.adoc | 21 --------------------- - src/luks/clevis-luks-bind.1.adoc | 1 - - src/luks/systemd/dracut/module-setup.sh.in | 1 - - src/pins/sss/clevis-encrypt-sss.1.adoc | 1 - - 5 files changed, 45 deletions(-) - -diff --git a/README.md b/README.md -index ce8def12ec96..d57339aca5d9 100644 ---- a/README.md -+++ b/README.md -@@ -58,27 +58,6 @@ advertisement is stored, or the JSON contents of the advertisment itself. When - the advertisment is specified manually like this, Clevis presumes that the - advertisement is trusted. - --#### PIN: HTTP -- --Clevis also ships a pin for performing escrow using HTTP. Please note that, --at this time, this pin does not provide HTTPS support and is suitable only --for use over local sockets. This provides integration with services like --[Custodia](http://github.com/latchset/custodia). -- --For example: -- --```bash --$ echo hi | clevis encrypt http '{"url": "http://server.local/key"}' > hi.jwe --``` -- --The HTTP pin generate a new (cryptographically-strong random) key and performs --encryption using it. It then performs a PUT request to the URL specified. It is --understood that the server will securely store this key for later retrieval. --During decryption, the pin will perform a GET request to retrieve the key and --perform decryption. -- --Patches to provide support for HTTPS and authentication are welcome. -- - #### PIN: TPM2 - - Clevis provides support to encrypt a key in a Trusted Platform Module 2.0 (TPM2) -diff --git a/src/clevis.1.adoc b/src/clevis.1.adoc -index 756aba57a4c8..dea0a696f5f7 100644 ---- a/src/clevis.1.adoc -+++ b/src/clevis.1.adoc -@@ -21,26 +21,6 @@ take a policy as its first argument and plaintext on standard input and to - encrypt the data so that it can be automatically decrypted if the policy is - met. Lets walk through an example. - --== HTTP ESCROW -- --When using the HTTP pin, we create a new, cryptographically-strong, random key. --This key is stored in a remote HTTP escrow server (using a simple PUT or POST). --Then at decryption time, we attempt to fetch the key back again in order to --decrypt our data. So, for our configuration we need to pass the URL to the key --location: -- -- $ clevis encrypt http '{"url":"https://escrow.srv/1234"}' < PT > JWE -- --To decrypt the data, simply provide the ciphertext (JWE): -- -- $ clevis decrypt < JWE > PLAINTEXT -- --Notice that we did not pass any configuration during decryption. The decrypt --command extracted the URL (and possibly other configuration) from the JWE --object, fetched the encryption key from the escrow and performed decryption. -- --For more information, see link:clevis-encrypt-http.1.adoc[*clevis-encrypt-http*(1)]. -- - == TANG BINDING - - Clevis provides support for the Tang network binding server. Tang provides -@@ -136,7 +116,6 @@ For more information, see link:clevis-luks-bind.1.adoc[*clevis-luks-bind*(1)]. - - == SEE ALSO - --link:clevis-encrypt-http.1.adoc[*clevis-encrypt-http*(1)], - link:clevis-encrypt-tang.1.adoc[*clevis-encrypt-tang*(1)], - link:clevis-encrypt-tpm2.1.adoc[*clevis-encrypt-tpm2*(1)], - link:clevis-encrypt-sss.1.adoc[*clevis-encrypt-sss*(1)], -diff --git a/src/luks/clevis-luks-bind.1.adoc b/src/luks/clevis-luks-bind.1.adoc -index 9f3a880cfb0c..0d649e3ec28b 100644 ---- a/src/luks/clevis-luks-bind.1.adoc -+++ b/src/luks/clevis-luks-bind.1.adoc -@@ -61,7 +61,6 @@ The images cannot be shared without also sharing a master key. - == SEE ALSO - - link:clevis-luks-unlockers.7.adoc[*clevis-luks-unlockers*(7)], --link:clevis-encrypt-http.1.adoc[*clevis-encrypt-http*(1)], - link:clevis-encrypt-tang.1.adoc[*clevis-encrypt-tang*(1)], - link:clevis-encrypt-sss.1.adoc[*clevis-encrypt-sss*(1)], - link:clevis-decrypt.1.adoc[*clevis-decrypt*(1)] -diff --git a/src/luks/systemd/dracut/module-setup.sh.in b/src/luks/systemd/dracut/module-setup.sh.in -index 119762e38326..48aea5b3f29a 100755 ---- a/src/luks/systemd/dracut/module-setup.sh.in -+++ b/src/luks/systemd/dracut/module-setup.sh.in -@@ -36,7 +36,6 @@ install() { - inst_hook initqueue/settled 60 "$moddir/clevis-hook.sh" - - inst_multiple /etc/services \ -- clevis-decrypt-http \ - clevis-decrypt-tang \ - clevis-decrypt-sss \ - @libexecdir@/clevis-luks-askpass \ -diff --git a/src/pins/sss/clevis-encrypt-sss.1.adoc b/src/pins/sss/clevis-encrypt-sss.1.adoc -index d46498db328c..7144e7e9ea96 100644 ---- a/src/pins/sss/clevis-encrypt-sss.1.adoc -+++ b/src/pins/sss/clevis-encrypt-sss.1.adoc -@@ -54,6 +54,5 @@ receive key fragments. - - == SEE ALSO - --link:clevis-encrypt-http.1.adoc[*clevis-encrypt-http*(1)], - link:clevis-encrypt-tang.1.adoc[*clevis-encrypt-tang*(1)], - link:clevis-decrypt.1.adoc[*clevis-decrypt*(1)] --- -2.19.1 - diff --git a/SOURCES/Improve-clevis-luks-regen-no-unbind-in-every-case.patch b/SOURCES/Improve-clevis-luks-regen-no-unbind-in-every-case.patch deleted file mode 100644 index b905ada..0000000 --- a/SOURCES/Improve-clevis-luks-regen-no-unbind-in-every-case.patch +++ /dev/null @@ -1,1262 +0,0 @@ -From 5536f15b9235cb6ae1b79a5ad1d96a8ea97b3113 Mon Sep 17 00:00:00 2001 -From: Sergio Correia -Date: Wed, 29 Jan 2020 06:29:32 -0500 -Subject: [PATCH] Improve clevis luks regen; no unbind in every case - -When updating the metadata -- likely due to a tang key rotation --, -clevis will not do unbind + bind in every case. - -Now we have 2 cases to be handled: -1) we have the key for the slot being rotated; in this case, the - rotation happens in-place - -2) we don't have the key for the slot being rotated; in this case, - we have to re-add the keyslot with updated info. - -Added also mechanisms for backup + restore of the LUKS header/slots, -so that we can revert back to the original state if clevis luks regen -fails. ---- - src/luks/clevis-luks-common-functions | 202 ++++++++++++++++++++++ - src/luks/clevis-luks-pass | 5 +- - src/luks/clevis-luks-regen | 223 ++++++++++++------------- - src/luks/tests/backup-restore-luks1 | 114 +++++++++++++ - src/luks/tests/backup-restore-luks2 | 115 +++++++++++++ - src/luks/tests/meson.build | 7 + - src/luks/tests/regen-inplace-luks1 | 98 +++++++++++ - src/luks/tests/regen-inplace-luks2 | 99 +++++++++++ - src/luks/tests/regen-not-inplace-luks1 | 95 +++++++++++ - src/luks/tests/regen-not-inplace-luks2 | 96 +++++++++++ - src/luks/tests/tests-common-functions | 27 ++- - 11 files changed, 966 insertions(+), 115 deletions(-) - create mode 100755 src/luks/tests/backup-restore-luks1 - create mode 100755 src/luks/tests/backup-restore-luks2 - create mode 100755 src/luks/tests/regen-inplace-luks1 - create mode 100755 src/luks/tests/regen-inplace-luks2 - create mode 100755 src/luks/tests/regen-not-inplace-luks1 - create mode 100755 src/luks/tests/regen-not-inplace-luks2 - -diff --git a/src/luks/clevis-luks-common-functions b/src/luks/clevis-luks-common-functions -index 9ba1812..2a1af26 100644 ---- a/src/luks/clevis-luks-common-functions -+++ b/src/luks/clevis-luks-common-functions -@@ -314,3 +314,205 @@ clevis_luks_read_pins_from_slot() { - fi - printf "%s: %s\n" "${SLOT}" "${cfg}" - } -+ -+# clevis_luks1_save_slot() works with LUKS1 devices and it saves a given JWE -+# to a specific device and slot. The last parameter indicates whether we -+# should overwrite existing metadata. -+clevis_luks1_save_slot() { -+ local DEV="${1}" -+ local SLOT="${2}" -+ local JWE="${3}" -+ local OVERWRITE="${4}" -+ -+ ! luksmeta test -d "${DEV}" && return 1 -+ -+ local UUID="cb6e8904-81ff-40da-a84a-07ab9ab5715e" -+ if luksmeta load -d "${DEV}" -s "${SLOT}" -u "${UUID}" >/dev/null 2>/dev/null; then -+ [ -z "${OVERWRITE}" ] && return 1 -+ if ! luksmeta wipe -f -d "${DEV}" -s "${SLOT}" -u "${UUID}"; then -+ echo "Error wiping slot ${SLOT} from ${DEV}" >&2 -+ return 1 -+ fi -+ fi -+ -+ if ! echo -n "${jwe}" | luksmeta save -d "${DEV}" -s "${SLOT}" -u "${UUID}"; then -+ echo "Error saving metadata to LUKSMeta slot ${SLOT} from ${DEV}" >&2 -+ return 1 -+ fi -+ return 0 -+} -+ -+# clevis_luks2_save_slot() works with LUKS2 devices and it saves a given JWE -+# to a specific device and slot. The last parameter indicates whether we -+# should overwrite existing metadata. -+clevis_luks2_save_slot() { -+ local DEV="${1}" -+ local SLOT="${2}" -+ local JWE="${3}" -+ local OVERWRITE="${4}" -+ -+ local dump token -+ dump="$(cryptsetup luksDump "${DEV}")" -+ if ! token="$(grep -E -B1 "^\s+Keyslot:\s+${SLOT}$" <<< "${dump}" \ -+ | sed -rn 's|^\s+([0-9]+): clevis|\1|p')"; then -+ echo "Error trying to read token from LUKS2 device ${DEV}, slot ${SLOT}" >&2 -+ return 1 -+ fi -+ -+ if [ -n "${token}" ]; then -+ [ -z "${OVERWRITE}" ] && return 1 -+ if ! cryptsetup token remove --token-id "${token}" "${DEV}"; then -+ echo "Error while removing token ${token} from LUKS2 device ${DEV}" >&2 -+ return 1 -+ fi -+ fi -+ -+ local metadata -+ metadata=$(printf '{"type":"clevis","keyslots":["%s"],"jwe":%s}' \ -+ "${SLOT}" "$(jose jwe fmt -i- <<< "${JWE}")") -+ if ! cryptsetup token import "${DEV}" <<< "${metadata}"; then -+ echo "Error saving metadata to LUKS2 header in device ${DEV}" >&2 -+ return 1 -+ fi -+ return 0 -+} -+ -+# clevis_luks_save_slot() saves a given JWE to a LUKS device+slot. It can also -+# overwrite existing metadata. -+clevis_luks_save_slot() { -+ local DEV="${1}" -+ local SLOT="${2}" -+ local JWE="${3}" -+ local OVERWRITE="${4}" -+ -+ if cryptsetup isLuks --type luks1 "${DEV}"; then -+ ! clevis_luks1_save_slot "${DEV}" "${SLOT}" "${JWE}" "${OVERWRITE}" \ -+ && return 1 -+ elif cryptsetup isLuks --type luks2 "${DEV}"; then -+ ! clevis_luks2_save_slot "${DEV}" "${SLOT}" "${JWE}" "${OVERWRITE}" \ -+ && return 1 -+ else -+ return 1 -+ fi -+ return 0 -+} -+ -+# clevis_luks1_backup_dev() backups the LUKSMeta slots from a LUKS device, -+# which can be restored with clevis_luks1_restore_dev(). -+clevis_luks1_backup_dev() { -+ local DEV="${1}" -+ local TMP="${2}" -+ -+ [ -z "${DEV}" ] && return 1 -+ [ -z "${TMP}" ] && return 1 -+ -+ local slots slt uuid jwe fname -+ readarray -t slots < <(cryptsetup luksDump "${DEV}" \ -+ | sed -rn 's|^Key Slot ([0-7]): ENABLED$|\1|p') -+ -+ for slt in "${slots[@]}"; do -+ if ! uuid=$(luksmeta show -d "${DEV}" -s "${slt}") \ -+ || [ -z "${uuid}" ]; then -+ continue -+ fi -+ if ! jwe=$(luksmeta load -d "${DEV}" -s "${slt}") \ -+ || [ -z "${jwe}" ]; then -+ continue -+ fi -+ -+ fname=$(printf "slot_%s_%s" "${slt}" "${uuid}") -+ printf "%s" "${jwe}" > "${TMP}/${fname}" -+ done -+ return 0 -+} -+ -+# clevis_luks1_restore_dev() takes care of restoring the LUKSMeta slots from -+# a LUKS device that was backup'ed by clevis_luks1_backup_dev(). -+clevis_luks1_restore_dev() { -+ local DEV="${1}" -+ local TMP="${2}" -+ -+ [ -z "${DEV}" ] && return 1 -+ [ -z "${TMP}" ] && return 1 -+ -+ local slt uuid jwe fname -+ for fname in "${TMP}"/slot_*; do -+ [ -f "${fname}" ] || break -+ if ! slt=$(echo "${fname}" | cut -d '_' -f 2) \ -+ || [ -z "${slt}" ]; then -+ continue -+ fi -+ if ! uuid=$(echo "${fname}" | cut -d '_' -f 3) \ -+ || [ -z "${uuid}" ]; then -+ continue -+ fi -+ if ! jwe=$(< "${fname}") || [ -z "${jwe}" ]; then -+ continue -+ fi -+ if ! clevis_luks1_save_slot "${DEV}" "${slt}" \ -+ "${jwe}" "overwrite"; then -+ echo "Error restoring LUKSmeta slot ${slt} from ${DEV}" >&2 -+ return 1 -+ fi -+ done -+ return 0 -+} -+ -+# clevis_luks_backup_dev() backups a particular LUKS device, which can then -+# be restored with clevis_luks_restore_dev(). -+clevis_luks_backup_dev() { -+ local DEV="${1}" -+ local TMP="${2}" -+ -+ [ -z "${DEV}" ] && return 1 -+ [ -z "${TMP}" ] && return 1 -+ -+ local HDR="${TMP}/$(basename "${DEV}").header" -+ if ! cryptsetup luksHeaderBackup "${DEV}" --batch-mode \ -+ --header-backup-file "${HDR}"; then -+ echo "Error backing up LUKS header from ${DEV}" >&2 -+ return 1 -+ fi -+ -+ # If LUKS1, we need to manually back up (and later restore) the -+ # LUKSmeta slots. For LUKS2, simply saving the header also saves -+ # the associated tokens. -+ if cryptsetup isLuks --type luks1 "${DEV}"; then -+ if ! clevis_luks1_backup_dev "${DEV}" "${TMP}"; then -+ return 1 -+ fi -+ fi -+ return 0 -+} -+ -+# clevis_luks_restore_dev() restores a given device that was backup'ed by -+# clevis_luks_backup_dev(). -+clevis_luks_restore_dev() { -+ local DEV="${1}" -+ local TMP="${2}" -+ -+ [ -z "${DEV}" ] && return 1 -+ [ -z "${TMP}" ] && return 1 -+ -+ local HDR="${TMP}/$(basename "${DEV}").header" -+ if [ ! -e "${HDR}" ]; then -+ echo "LUKS header backup does not exist" >&2 -+ return 1 -+ fi -+ -+ if ! cryptsetup luksHeaderRestore "${DEV}" --batch-mode \ -+ --header-backup-file "${HDR}"; then -+ echo "Error restoring LUKS header from ${DEV}" >&2 -+ return 1 -+ fi -+ -+ # If LUKS1, we need to manually back up (and later restore) the -+ # LUKSmeta slots. For LUKS2, simply saving the header also saves -+ # the associated tokens. -+ if cryptsetup isLuks --type luks1 "${DEV}"; then -+ if ! clevis_luks1_restore_dev "${DEV}" "${TMP}"; then -+ return 1 -+ fi -+ fi -+ return 0 -+} -diff --git a/src/luks/clevis-luks-pass b/src/luks/clevis-luks-pass -index 1ce8c4c..d31bc17 100755 ---- a/src/luks/clevis-luks-pass -+++ b/src/luks/clevis-luks-pass -@@ -63,7 +63,8 @@ if ! jwe=$(clevis_luks_read_slot "${DEV}" "${SLT}" 2>/dev/null); then - exit 1 - fi - --if ! clevis decrypt < <(echo -n "${jwe}"); then -- echo "It was not possible to decrypt the passphrase associated to slot ${SLT} in {DEV}!" >&2 -+if ! passphrase=$(clevis decrypt < <(echo -n "${jwe}") 2>/dev/null); then -+ echo "It was not possible to decrypt the passphrase associated to slot ${SLT} in ${DEV}!" >&2 - exit 1 - fi -+echo -n "${passphrase}" -diff --git a/src/luks/clevis-luks-regen b/src/luks/clevis-luks-regen -index 9535ba3..44fd673 100755 ---- a/src/luks/clevis-luks-regen -+++ b/src/luks/clevis-luks-regen -@@ -1,8 +1,9 @@ --#!/usr/bin/env bash -+#!/usr/bin/bash - # vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: - # - # Copyright (c) 2018 Red Hat, Inc. - # Author: Radovan Sroka -+# Author: Sergio Correia - # - # 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 -@@ -28,19 +29,27 @@ if [ "$1" == "--summary" ]; then - fi - - function usage_and_exit () { -- echo >&2 -- echo "Usage: clevis luks regen -d DEV -s SLOT" >&2 -- echo >&2 -- echo "$SUMMARY" >&2 -- echo >&2 -- exit "$1" -+ exec >&2 -+ echo "Usage: clevis luks regen -d DEV -s SLOT" -+ echo -+ echo "$SUMMARY" -+ echo -+ echo " -d DEV The LUKS device on which to perform rebinding" -+ echo -+ echo " -s SLT The LUKS slot to use" -+ echo -+ exit "${1}" - } - --if [ "$#" -ne "4" ]; then -- usage_and_exit 1 --fi -+on_exit() { -+ if [ ! -d "${TMP}" ] || ! rm -rf "${TMP}"; then -+ echo "Delete temporary files failed!" >&2 -+ echo "You need to clean up: ${TMP}" >&2 -+ exit 1 -+ fi -+} - --while getopts "hd:s:" o; do -+while getopts ":hfd:s:" o; do - case "$o" in - d) DEV="$OPTARG";; - h) usage_and_exit 0;; -@@ -49,88 +58,6 @@ while getopts "hd:s:" o; do - esac - done - --function decode_luks_header () { -- if DATA_CODED="$(jose jwe fmt -i- <<< "$1")"; then -- DATA_CODED="$(jose fmt -j- -g protected -u- <<< "$DATA_CODED")" -- DATA_DECODED="$(jose b64 dec -i- <<< "$DATA_CODED")" -- else -- echo "Error decoding JWE protected header!" >&2 -- exit 1 -- fi -- -- echo "$DATA_DECODED" --} -- --function generate_cfg () { -- echo -n "{" -- DATA="$(decode_luks_header "$1")" -- -- if ! P="$(jose fmt -j- -g clevis -g pin -u- <<< "$DATA")" || [ -z "$P" ]; then -- echo "Pin wasn't found in LUKS metadata!" >&2 -- exit 1 -- fi -- -- if ! CONTENT="$(jose fmt -j- -g clevis -g "$P" -o- <<< "$DATA")" || [ -z "$CONTENT" ]; then -- echo "Content was not found!" >&2 -- fi -- -- # echo -n "\"$P\": [" -- -- if [ "$P" = "tang" ] || [ "$P" = "http" ]; then -- URL="$(jose fmt -j- -g url -u- <<< "$CONTENT")" -- echo -n "\"url\":\"$URL\"" -- elif [ "$P" = "sss" ]; then -- THRESHOLD="$(jose fmt -j- -g t -o- <<< "$CONTENT")" -- if [ -n "$THRESHOLD" ]; then -- echo -n "\"t\":$THRESHOLD," -- fi -- -- echo -n "\"pins\":{" -- -- CNT=0 -- PREV="" -- while ITEM="$(jose fmt -j- -g jwe -g"$CNT" -u- <<< "$CONTENT")"; do -- if [ -z "$ITEM" ]; then -- CNT=$(( CNT + 1 )) -- continue # in some cases it can be empty string -- fi -- -- DD="$(decode_luks_header "$ITEM")" -- -- if ! PP="$(jose fmt -j- -g clevis -g pin -u- <<< "$DD")" || [ -z "$PP" ]; then -- echo "Pin wasn't found in LUKS metadata!" >&2 -- exit 1 -- fi -- -- if [ "$CNT" -eq 0 ]; then -- PREV="$PP" -- echo -n "\"$PP\":[" -- echo -n "$(generate_cfg "$ITEM")" -- else -- if ! [ "$PREV" = "$PP" ]; then -- echo -n "],\"$PP\":[" -- echo -n "$(generate_cfg "$ITEM")" -- else -- echo -n ",$(generate_cfg "$ITEM")" -- fi -- fi -- -- PREV="$PP" -- CNT=$(( CNT + 1 )) -- done -- -- echo -n "]}" -- -- else -- echo "Unknown pin $P!" >&2 -- exit 1 -- fi -- -- echo -n "}" --} -- --### get luks metadata -- - if [ -z "$DEV" ]; then - echo "Did not specify a device!" >&2 - exit 1 -@@ -141,23 +68,14 @@ if [ -z "$SLT" ]; then - exit 1 - fi - --if ! OLD_LUKS_CODED="$(clevis_luks_read_slot "$DEV" "$SLT")"; then -- echo "Error reading metadata from LUKS device!" >&2 -- exit 1 --fi -- - ### ---------------------------------------------------------------------- -- --DECODED="$(decode_luks_header "$OLD_LUKS_CODED")" -- --if ! PIN="$(jose fmt -j- -g clevis -g pin -u- <<< "$DECODED")" || [ -z "$PIN" ]; then -- echo "Pin wasn't found in LUKS metadata!" >&2 -+if ! pin_cfg=$(clevis luks list -d "${DEV}" -s "${SLT}" 2>/dev/null); then -+ echo "Error obtaining current configuration of device ${DEV}:${SLT}" >&2 - exit 1 - fi - --CFG="$(generate_cfg "$OLD_LUKS_CODED")" -- --### ---------------------------------------------------------------------- -+PIN=$(echo "${pin_cfg}" | awk '{ print $2 }') -+CFG=$(echo "${pin_cfg}" | awk '{ print $3 }' | tr -d "'") - - echo "Regenerating with:" - echo "PIN: $PIN" -@@ -166,20 +84,101 @@ echo "CONFIG: $CFG" - trap 'echo "Ignoring CONTROL-C!"' INT TERM - - # Get the existing key. --read -r -s -p "Enter existing LUKS password: " existing_key; echo -+if ! existing_key=$(clevis luks pass -d "${DEV}" -s "${SLT}" 2>/dev/null); then -+ # We failed to obtain the passphrase for the slot -- perhaps -+ # it was rotated? --, so let's request user input. -+ read -r -s -p "Enter existing LUKS password: " existing_key; echo -+fi - - # Check if the key is valid. --if ! cryptsetup luksOpen --test-passphrase "${DEV}" <<< "${existing_key}"; then -+if ! cryptsetup open --test-passphrase "${DEV}" <<< "${existing_key}"; then -+ exit 1 -+fi -+ -+# Check if we can do the update in-place, i.e., if the key we got is the one -+# for the slot we are interested in. -+in_place= -+if cryptsetup open --test-passphrase --key-slot "${SLT}" "${DEV}" \ -+ <<< "${existing_key}"; then -+ in_place=true -+fi -+ -+# Create new key. -+if ! new_passphrase=$(generate_key "${DEV}"); then -+ echo "Error generating new key for device ${DEV}" >&2 -+ exit 1 -+fi -+ -+# Reencrypt the new password. -+if ! jwe=$(clevis encrypt "${PIN}" "${CFG}" <<< "${new_passphrase}"); then -+ echo "Error using pin '${PIN}' with config '${CFG}'" >&2 -+ exit 1 -+fi -+ -+# Updating the metadata and the actual passphrase are destructive operations, -+# hence we will do a backup of the LUKS header and restore it later in case -+# we have issues performing these operations. -+if ! TMP="$(mktemp -d)"; then -+ echo "Creating a temporary dir for device backup/restore failed!" >&2 -+ exit 1 -+fi -+trap 'on_exit' EXIT -+ -+# Backup LUKS header. -+if ! clevis_luks_backup_dev "${DEV}" "${TMP}"; then -+ echo "Error while trying to back up LUKS header from ${DEV}" >&2 - exit 1 - fi - --if ! clevis luks unbind -d "${DEV}" -s "${SLT}" -f; then -- echo "Error during unbind of rotated key from slot:$SLT in $DEV" >&2 -+restore_device() { -+ local DEV="${1}" -+ local TMP="${2}" -+ -+ if ! clevis_luks_restore_dev "${DEV}" "${TMP}"; then -+ echo "Error while trying to restore LUKS header from ${DEV}." >&2 -+ else -+ echo "LUKS header restored successfully." >&2 -+ fi -+} -+ -+# Update the key slot with the new key. If we have the key for this slot, -+# the change happens in-place. Otherwise, we kill the slot and re-add it. -+if [ -n "${in_place}" ]; then -+ if ! cryptsetup luksChangeKey "${DEV}" --key-slot "${SLT}" \ -+ <(echo -n "${new_passphrase}") <<< "${existing_key}"; then -+ echo "Error updating LUKS passphrase in ${DEV}:${SLT}" >&2 -+ restore_device "${DEV}" "${TMP}" -+ exit 1 -+ fi -+else -+ if ! cryptsetup luksKillSlot --batch-mode "${DEV}" "${SLT}"; then -+ echo "Error wiping slot ${SLT} from ${DEV}" >&2 -+ restore_device "${DEV}" "${TMP}" -+ exit 1 -+ fi -+ -+ if ! echo -n "${new_passphrase}" \ -+ | cryptsetup luksAddKey --key-slot "${SLT}" \ -+ --key-file <(echo -n "${existing_key}") "${DEV}"; then -+ echo "Error updating LUKS passphrase in ${DEV}:${SLT}." >&2 -+ restore_device "${DEV}" "${TMP}" -+ exit 1 -+ fi -+fi -+ -+# Update the metadata. -+if ! clevis_luks_save_slot "${DEV}" "${SLT}" "${jwe}" "overwrite"; then -+ echo "Error updating metadata in ${DEV}:${SLT}" >&2 -+ restore_device "${DEV}" "${TMP}" - exit 1 - fi - --if ! clevis luks bind -d "${DEV}" -s "${SLT}" "${PIN}" "${CFG}" -k - <<< "${existing_key}"; then -- echo "Error during bind of new key from slot:$SLT in $DEV" >&2 -+# Now make sure that we can unlock this device after the change. -+# If we can't, undo the changes. -+if ! cryptsetup open --test-passphrase --key-slot "${SLT}" "${DEV}" 2>/dev/null \ -+ <<< $(clevis luks pass -d "${DEV}" -s "${SLT}" 2>/dev/null); then -+ echo "Invalid configuration detected after rebinding. Reverting changes." -+ restore_device "${DEV}" "${TMP}" - exit 1 - fi - -diff --git a/src/luks/tests/backup-restore-luks1 b/src/luks/tests/backup-restore-luks1 -new file mode 100755 -index 0000000..733a4b6 ---- /dev/null -+++ b/src/luks/tests/backup-restore-luks1 -@@ -0,0 +1,114 @@ -+#!/bin/bash -x -+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: -+# -+# Copyright (c) 2020 Red Hat, Inc. -+# Author: Sergio Correia -+# -+# 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 . -+# -+ -+TEST="${0}" -+. tests-common-functions -+. clevis-luks-common-functions -+ -+function on_exit() { -+ if [ "$PID" ]; then kill $PID; wait $PID || true; fi -+ [ -d "$TMP" ] && rm -rf $TMP -+} -+ -+trap 'on_exit' EXIT -+trap 'exit' ERR -+ -+export TMP=$(mktemp -d) -+mkdir -p "${TMP}/db" -+ -+# Generate the server keys -+KEYS="$TMP/db" -+tangd-keygen $TMP/db sig exc -+if which tangd-update; then -+ mkdir -p "${TMP}/cache" -+ tangd-update "${TMP}/db" "${TMP}/cache" -+ KEYS="${TMP}/cache" -+fi -+ -+# Start the server. -+port=$(shuf -i 1024-65536 -n 1) -+"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" & -+export PID=$! -+sleep 0.25 -+ -+url="http://localhost:${port}" -+adv="${TMP}/adv" -+curl "${url}/adv" -o "${adv}" -+ -+cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv") -+ -+# LUKS1. -+DEV="${TMP}/luks1-device" -+new_device "luks1" "${DEV}" -+ -+SLT=5 -+if ! clevis luks bind -f -s "${SLT}" -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then -+ error "${TEST}: Bind should have succeeded." -+fi -+ -+ORIG_DEV="${TMP}/device.orig" -+# Let's save it for comparison later. -+cp -f "${DEV}" "${ORIG_DEV}" -+ -+# Now let's backup and restore. -+if ! clevis_luks_backup_dev "${DEV}" "${TMP}"; then -+ error "${TEST}: backup of ${DEV} failed." -+fi -+ -+# Now let's remove both the binding and the initial passphrase. -+if ! clevis luks unbind -f -s "${SLT}" -d "${DEV}" <<< "${DEFAULT_PASS}"; then -+ error "${TEST}: unbind of slot ${SLT} in ${DEV} failed." -+fi -+ -+if ! cryptsetup luksRemoveKey --batch-mode "${DEV}" <<< "${DEFAULT_PASS}"; then -+ error "${TEST}: error removing the default password from ${DEV}." -+fi -+ -+# Making sure we have no slots enabled. -+enabled=$(cryptsetup luksDump "${DEV}" | grep ENABLED | wc -l) -+if [ "${enabled}" -ne 0 ]; then -+ error "${TEST}: we should not have any enabled (${enabled}) slots." -+fi -+ -+# Now we can restore it. -+if ! clevis_luks_restore_dev "${DEV}" "${TMP}"; then -+ error "${TEST}: error restoring ${DEV}." -+fi -+ -+# And compare whether they are the same. -+if ! cmp --silent "${ORIG_DEV}" "${DEV}"; then -+ error "${TEST}: the device differs from the original one after the restore." -+fi -+ -+# And making sure both the default passphrase and the binding work. -+if ! cryptsetup open --test-passphrase "${DEV}" <<< "${DEFAULT_PASS}"; then -+ error "${TEST}: the default passphrase for ${DEV} did no work." -+fi -+ -+TEST_DEV="test-device-${RANDOM}" -+if ! clevis luks unlock -d "${DEV}" -n "${TEST_DEV}"; then -+ error "${TEST}: we were unable to unlock ${DEV}." -+fi -+ -+cryptsetup close "${TEST_DEV}" -+ -+kill -9 "${PID}" -+! wait "${PID}" -+unset PID -diff --git a/src/luks/tests/backup-restore-luks2 b/src/luks/tests/backup-restore-luks2 -new file mode 100755 -index 0000000..a3b8608 ---- /dev/null -+++ b/src/luks/tests/backup-restore-luks2 -@@ -0,0 +1,115 @@ -+#!/bin/bash -x -+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: -+# -+# Copyright (c) 2020 Red Hat, Inc. -+# Author: Sergio Correia -+# -+# 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 . -+# -+ -+TEST="${0}" -+. tests-common-functions -+. clevis-luks-common-functions -+ -+function on_exit() { -+ if [ "$PID" ]; then kill $PID; wait $PID || true; fi -+ [ -d "$TMP" ] && rm -rf $TMP -+} -+ -+trap 'on_exit' EXIT -+trap 'exit' ERR -+ -+export TMP=$(mktemp -d) -+mkdir -p "${TMP}/db" -+ -+# Generate the server keys -+KEYS="$TMP/db" -+tangd-keygen $TMP/db sig exc -+if which tangd-update; then -+ mkdir -p "${TMP}/cache" -+ tangd-update "${TMP}/db" "${TMP}/cache" -+ KEYS="${TMP}/cache" -+fi -+ -+# Start the server. -+port=$(shuf -i 1024-65536 -n 1) -+"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" & -+export PID=$! -+sleep 0.25 -+ -+url="http://localhost:${port}" -+adv="${TMP}/adv" -+curl "${url}/adv" -o "${adv}" -+ -+cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv") -+ -+# LUKS2. -+DEV="${TMP}/luks2-device" -+new_device "luks2" "${DEV}" -+ -+SLT=5 -+if ! clevis luks bind -f -s "${SLT}" -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then -+ error "${TEST}: Bind should have succeeded." -+fi -+ -+ORIG_DEV="${TMP}/device.orig" -+# Let's save it for comparison later. -+cp -f "${DEV}" "${ORIG_DEV}" -+ -+# Now let's backup and restore. -+if ! clevis_luks_backup_dev "${DEV}" "${TMP}"; then -+ error "${TEST}: backup of ${DEV} failed." -+fi -+ -+# Now let's remove both the binding and the initial passphrase. -+if ! clevis luks unbind -f -s "${SLT}" -d "${DEV}" <<< "${DEFAULT_PASS}"; then -+ error "${TEST}: unbind of slot ${SLT} in ${DEV} failed." -+fi -+ -+if ! cryptsetup luksRemoveKey --batch-mode "${DEV}" <<< "${DEFAULT_PASS}"; then -+ error "${TEST}: error removing the default password from ${DEV}." -+fi -+ -+# Making sure we have no slots enabled. -+enabled=$(cryptsetup luksDump "${DEV}" \ -+ | sed -rn 's|^\s+([0-9]+): luks2$|\1|p' | wc -l) -+if [ "${enabled}" -ne 0 ]; then -+ error "${TEST}: we should not have any enabled (${enabled}) slots." -+fi -+ -+# Now we can restore it. -+if ! clevis_luks_restore_dev "${DEV}" "${TMP}"; then -+ error "${TEST}: error restoring ${DEV}." -+fi -+ -+# And compare whether they are the same. -+if ! cmp --silent "${ORIG_DEV}" "${DEV}"; then -+ error "${TEST}: the device differs from the original one after the restore." -+fi -+ -+# And making sure both the default passphrase and the binding work. -+if ! cryptsetup open --test-passphrase "${DEV}" <<< "${DEFAULT_PASS}"; then -+ error "${TEST}: the default passphrase for ${DEV} did no work." -+fi -+ -+TEST_DEV="test-device-${RANDOM}" -+if ! clevis luks unlock -d "${DEV}" -n "${TEST_DEV}"; then -+ error "${TEST}: we were unable to unlock ${DEV}." -+fi -+ -+cryptsetup close "${TEST_DEV}" -+ -+kill -9 "${PID}" -+! wait "${PID}" -+unset PID -diff --git a/src/luks/tests/meson.build b/src/luks/tests/meson.build -index 248d2ea..4e0a6cb 100644 ---- a/src/luks/tests/meson.build -+++ b/src/luks/tests/meson.build -@@ -35,6 +35,9 @@ else - warning('Will not run "clevis luks list" tests due to missing jq dependency') - endif - test('pass-tang-luks1', find_program('pass-tang-luks1'), env: env) -+test('backup-restore-luks1', find_program('backup-restore-luks1'), env: env) -+test('regen-inplace-luks1', find_program('regen-inplace-luks1'), env: env, timeout: 90) -+test('regen-not-inplace-luks1', find_program('regen-not-inplace-luks1'), env: env, timeout: 90) - - # LUKS2 tests go here, and they get included if we get support for it, based - # on the cryptsetup version. -@@ -45,3 +48,7 @@ if jq.found() - test('list-sss-tang-luks2', find_program('list-sss-tang-luks2'), env: env, timeout: 60) - endif - test('pass-tang-luks2', find_program('pass-tang-luks2'), env: env, timeout: 60) -+test('backup-restore-luks2', find_program('backup-restore-luks2'), env:env, timeout: 90) -+test('regen-inplace-luks2', find_program('regen-inplace-luks2'), env: env, timeout: 90) -+test('regen-not-inplace-luks2', find_program('regen-not-inplace-luks2'), env: env, timeout: 90) -+ -diff --git a/src/luks/tests/regen-inplace-luks1 b/src/luks/tests/regen-inplace-luks1 -new file mode 100755 -index 0000000..3a42ced ---- /dev/null -+++ b/src/luks/tests/regen-inplace-luks1 -@@ -0,0 +1,98 @@ -+#!/bin/bash -x -+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: -+# -+# Copyright (c) 2020 Red Hat, Inc. -+# Author: Sergio Correia -+# -+# 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 . -+# -+ -+TEST="${0}" -+. tests-common-functions -+ -+function on_exit() { -+ if [ "$PID" ]; then kill $PID; wait $PID || true; fi -+ [ -d "$TMP" ] && rm -rf $TMP -+} -+ -+trap 'on_exit' EXIT -+trap 'exit' ERR -+ -+export TMP=$(mktemp -d) -+mkdir -p "${TMP}/db" -+ -+# Generate the server keys -+KEYS="$TMP/db" -+tangd-keygen $TMP/db sig exc -+if which tangd-update; then -+ mkdir -p "${TMP}/cache" -+ tangd-update "${TMP}/db" "${TMP}/cache" -+ KEYS="${TMP}/cache" -+fi -+ -+# Start the server. -+port=$(shuf -i 1024-65536 -n 1) -+"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" & -+export PID=$! -+sleep 0.25 -+ -+url="http://localhost:${port}" -+adv="${TMP}/adv" -+curl "${url}/adv" -o "${adv}" -+ -+cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv") -+ -+# LUKS1. -+DEV="${TMP}/luks1-device" -+new_device "luks1" "${DEV}" -+ -+SLT=1 -+if ! clevis luks bind -f -s "${SLT}" -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then -+ error "${TEST}: Bind should have succeeded." -+fi -+ -+# Now let's remove the initial passphrase. -+if ! cryptsetup luksRemoveKey --batch-mode "${DEV}" <<< "${DEFAULT_PASS}"; then -+ error "${TEST}: error removing the default password from ${DEV}." -+fi -+ -+# Making sure we have a single slot enabled. -+enabled=$(cryptsetup luksDump "${DEV}" | grep ENABLED | wc -l) -+if [ "${enabled}" -ne 1 ]; then -+ error "${TEST}: we should have only one slot enabled (${enabled})." -+fi -+ -+old_key=$(clevis luks pass -d "${DEV}" -s "${SLT}") -+ -+# Now let's try regen. -+if ! clevis_regen "${DEV}" "${SLT}" "${DEFAULT_PASS}"; then -+ error "${TEST}: clevis luks regen failed" -+fi -+ -+new_key=$(clevis luks pass -d "${DEV}" -s "${SLT}") -+ -+if [ "${old_key}" = "${new_key}" ]; then -+ error "${TEST}: the passphrases should be different" -+fi -+ -+TEST_DEV="test-device-${RANDOM}" -+if ! clevis luks unlock -d "${DEV}" -n "${TEST_DEV}"; then -+ error "${TEST}: we were unable to unlock ${DEV}." -+fi -+ -+cryptsetup close "${TEST_DEV}" -+ -+kill -9 "${PID}" -+! wait "${PID}" -+unset PID -diff --git a/src/luks/tests/regen-inplace-luks2 b/src/luks/tests/regen-inplace-luks2 -new file mode 100755 -index 0000000..1cb7a29 ---- /dev/null -+++ b/src/luks/tests/regen-inplace-luks2 -@@ -0,0 +1,99 @@ -+#!/bin/bash -x -+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: -+# -+# Copyright (c) 2020 Red Hat, Inc. -+# Author: Sergio Correia -+# -+# 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 . -+# -+ -+TEST="${0}" -+. tests-common-functions -+ -+function on_exit() { -+ if [ "$PID" ]; then kill $PID; wait $PID || true; fi -+ [ -d "$TMP" ] && rm -rf $TMP -+} -+ -+trap 'on_exit' EXIT -+trap 'exit' ERR -+ -+export TMP=$(mktemp -d) -+mkdir -p "${TMP}/db" -+ -+# Generate the server keys -+KEYS="$TMP/db" -+tangd-keygen $TMP/db sig exc -+if which tangd-update; then -+ mkdir -p "${TMP}/cache" -+ tangd-update "${TMP}/db" "${TMP}/cache" -+ KEYS="${TMP}/cache" -+fi -+ -+# Start the server. -+port=$(shuf -i 1024-65536 -n 1) -+"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" & -+export PID=$! -+sleep 0.25 -+ -+url="http://localhost:${port}" -+adv="${TMP}/adv" -+curl "${url}/adv" -o "${adv}" -+ -+cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv") -+ -+# LUKS2. -+DEV="${TMP}/luks2-device" -+new_device "luks2" "${DEV}" -+ -+SLT=1 -+if ! clevis luks bind -f -s "${SLT}" -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then -+ error "${TEST}: Bind should have succeeded." -+fi -+ -+# Now let's remove the initial passphrase. -+if ! cryptsetup luksRemoveKey --batch-mode "${DEV}" <<< "${DEFAULT_PASS}"; then -+ error "${TEST}: error removing the default password from ${DEV}." -+fi -+ -+# Making sure we have a single slot enabled. -+enabled=$(cryptsetup luksDump "${DEV}" \ -+ | sed -rn 's|^\s+([0-9]+): luks2$|\1|p' | wc -l) -+if [ "${enabled}" -ne 1 ]; then -+ error "${TEST}: we should have only one slot enabled (${enabled})." -+fi -+ -+old_key=$(clevis luks pass -d "${DEV}" -s "${SLT}") -+ -+# Now let's try regen. -+if ! clevis_regen "${DEV}" "${SLT}" "${DEFAULT_PASS}"; then -+ error "${TEST}: clevis luks regen failed" -+fi -+ -+new_key=$(clevis luks pass -d "${DEV}" -s "${SLT}") -+ -+if [ "${old_key}" = "${new_key}" ]; then -+ error "${TEST}: the passphrases should be different" -+fi -+ -+TEST_DEV="test-device-${RANDOM}" -+if ! clevis luks unlock -d "${DEV}" -n "${TEST_DEV}"; then -+ error "${TEST}: we were unable to unlock ${DEV}." -+fi -+ -+cryptsetup close "${TEST_DEV}" -+ -+kill -9 "${PID}" -+! wait "${PID}" -+unset PID -diff --git a/src/luks/tests/regen-not-inplace-luks1 b/src/luks/tests/regen-not-inplace-luks1 -new file mode 100755 -index 0000000..1b65ca7 ---- /dev/null -+++ b/src/luks/tests/regen-not-inplace-luks1 -@@ -0,0 +1,95 @@ -+#!/bin/bash -x -+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: -+# -+# Copyright (c) 2020 Red Hat, Inc. -+# Author: Sergio Correia -+# -+# 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 . -+# -+ -+TEST="${0}" -+. tests-common-functions -+ -+function on_exit() { -+ if [ "$PID" ]; then kill $PID; wait $PID || true; fi -+ [ -d "$TMP" ] && rm -rf $TMP -+} -+ -+trap 'on_exit' EXIT -+trap 'exit' ERR -+ -+export TMP=$(mktemp -d) -+mkdir -p "${TMP}/db" -+ -+# Generate the server keys -+KEYS="$TMP/db" -+tangd-keygen $TMP/db sig exc -+if which tangd-update; then -+ mkdir -p "${TMP}/cache" -+ tangd-update "${TMP}/db" "${TMP}/cache" -+ KEYS="${TMP}/cache" -+fi -+ -+# Start the server. -+port=$(shuf -i 1024-65536 -n 1) -+"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" & -+export PID=$! -+sleep 0.25 -+ -+url="http://localhost:${port}" -+adv="${TMP}/adv" -+curl "${url}/adv" -o "${adv}" -+ -+cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv") -+ -+# LUKS1. -+DEV="${TMP}/luks1-device" -+new_device "luks1" "${DEV}" -+ -+SLT=1 -+if ! clevis luks bind -f -s "${SLT}" -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then -+ error "${TEST}: Bind should have succeeded." -+fi -+ -+# Now let's rotate the keys in the server and remove the old ones, so that we -+# will be unable to unlock the volume using clevis and will have to provide -+# manually a password for clevis luks regen. -+rm -rf "${TMP}"/db/* -+tangd-keygen $TMP/db sig exc -+if which tangd-update; then -+ mkdir -p "${TMP}/cache" -+ tangd-update "${TMP}/db" "${TMP}/cache" -+fi -+ -+# Making sure we have two slots enabled. -+enabled=$(cryptsetup luksDump "${DEV}" | grep ENABLED | wc -l) -+if [ "${enabled}" -ne 2 ]; then -+ error "${TEST}: we should have two slots enabled (${enabled})." -+fi -+ -+# Now let's try regen. -+if ! clevis_regen "${DEV}" "${SLT}" "${DEFAULT_PASS}"; then -+ error "${TEST}: clevis luks regen failed" -+fi -+ -+TEST_DEV="test-device-${RANDOM}" -+if ! clevis luks unlock -d "${DEV}" -n "${TEST_DEV}"; then -+ error "${TEST}: we were unable to unlock ${DEV}." -+fi -+ -+cryptsetup close "${TEST_DEV}" -+ -+kill -9 "${PID}" -+! wait "${PID}" -+unset PID -diff --git a/src/luks/tests/regen-not-inplace-luks2 b/src/luks/tests/regen-not-inplace-luks2 -new file mode 100755 -index 0000000..dc91449 ---- /dev/null -+++ b/src/luks/tests/regen-not-inplace-luks2 -@@ -0,0 +1,96 @@ -+#!/bin/bash -x -+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: -+# -+# Copyright (c) 2020 Red Hat, Inc. -+# Author: Sergio Correia -+# -+# 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 . -+# -+ -+TEST="${0}" -+. tests-common-functions -+ -+function on_exit() { -+ if [ "$PID" ]; then kill $PID; wait $PID || true; fi -+ [ -d "$TMP" ] && rm -rf $TMP -+} -+ -+trap 'on_exit' EXIT -+trap 'exit' ERR -+ -+export TMP=$(mktemp -d) -+mkdir -p "${TMP}/db" -+ -+# Generate the server keys -+KEYS="$TMP/db" -+tangd-keygen $TMP/db sig exc -+if which tangd-update; then -+ mkdir -p "${TMP}/cache" -+ tangd-update "${TMP}/db" "${TMP}/cache" -+ KEYS="${TMP}/cache" -+fi -+ -+# Start the server. -+port=$(shuf -i 1024-65536 -n 1) -+"${SD_ACTIVATE}" --inetd -l 127.0.0.1:"${port}" -a tangd "${KEYS}" & -+export PID=$! -+sleep 0.25 -+ -+url="http://localhost:${port}" -+adv="${TMP}/adv" -+curl "${url}/adv" -o "${adv}" -+ -+cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv") -+ -+# LUKS2. -+DEV="${TMP}/luks2-device" -+new_device "luks2" "${DEV}" -+ -+SLT=1 -+if ! clevis luks bind -f -s "${SLT}" -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then -+ error "${TEST}: Bind should have succeeded." -+fi -+ -+# Now let's rotate the keys in the server and remove the old ones, so that we -+# will be unable to unlock the volume using clevis and will have to provide -+# manually a password for clevis luks regen. -+rm -rf "${TMP}"/db/* -+tangd-keygen $TMP/db sig exc -+if which tangd-update; then -+ mkdir -p "${TMP}/cache" -+ tangd-update "${TMP}/db" "${TMP}/cache" -+fi -+ -+# Making sure we have two slots enabled. -+enabled=$(cryptsetup luksDump "${DEV}" \ -+ | sed -rn 's|^\s+([0-9]+): luks2$|\1|p' | wc -l) -+if [ "${enabled}" -ne 2 ]; then -+ error "${TEST}: we should have two slots enabled (${enabled})." -+fi -+ -+# Now let's try regen. -+if ! clevis_regen "${DEV}" "${SLT}" "${DEFAULT_PASS}"; then -+ error "${TEST}: clevis luks regen failed" -+fi -+ -+TEST_DEV="test-device-${RANDOM}" -+if ! clevis luks unlock -d "${DEV}" -n "${TEST_DEV}"; then -+ error "${TEST}: we were unable to unlock ${DEV}." -+fi -+ -+cryptsetup close "${TEST_DEV}" -+ -+kill -9 "${PID}" -+! wait "${PID}" -+unset PID -diff --git a/src/luks/tests/tests-common-functions b/src/luks/tests/tests-common-functions -index 7758876..1139f09 100644 ---- a/src/luks/tests/tests-common-functions -+++ b/src/luks/tests/tests-common-functions -@@ -63,7 +63,7 @@ new_device() { - return 0 - fi - -- fallocate -l16M "${DEV}" -+ fallocate -l64M "${DEV}" - local extra_options='--pbkdf pbkdf2 --pbkdf-force-iterations 1000' - cryptsetup luksFormat --type "${LUKS}" ${extra_options} --batch-mode --force-password "${DEV}" <<< "${DEFAULT_PASS}" - # Caching the just-formatted device for possible reuse. -@@ -83,4 +83,29 @@ pin_cfg_equal() { - <(jq -S . < <(echo -n "${cfg2}")) - } - -+clevis_regen() { -+ local DEV="${1}" -+ local SLT="${2}" -+ local PASS="${3}" -+ -+ expect -d << CLEVIS_REGEN -+ set timeout 120 -+ spawn sh -c "clevis luks regen -d $DEV -s $SLT" -+ expect { -+ "Enter existing LUKS password" { -+ send "$PASS\r" -+ exp_continue -+ } -+ "Do you wish to trust these keys" { -+ send "y\r" -+ exp_continue -+ } -+ expect eof -+ wait -+ } -+CLEVIS_REGEN -+ ret=$? -+ return "${ret}" -+} -+ - export DEFAULT_PASS='just-some-test-password-here' --- -2.18.2 - diff --git a/SOURCES/Improve-error-message-when-bind-is-given-an-invalid-.patch b/SOURCES/Improve-error-message-when-bind-is-given-an-invalid-.patch deleted file mode 100644 index e0c2af0..0000000 --- a/SOURCES/Improve-error-message-when-bind-is-given-an-invalid-.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 02f17448e379c92745f8203f47e5de0725b1c1b6 Mon Sep 17 00:00:00 2001 -From: Sergio Correia -Date: Fri, 18 Oct 2019 09:04:22 -0300 -Subject: [PATCH] Improve error message when bind is given an invalid PIN - ---- - src/luks/clevis-luks-bind | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - -diff --git a/src/luks/clevis-luks-bind b/src/luks/clevis-luks-bind -index 7aae2ea..1b5caf2 100755 ---- a/src/luks/clevis-luks-bind -+++ b/src/luks/clevis-luks-bind -@@ -19,6 +19,8 @@ - # along with this program. If not, see . - # - -+. clevis-luks-common-functions -+ - SUMMARY="Binds a LUKS device using the specified policy" - UUID=cb6e8904-81ff-40da-a84a-07ab9ab5715e - -@@ -68,6 +70,9 @@ fi - if ! PIN=${@:$((OPTIND++)):1} || [ -z "$PIN" ]; then - echo "Did not specify a pin!" >&2 - usage -+elif ! EXE=$(findexe clevis-encrypt-"${PIN}"); then -+ echo "'$PIN' is not a valid pin!" >&2 -+ usage - fi - - if ! CFG=${@:$((OPTIND++)):1} || [ -z "$CFG" ]; then -@@ -142,4 +147,4 @@ else - cryptsetup luksRemoveKey "$DEV" <<<"$key" - exit 1 - fi --fi -\ No newline at end of file -+fi --- -2.21.0 - diff --git a/SOURCES/Improve-tests-speed.patch b/SOURCES/Improve-tests-speed.patch deleted file mode 100644 index 3993614..0000000 --- a/SOURCES/Improve-tests-speed.patch +++ /dev/null @@ -1,60 +0,0 @@ -From 874ee402645f9c3c40eaf5882a9cb92bc88b1a8f Mon Sep 17 00:00:00 2001 -From: Sergio Correia -Date: Sun, 22 Dec 2019 16:46:19 -0500 -Subject: [PATCH] Improve tests speed Also run tests only as root, if they - involve cryptsetup - ---- - src/luks/tests/tests-common-functions | 16 +++++++++++++--- - 1 file changed, 13 insertions(+), 3 deletions(-) - -diff --git a/src/luks/tests/tests-common-functions b/src/luks/tests/tests-common-functions -index b65a84a..7758876 100644 ---- a/src/luks/tests/tests-common-functions -+++ b/src/luks/tests/tests-common-functions -@@ -25,6 +25,11 @@ luks2_supported() { - return 0 - } - -+skip_test() { -+ echo "${1}" >&2 -+ exit 77 -+} -+ - # Creates a tang adv to be used in the test. - create_tang_adv() { - local adv="${1}" -@@ -39,14 +44,18 @@ create_tang_adv() { - | jose jws sig -I- -s "${TEMPLATE}" -k "${SIG}" -o "${adv}" - } - -- - # Creates a new LUKS1 or LUKS2 device to be used. - new_device() { - local LUKS="${1}" - local DEV="${2}" -- - local DEV_CACHED="${TMP}/${LUKS}.cached" - -+ # Some builders fail if the cryptsetup steps are not ran as root, so let's -+ # skip the test now if not running as root. -+ if [ $(id -u) != 0 ]; then -+ skip_test "WARNING: You must be root to run this test; test skipped." -+ fi -+ - # Let's reuse an existing device, if there is one. - if [ -f "${DEV_CACHED}" ]; then - echo "Reusing cached ${LUKS} device..." -@@ -55,7 +64,8 @@ new_device() { - fi - - fallocate -l16M "${DEV}" -- cryptsetup luksFormat --type "${LUKS}" --batch-mode --force-password "${DEV}" <<< "${DEFAULT_PASS}" -+ local extra_options='--pbkdf pbkdf2 --pbkdf-force-iterations 1000' -+ cryptsetup luksFormat --type "${LUKS}" ${extra_options} --batch-mode --force-password "${DEV}" <<< "${DEFAULT_PASS}" - # Caching the just-formatted device for possible reuse. - cp -f "${DEV}" "${DEV_CACHED}" - } --- -2.18.1 - diff --git a/SOURCES/Install-cryptsetup-and-tpm2_pcrlist-in-the-initramfs.patch b/SOURCES/Install-cryptsetup-and-tpm2_pcrlist-in-the-initramfs.patch deleted file mode 100644 index 28082b7..0000000 --- a/SOURCES/Install-cryptsetup-and-tpm2_pcrlist-in-the-initramfs.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 34658590e45ab85f6008379d9433406a5c7fd914 Mon Sep 17 00:00:00 2001 -From: Javier Martinez Canillas -Date: Wed, 7 Nov 2018 15:12:17 +0100 -Subject: [PATCH 2/3] Install cryptsetup and tpm2_pcrlist in the initramfs - -The cryptsetup and tpm2_pcrlist are missing in the initramfs, this makes -automatic LUKS unlocking fail with the following errors: - -dracut-initqueue[382]: /usr/libexec/clevis-luks-askpass: line 52: cryptsetup: command not found -dracut-initqueue[382]: /usr/bin/clevis-decrypt-tpm2: line 40: tpm2_pcrlist: command not found - -Suggested-by: Federico Chiacchiaretta - -Fixes: #74 ---- - src/luks/systemd/dracut/module-setup.sh.in | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/src/luks/systemd/dracut/module-setup.sh.in b/src/luks/systemd/dracut/module-setup.sh.in -index 48aea5b3f29a..41e7d6c9b002 100755 ---- a/src/luks/systemd/dracut/module-setup.sh.in -+++ b/src/luks/systemd/dracut/module-setup.sh.in -@@ -40,6 +40,7 @@ install() { - clevis-decrypt-sss \ - @libexecdir@/clevis-luks-askpass \ - clevis-decrypt \ -+ cryptsetup \ - luksmeta \ - clevis \ - mktemp \ -@@ -49,6 +50,7 @@ install() { - - for cmd in clevis-decrypt-tpm2 \ - tpm2_createprimary \ -+ tpm2_pcrlist \ - tpm2_unseal \ - tpm2_load; do - -@@ -60,6 +62,7 @@ install() { - if (($ret == 0)); then - inst_multiple clevis-decrypt-tpm2 \ - tpm2_createprimary \ -+ tpm2_pcrlist \ - tpm2_unseal \ - tpm2_load - fi --- -2.19.1 - diff --git a/SOURCES/Use-one-clevis-luks-askpass-per-device.patch b/SOURCES/Use-one-clevis-luks-askpass-per-device.patch deleted file mode 100644 index 87c657b..0000000 --- a/SOURCES/Use-one-clevis-luks-askpass-per-device.patch +++ /dev/null @@ -1,237 +0,0 @@ -From 523f1361c759d5af0952b0137d4dbd51be1e7b3d Mon Sep 17 00:00:00 2001 -From: Sergio Correia -Date: Sun, 22 Dec 2019 17:01:09 -0500 -Subject: [PATCH] Use one clevis-luks-askpass per device - -This should improve the reliability of the boot unlocking, especially -when unlocking multiple devices upon boot. - -It also greatly simplifies the configuration, as three is no need to -enable any systemd units manually nor add _netdev to either fstab or -crypttab. ---- - src/luks/clevis-luks-unlockers.7.adoc | 16 +++---------- - src/luks/systemd/clevis-luks-askpass | 7 +++++- - src/luks/systemd/clevis-luks-askpass.path | 10 -------- - .../systemd/clevis-luks-askpass.service.in | 8 ------- - src/luks/systemd/clevis-luks-askpass@.path | 12 ++++++++++ - .../systemd/clevis-luks-askpass@.service.in | 9 +++++++ - src/luks/systemd/dracut/module-setup.sh.in | 24 +++++++++++++++++++ - src/luks/systemd/meson.build | 8 +++---- - 8 files changed, 58 insertions(+), 36 deletions(-) - delete mode 100644 src/luks/systemd/clevis-luks-askpass.path - delete mode 100644 src/luks/systemd/clevis-luks-askpass.service.in - create mode 100644 src/luks/systemd/clevis-luks-askpass@.path - create mode 100644 src/luks/systemd/clevis-luks-askpass@.service.in - -diff --git a/src/luks/clevis-luks-unlockers.7.adoc b/src/luks/clevis-luks-unlockers.7.adoc -index 161b73a..e8d47ba 100644 ---- a/src/luks/clevis-luks-unlockers.7.adoc -+++ b/src/luks/clevis-luks-unlockers.7.adoc -@@ -26,7 +26,7 @@ You can unlock a LUKS volume manually using the following command: - - For more information, see link:clevis-luks-unlock.1.adoc[*clevis-luks-unlock*(1)]. - --== EARLY BOOT UNLOCKING -+== BOOT UNLOCKING - - If Clevis integration does not already ship in your initramfs, you may need to - rebuild your initramfs with this command: -@@ -34,23 +34,13 @@ rebuild your initramfs with this command: - $ sudo dracut -f - - Once Clevis is integrated into your initramfs, a simple reboot should unlock --your root volume. Note, however, that early boot integration only works for the --root volume. Non-root volumes should use the late boot unlocker. -+your clevis-bound volumes. Root volumes will be unlocked in early-boot, while the -+remaining volumes will be unlocked after dracut switch-root. - - Dracut will bring up your network using DHCP by default. If you need to specify - additional network parameters, such as static IP configuration, please consult - the dracut documentation. - --== LATE BOOT UNLOCKING -- --You can enable late boot unlocking by executing the following command: -- -- $ sudo systemctl enable clevis-luks-askpass.path -- --After a reboot, Clevis will attempt to unlock all *_netdev* devices listed in --*/etc/crypttab* when systemd prompts for their passwords. This implies that --systemd support for *_netdev* is required. -- - == DESKTOP UNLOCKING - - When the udisks2 unlocker is installed, your GNOME desktop session should -diff --git a/src/luks/systemd/clevis-luks-askpass b/src/luks/systemd/clevis-luks-askpass -index b01d93a..feebb1a 100755 ---- a/src/luks/systemd/clevis-luks-askpass -+++ b/src/luks/systemd/clevis-luks-askpass -@@ -24,15 +24,17 @@ UUID=cb6e8904-81ff-40da-a84a-07ab9ab5715e - shopt -s nullglob - - path=/run/systemd/ask-password --while getopts ":lp:" o; do -+while getopts ":lpu:" o; do - case "$o" in - l) loop=true;; - p) path=$OPTARG;; -+ u) device_uuid=$OPTARG;; - esac - done - - while true; do - todo=0 -+ [ -n "${device_uuid}" ] && todo=1 && loop=true - - for question in $path/ask.*; do - metadata=false -@@ -48,6 +50,8 @@ while true; do - done < "$question" - - [ -z "$d" -o -z "$s" ] && continue -+ [[ -n "${device_uuid}" ]] && [[ "${d}" != *"${device_uuid}"* ]] \ -+ && continue - - if cryptsetup isLuks --type luks1 "$d"; then - # If the device is not initialized, sliently skip it. -@@ -79,6 +83,7 @@ while true; do - done - fi - -+ [ -n "${device_uuid}" ] && [ "${unlocked}" == true ] && todo=0 && break - [ $metadata == true ] || continue - [ $unlocked == true ] && continue - todo=$((todo + 1)) -diff --git a/src/luks/systemd/clevis-luks-askpass.path b/src/luks/systemd/clevis-luks-askpass.path -deleted file mode 100644 -index a4d01ba..0000000 ---- a/src/luks/systemd/clevis-luks-askpass.path -+++ /dev/null -@@ -1,10 +0,0 @@ --[Unit] --Description=Clevis systemd-ask-password Watcher --Before=remote-fs-pre.target --Wants=remote-fs-pre.target -- --[Path] --PathChanged=/run/systemd/ask-password -- --[Install] --WantedBy=remote-fs.target -diff --git a/src/luks/systemd/clevis-luks-askpass.service.in b/src/luks/systemd/clevis-luks-askpass.service.in -deleted file mode 100644 -index 2c6bbed..0000000 ---- a/src/luks/systemd/clevis-luks-askpass.service.in -+++ /dev/null -@@ -1,8 +0,0 @@ --[Unit] --Description=Clevis LUKS systemd-ask-password Responder --Requires=network-online.target --After=network-online.target -- --[Service] --Type=oneshot --ExecStart=@libexecdir@/clevis-luks-askpass -l -diff --git a/src/luks/systemd/clevis-luks-askpass@.path b/src/luks/systemd/clevis-luks-askpass@.path -new file mode 100644 -index 0000000..3f23665 ---- /dev/null -+++ b/src/luks/systemd/clevis-luks-askpass@.path -@@ -0,0 +1,12 @@ -+[Unit] -+Description=Clevis systemd-ask-password Watcher for %i -+DefaultDependencies=no -+Conflicts=shutdown.target -+Before=basic.target shutdown.target -+ -+[Path] -+DirectoryNotEmpty=/run/systemd/ask-password -+MakeDirectory=yes -+ -+[Install] -+WantedBy=basic.target -diff --git a/src/luks/systemd/clevis-luks-askpass@.service.in b/src/luks/systemd/clevis-luks-askpass@.service.in -new file mode 100644 -index 0000000..cd26eb2 ---- /dev/null -+++ b/src/luks/systemd/clevis-luks-askpass@.service.in -@@ -0,0 +1,9 @@ -+[Unit] -+Description=Clevis LUKS systemd-ask-password Responder for luks-%i -+DefaultDependencies=no -+Conflicts=shutdown.target -+Before=shutdown.target -+ -+[Service] -+Type=oneshot -+ExecStart=@libexecdir@/clevis-luks-askpass -u %i -diff --git a/src/luks/systemd/dracut/module-setup.sh.in b/src/luks/systemd/dracut/module-setup.sh.in -index 841f7a8..1877715 100755 ---- a/src/luks/systemd/dracut/module-setup.sh.in -+++ b/src/luks/systemd/dracut/module-setup.sh.in -@@ -29,6 +29,29 @@ is_bound_to_tang() { - return 1 - } - -+configure_passwd_watchers() { -+ if ! command -v systemctl >/dev/null; then -+ return 1 -+ fi -+ -+ local proc_cmdline -+ proc_cmdline=$(/dev/null); then -+ local action=enable -+ [ -z "${cfg}" ] && action=disable -+ systemctl "${action}" "clevis-luks-askpass@${luks_uuid}.path" 2>/dev/null -+ fi -+ done -+} -+ - depends() { - local depends="crypt systemd" - if is_bound_to_tang; then -@@ -84,6 +107,7 @@ install() { - inst_libdir_file "libtss2-tcti-device.so*" - fi - -+ configure_passwd_watchers - dracut_need_initqueue - } - -diff --git a/src/luks/systemd/meson.build b/src/luks/systemd/meson.build -index 108e9d8..334e84c 100644 ---- a/src/luks/systemd/meson.build -+++ b/src/luks/systemd/meson.build -@@ -6,14 +6,14 @@ if systemd.found() - unitdir = systemd.get_pkgconfig_variable('systemdsystemunitdir') - - configure_file( -- input: 'clevis-luks-askpass.service.in', -- output: 'clevis-luks-askpass.service', -+ input: 'clevis-luks-askpass@.service.in', -+ output: 'clevis-luks-askpass@.service', - install_dir: unitdir, - configuration: data, - ) - -- install_data('clevis-luks-askpass.path', install_dir: unitdir) -+ install_data('clevis-luks-askpass@.path', install_dir: unitdir) - install_data('clevis-luks-askpass', install_dir: libexecdir) - else - warning('Will not install systemd support due to missing dependencies!') --endif -\ No newline at end of file -+endif --- -2.18.1 - diff --git a/SOURCES/luks-fix-handling-of-devices-in-clevis-luks-askpass-.patch b/SOURCES/luks-fix-handling-of-devices-in-clevis-luks-askpass-.patch deleted file mode 100644 index 4aa7f68..0000000 --- a/SOURCES/luks-fix-handling-of-devices-in-clevis-luks-askpass-.patch +++ /dev/null @@ -1,61 +0,0 @@ -From 1f9e0d9533e970a79bb9a525b5e407bf80f6fc5b Mon Sep 17 00:00:00 2001 -From: Sergio Correia -Date: Mon, 6 Jul 2020 08:53:58 -0300 -Subject: [PATCH] luks: fix handling of devices in clevis-luks-askpass to - handle - ---- - src/luks/systemd/clevis-luks-askpass | 23 ++++++++++++++++++++--- - 1 file changed, 20 insertions(+), 3 deletions(-) - -diff --git a/src/luks/systemd/clevis-luks-askpass b/src/luks/systemd/clevis-luks-askpass -index feebb1a..5719ab6 100755 ---- a/src/luks/systemd/clevis-luks-askpass -+++ b/src/luks/systemd/clevis-luks-askpass -@@ -23,6 +23,12 @@ UUID=cb6e8904-81ff-40da-a84a-07ab9ab5715e - - shopt -s nullglob - -+clevis_is_luks_device_by_uuid_open() { -+ local LUKS_UUID="${1}" -+ [ -z "${LUKS_UUID}" ] && return 1 -+ test -b /dev/disk/by-id/dm-uuid-*"${LUKS_UUID//-/}"* -+} -+ - path=/run/systemd/ask-password - while getopts ":lpu:" o; do - case "$o" in -@@ -49,9 +55,13 @@ while true; do - esac - done < "$question" - -- [ -z "$d" -o -z "$s" ] && continue -- [[ -n "${device_uuid}" ]] && [[ "${d}" != *"${device_uuid}"* ]] \ -- && continue -+ [ -b "${d}" ] || continue -+ [ -S "${s}" ] || continue -+ -+ if [ -n "${device_uuid}" ]; then -+ uuid="$(cryptsetup luksUUID "${d}")" -+ [ "${uuid}" != "${device_uuid}" ] && todo=1 && continue -+ fi - - if cryptsetup isLuks --type luks1 "$d"; then - # If the device is not initialized, sliently skip it. -@@ -89,6 +99,13 @@ while true; do - todo=$((todo + 1)) - done - -+ if [ -n "${device_uuid}" ]; then -+ [ ! -b /dev/disk/by-uuid/"${device_uuid}" ] && break -+ if clevis_is_luks_device_by_uuid_open "${device_uuid}"; then -+ break -+ fi -+ fi -+ - if [ $todo -eq 0 ] || [ "$loop" != "true" ]; then - break; - fi --- -2.18.4 - diff --git a/SPECS/clevis.spec b/SPECS/clevis.spec index 8e0cab4..63a02e4 100644 --- a/SPECS/clevis.spec +++ b/SPECS/clevis.spec @@ -1,28 +1,24 @@ %global _hardened_build 1 Name: clevis -Version: 11 -Release: 9%{?dist}.1 +Version: 13 +Release: 3%{?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.xz -Patch01: Delete-remaining-references-to-the-removed-http-pin.patch -Patch02: Install-cryptsetup-and-tpm2_pcrlist-in-the-initramfs.patch -Patch03: Add-device-TCTI-library-to-the-initramfs.patch -Patch04: Check-key-derivation-key-is-available.patch -Patch05: Adjust-pin-tang-test-to-account-for-newer-tang-witho.patch -Patch06: Add-clevis-luks-report-regen.patch -Patch07: Improve-error-message-when-bind-is-given-an-invalid-.patch -Patch08: Add-support-for-listing-existing-PBD-policies-in-pla.patch -Patch09: Add-the-option-to-extract-luks-passphrase-used-for-b.patch -Patch10: Add-rd.neednet-1-to-cmdline-only-if-there-are-device.patch -Patch11: Improve-tests-speed.patch -Patch12: Use-one-clevis-luks-askpass-per-device.patch -Patch13: Improve-clevis-luks-regen-no-unbind-in-every-case.patch -Patch14: luks-fix-handling-of-devices-in-clevis-luks-askpass-.patch +Patch0001: 0001-Adjust-pin-tang-test-to-account-for-newer-tang-witho.patch +Patch0002: 0002-Fix-clevis-luks-unlock-and-add-related-tests.patch +Patch0003: 0003-Improve-error-message-when-bind-is-given-an-invalid-.patch +Patch0004: 0004-Add-rd.neednet-1-to-cmdline-only-if-there-are-device.patch +Patch0005: 0005-Add-the-option-to-extract-luks-passphrase-used-for-b.patch +Patch0006: 0006-Add-clevis-luks-regen-command.patch +Patch0007: 0007-Add-clevis-luks-report.patch +Patch0008: 0008-Use-one-clevis-luks-askpass-per-device.patch +Patch0009: 0009-Introduce-y-assume-yes-argument-to-clevis-luks-bind.patch +Patch0010: 0010-Add-clevis-luks-edit-command.patch BuildRequires: gcc BuildRequires: meson @@ -48,11 +44,14 @@ BuildRequires: cracklib-dicts Buildrequires: jq BuildRequires: diffutils BuildRequires: expect +BuildRequires: openssl +Requires: cracklib-dicts Requires: tpm2-tools >= 3.0.0 Requires: coreutils Requires: jose >= 8 Requires: curl +Requires: jq Requires(pre): shadow-utils %description @@ -63,17 +62,17 @@ 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 +and clevis-dracut packages for automatic root volume unlocking of LUKS volumes during early boot. %package luks -Summary: LUKSv1 integration for clevis +Summary: LUKS 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 +LUKS integration for clevis. This package allows you to bind a LUKS volume to a clevis unlocking policy. For automated unlocking, an unlocker will also be required. See, for example, clevis-dracut and clevis-udisks2. @@ -96,7 +95,7 @@ Requires: systemd%{?_isa} >= 236 Requires: nc %description systemd -Automatically unlocks LUKSv1 _netdev block devices from /etc/crypttab. +Automatically unlocks clevis-bound LUKS block devices during boot. %package dracut Summary: Dracut integration for clevis @@ -104,18 +103,18 @@ Requires: %{name}-systemd%{?_isa} = %{version}-%{release} Requires: dracut-network %description dracut -Automatically unlocks LUKSv1 block devices in early boot. +Automatically unlocks LUKS 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 +Automatically unlocks LUKS block devices in desktop environments that use UDisks2 or storaged (like GNOME). %prep -%autosetup -p1 +%autosetup -S git %build %meson -Duser=clevis -Dgroup=clevis @@ -167,22 +166,24 @@ exit 0 %{_mandir}/man1/%{name}-luks-unlock.1* %{_mandir}/man1/%{name}-luks-unbind.1* %{_mandir}/man1/%{name}-luks-bind.1* +%{_mandir}/man1/%{name}-luks-list.1* +%{_mandir}/man1/%{name}-luks-pass.1* %{_mandir}/man1/%{name}-luks-regen.1* %{_mandir}/man1/%{name}-luks-report.1* -%{_mandir}/man1/%{name}-luks-list.1* -%{_mandir}/man1/%{name}-luks-pass.1.* +%{_mandir}/man1/%{name}-luks-edit.1* %{_bindir}/%{name}-luks-unlock %{_bindir}/%{name}-luks-unbind %{_bindir}/%{name}-luks-bind %{_bindir}/%{name}-luks-common-functions +%{_bindir}/%{name}-luks-list +%{_bindir}/%{name}-luks-pass %{_bindir}/%{name}-luks-regen %{_bindir}/%{name}-luks-report %{_bindir}/%{name}-luks-report-compare %{_bindir}/%{name}-luks-report-decode %{_bindir}/%{name}-luks-report-sss %{_bindir}/%{name}-luks-report-tang -%{_bindir}/%{name}-luks-list -%{_bindir}/%{name}-luks-pass +%{_bindir}/%{name}-luks-edit %files systemd %{_libexecdir}/%{name}-luks-askpass @@ -191,17 +192,33 @@ exit 0 %files dracut %{_prefix}/lib/dracut/modules.d/60%{name} +%{_prefix}/lib/dracut/modules.d/60%{name}-pin-sss +%{_prefix}/lib/dracut/modules.d/60%{name}-pin-tang +%{_prefix}/lib/dracut/modules.d/60%{name}-pin-tpm2 %files udisks2 %{_sysconfdir}/xdg/autostart/%{name}-luks-udisks2.desktop %attr(4755, root, root) %{_libexecdir}/%{name}-luks-udisks2 %changelog -* Mon Jul 06 2020 Sergio Correia - 11-9.1 -- Fix handling of device names in clevis-luks-askpass - Resolves: rhbz#1849593 - -* Wed Feb 02 2020 Sergio Correia - 11-9 +* Wed May 20 2020 Sergio Correia - 13-3 +- Add clevis luks edit command + Resolves: rhbz#1436735 + +* Mon May 18 2020 Sergio Correia - 13-2 +- Introduce -y (assume yes) argument to clevis luks bind + Resolves: rhbz#1819767 + +* Sun May 10 2020 Sergio Correia - 13-1 +- Update to new upstream release, v13 + Resolves: rhbz#1827225 + Resolves: rhbz#1827665 + Resolves: rhbz#1801556 + Resolves: rhbz#1784448 + Resolves: rhbz#1826917 + Resolves: rhbz#1812014 + +* Sun Feb 02 2020 Sergio Correia - 11-9 - Improve clevis luks regen not to unbind+bind in every case Resolves: rhbz#1795675