File: //bin/clevis-luks-report
#!/bin/bash -e
# vim: set ts=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
#
# Copyright (c) 2018, 2020 Red Hat, Inc.
# Author: Radovan Sroka <rsroka@redhat.com>
# Author: Sergio Correia <scorreia@redhat.com>
#
# 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 <http://www.gnu.org/licenses/>.
#
. clevis-luks-common-functions
SUMMARY="Report tang keys' rotations"
if [ "${1}" = "--summary" ]; then
echo "${SUMMARY}"
exit 0
fi
report_compare() {
local adv_keys="${1}"
local mdata_keys="${2}"
[ -z "${adv_keys}" ] && return 1
[ -z "${mdata_keys}" ] && return 1
local thp keys
for thp in $(printf '%s' "${mdata_keys}" | jose jwk thp --input=-); do
if ! printf '%s' "${adv_keys}" | jose jwk thp --input=- \
--find "${thp}" >/dev/null; then
keys="$(printf '%s %s' "${keys}" "${thp}")"
fi
done
printf '%s' "${keys}"
}
report_tang() {
local content="${1}"
[ -z "${content}" ] && return 1
local url
if ! url="$(jose fmt --json="${content}" --get url --unquote=-)" \
|| [ -z "${url}" ]; then
echo "Invalid tang metadata; URL not found" >&2
return 1
fi
local jws
if ! jws="$(curl -sfg "${url}/adv")"; then
echo "Unable to fetch advertisement (${url}/adv)" >&2
return 1
fi
local adv_keys
if ! adv_keys="$(jose fmt --json="${jws}" --object --get payload \
--string --b64load --object --get keys \
--array --unwind --output=-)"; then
echo "Advertisement is malformed" >&2
return 1
fi
# Check advertisement validity.
local ver
if ! ver="$(printf '%s' "${adv_keys}" | jose jwk use --input=- \
--required \
--use=verify \
--output=-)"; then
echo "Unable to validate advertisement" >&2
return 1
fi
if ! printf '%s' "${ver}" | jose jws ver --input="${jws}" --key=- \
--all; then
echo "Advertisement is missing signatures" >&2
return 1
fi
local mdata_keys
if ! mdata_keys="$(jose fmt --json="${content}" --get adv --output=-)" \
|| [ -z "${mdata_keys}" ]; then
echo "Keys from clevis metadata not found" >&2
return 1
fi
report_compare "${adv_keys}" "${mdata_keys}"
}
report_sss() {
local content="${1}"
[ -z "${content}" ] && return 1
local jwe
for jwe in $(jose fmt --json="${content}" --get jwe --foreach=-); do
jwe="$(printf '%s' "${jwe}" | sed -e 's/"//g')"
report_decode "${jwe}"
done
}
report_decode() {
local data64="${1}"
[ -z "${data64}" ] && return 1
local data
if ! data="$(clevis_luks_decode_jwe "${data64}")" || [ -z "${data}" ]; then
echo "Unable to decode metadata" >&2
exit 1
fi
local pin
if ! pin="$(jose fmt --json="${data}" --get clevis --get pin --unquote=-)" \
|| [ -z "${pin}" ]; then
echo "Pin not found in clevis metadata" >&2
exit 1
fi
local content
if ! content="$(jose fmt --json="${data}" --get clevis --get "${pin}" \
--output=-)" || [ -z "${content}" ]; then
echo "Invalid pin metadata; no content found" >&2
return 1
fi
case "${pin}" in
tang)
report_tang "${content}"
;;
sss)
report_sss "${content}"
;;
esac
}
usage_and_exit () {
exec >&2
echo "Usage: clevis luks report [-q] [-r] -d DEV -s SLOT"
echo
echo "${SUMMARY}"
echo
echo " -d DEV The LUKS device to check for key rotations"
echo
echo " -s SLT The LUKS slot to use"
echo
echo " -q Quiet mode; do not prompt for using 'clevis luks regen'"
echo
echo " -r Regenerate binding with 'clevis luks regen -q -d DEV -s SLOT'"
echo
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
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 ! data64="$(clevis_luks_read_slot "${DEV}" "${SLT}")" \
|| [ -z "${data64}" ]; then
# Error message was already displayed by clevis_luks_read_slot(),
# at this point.
exit 1
fi
if ! keys="$(report_decode "${data64}")"; then
echo "Unable to verify whether there are rotated keys" >&2
exit 1
fi
# No rotated keys.
[ -z "${keys}" ] && exit 0
echo "The following keys are not in the current advertisement and were probably rotated:"
for k in ${keys}; do
printf ' %s\n' "${k}"
done
if [ -z "${QOPT}" ] && [ -z "${ROPT}" ]; then
read -r -p "Do you want to regenerate the binding with \"clevis luks regen -q -d ${DEV} -s ${SLT}\"? [ynYN] " ans
if [ "${ans}" = "y" ] || [ "${ans}" = "Y" ]; then
ROPT="regen"
fi
fi
if [ "${ROPT}" = "regen" ]; then
if ! EXE="$(command -v clevis-luks-regen)" || [ -z "${EXE}" ]; then
echo "Unable to find clevis luks regen" >&2
exit 1
fi
exec "${EXE}" -q -d "${DEV}" -s "${SLT}"
fi
exit 1