#!/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 # Author: Sergio Correia # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # . clevis-luks-common-functions SUMMARY="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