diff --git a/SOURCES/Add-clevis-luks-report-regen.patch b/SOURCES/Add-clevis-luks-report-regen.patch new file mode 100644 index 0000000..274e651 --- /dev/null +++ b/SOURCES/Add-clevis-luks-report-regen.patch @@ -0,0 +1,858 @@ +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-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 new file mode 100644 index 0000000..a73b797 --- /dev/null +++ b/SOURCES/Add-rd.neednet-1-to-cmdline-only-if-there-are-device.patch @@ -0,0 +1,57 @@ +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 new file mode 100644 index 0000000..4f6c2bf --- /dev/null +++ b/SOURCES/Add-support-for-listing-existing-PBD-policies-in-pla.patch @@ -0,0 +1,1009 @@ +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 new file mode 100644 index 0000000..4f2465d --- /dev/null +++ b/SOURCES/Add-the-option-to-extract-luks-passphrase-used-for-b.patch @@ -0,0 +1,364 @@ +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 new file mode 100644 index 0000000..20e6137 --- /dev/null +++ b/SOURCES/Adjust-pin-tang-test-to-account-for-newer-tang-witho.patch @@ -0,0 +1,77 @@ +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/Improve-clevis-luks-regen-no-unbind-in-every-case.patch b/SOURCES/Improve-clevis-luks-regen-no-unbind-in-every-case.patch new file mode 100644 index 0000000..b905ada --- /dev/null +++ b/SOURCES/Improve-clevis-luks-regen-no-unbind-in-every-case.patch @@ -0,0 +1,1262 @@ +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 new file mode 100644 index 0000000..e0c2af0 --- /dev/null +++ b/SOURCES/Improve-error-message-when-bind-is-given-an-invalid-.patch @@ -0,0 +1,42 @@ +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 new file mode 100644 index 0000000..3993614 --- /dev/null +++ b/SOURCES/Improve-tests-speed.patch @@ -0,0 +1,60 @@ +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/Use-one-clevis-luks-askpass-per-device.patch b/SOURCES/Use-one-clevis-luks-askpass-per-device.patch new file mode 100644 index 0000000..87c657b --- /dev/null +++ b/SOURCES/Use-one-clevis-luks-askpass-per-device.patch @@ -0,0 +1,237 @@ +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/SPECS/clevis.spec b/SPECS/clevis.spec index d72e15d..02a259b 100644 --- a/SPECS/clevis.spec +++ b/SPECS/clevis.spec @@ -2,17 +2,26 @@ Name: clevis Version: 11 -Release: 2%{?dist} +Release: 9%{?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 -Patch0: Delete-remaining-references-to-the-removed-http-pin.patch -Patch1: Install-cryptsetup-and-tpm2_pcrlist-in-the-initramfs.patch -Patch2: Add-device-TCTI-library-to-the-initramfs.patch -Patch3: Check-key-derivation-key-is-available.patch +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 BuildRequires: gcc BuildRequires: meson @@ -33,6 +42,11 @@ BuildRequires: systemd BuildRequires: dracut BuildRequires: tang >= 6 BuildRequires: curl +BuildRequires: luksmeta +BuildRequires: cracklib-dicts +Buildrequires: jq +BuildRequires: diffutils +BuildRequires: expect Requires: tpm2-tools >= 3.0.0 Requires: coreutils @@ -121,6 +135,15 @@ getent passwd %{name} >/dev/null || \ -c "Clevis Decryption Framework unprivileged user" %{name} exit 0 +%pre systemd +if [ $1 -ge 0 ]; then + # clevis-systemd < 11-8 shipped with clevis-luks-askpass.path unit. + # Make sure it's gone. + [ -e /usr/lib/systemd/system/clevis-luks-askpass.path ] && \ + systemctl disable clevis-luks-askpass.path +fi +exit 0 + %files %license COPYING %{_datadir}/bash-completion/ @@ -143,14 +166,27 @@ exit 0 %{_mandir}/man1/%{name}-luks-unlock.1* %{_mandir}/man1/%{name}-luks-unbind.1* %{_mandir}/man1/%{name}-luks-bind.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.* %{_bindir}/%{name}-luks-unlock %{_bindir}/%{name}-luks-unbind %{_bindir}/%{name}-luks-bind +%{_bindir}/%{name}-luks-common-functions +%{_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 %files systemd %{_libexecdir}/%{name}-luks-askpass -%{_unitdir}/%{name}-luks-askpass.path -%{_unitdir}/%{name}-luks-askpass.service +%{_unitdir}/%{name}-luks-askpass@.path +%{_unitdir}/%{name}-luks-askpass@.service %files dracut %{_prefix}/lib/dracut/modules.d/60%{name} @@ -160,6 +196,35 @@ exit 0 %attr(4755, root, root) %{_libexecdir}/%{name}-luks-udisks2 %changelog +* Wed Feb 02 2020 Sergio Correia - 11-9 +- Improve clevis luks regen not to unbind+bind in every case + Resolves: rhbz#1795675 + +* Mon Jan 13 2020 Sergio Correia - 11-8 +- Use one clevis-luks-askpass per device + Resolves: rhbz#1784524 + +* Sat Nov 30 2019 Sergio Correia - 11-7 +- Add rd.neednet=1 to cmdline only if there are devices bound to tang + Resolves: rhbz#1762028 + +* Sat Nov 30 2019 Sergio Correia - 11-6 +- Add option to extract luks passphrase used for binding + Resolves: rhbz#1436780 + +* Thu Nov 28 2019 Sergio Correia - 11-5 +- Add support for listing existing PBD policies in place + Resolves: rhbz#1766526 + +* Fri Oct 18 2019 Sergio Correia - 11-4 +- Improve error message when bind is given an invalid PIN + Resolves: rhbz#1543380 + +* Wed Oct 16 2019 Sergio Correia - 11-3 +- Add clevis luks report and regen + Resolves: rhbz#1564566 + Resolves: rhbz#1564559 + * Fri Jan 04 2019 Daniel Kopecek - 11-2 - Check that key derivation key is available - Delete remaining references to the removed http pin