123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- #!/bin/bash -ex
- # vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
- #
- # Copyright (c) 2019 Red Hat, Inc.
- # 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/>.
- #
- error() {
- echo "${1}" >&2
- exit 1
- }
- skip_test() {
- echo "${1}" >&2
- exit 77
- }
- # We require cryptsetup >= 2.0.4 to fully support LUKSv2.
- # Support is determined at build time.
- luks2_supported() {
- return @OLD_CRYPTSETUP@
- }
- # 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 PASS="${3}"
- # 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
- # Using a default password, if none has been provided.
- if [ -z "${PASS}" ]; then
- PASS="${DEFAULT_PASS}"
- fi
- 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 -l64M "${DEV}"
- cryptsetup luksFormat --type "${LUKS}" --pbkdf pbkdf2 \
- --pbkdf-force-iterations 1000 --batch-mode \
- --force-password "${DEV}" <<< "${PASS}"
- # Caching the just-formatted device for possible reuse.
- cp -f "${DEV}" "${DEV_CACHED}"
- }
- # Creates a new LUKS1 or LUKS2 device to be used, using a keyfile.
- new_device_keyfile() {
- local LUKS="${1}"
- local DEV="${2}"
- local KEYFILE="${3}"
- # 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
- if [[ -z "${KEYFILE}" ]] || [[ ! -f "${KEYFILE}" ]]; then
- error "Invalid keyfile (${KEYFILE})."
- fi
- fallocate -l64M "${DEV}"
- cryptsetup luksFormat --type "${LUKS}" --pbkdf pbkdf2 \
- --pbkdf-force-iterations 1000 --batch-mode \
- "${DEV}" "${KEYFILE}"
- }
- pin_cfg_equal() {
- # Let's remove the single quotes from the pin configuration.
- local cfg1="${1//\'/}"
- local cfg2="${2//\'/}"
- # Now we sort and present them in compact form.
- local sorted_cfg1 sorted_cfg2
- sorted_cfg1="$(jq --compact-output --sort-keys . < <(echo -n "${cfg1}"))"
- sorted_cfg2="$(jq --compact-output --sort-keys . < <(echo -n "${cfg2}"))"
- # And we finally compare.
- if [ "${sorted_cfg1}" = "${sorted_cfg2}" ]; then
- return 0
- fi
- return 1
- }
- compare_luks_header() {
- DEV1="${1}"
- DEV2="${2}"
- TMP="${3}"
- cryptsetup luksHeaderBackup "${DEV1}" \
- --header-backup-file "${TMP}"/check-header1
- cryptsetup luksHeaderBackup "${DEV2}" \
- --header-backup-file "${TMP}"/check-header2
- local cs1 cs2
- cs1=$(cksum "${TMP}"/check-header1 | cut -d' ' -f 1)
- cs2=$(cksum "${TMP}"/check-header2 | cut -d' ' -f 1)
- rm -f "${TMP}"/check-header{1,2}
- if [ "${cs1}" == "${cs2}" ]; then
- return 0
- fi
- return 1
- }
- used_luks1_metadata_slots() {
- DEV="${1}"
- if ! luksmeta test -d "${DEV}"; then
- echo ""
- return 0
- fi
- local clevis_uuid="cb6e8904-81ff-40da-a84a-07ab9ab5715e"
- luksmeta show -d "${DEV}" \
- | sed -rn "s|^([0-9]+)\s+active\s+${clevis_uuid}$|\1|p" \
- | tr '\n' ' ' | sed 's/ $//'
- }
- used_luks2_metadata_slots() {
- DEV="${1}"
- cryptsetup luksDump "${DEV}" \
- | grep -E -A1 "^\s+[0-9]+:\s+clevis$" \
- | sed -rn 's|^\s+Keyslot:\s+([0-9]+)$|\1|p' | sort -n \
- | tr '\n' ' ' | sed 's/ $//'
- }
- used_luks2_metadata_tokens() {
- DEV="${1}"
- cryptsetup luksDump "${DEV}" \
- | grep -E -B1 "^\s+Keyslot:\s+[0-9]+$" \
- | sed -rn 's|^\s+([0-9]+): clevis|\1|p' \
- | tr '\n' ' ' | sed 's/ $//'
- }
- compare_luks1_metadata() {
- DEV1="${1}"
- DEV2="${2}"
- # If both are non-initialized, metadata is the same.
- ! luksmeta test -d "${DEV1}" && ! luksmeta test -d "${DEV2}" && return 0
- # Otherwise, metadata differ.
- ! luksmeta test -d "${DEV1}" && return 1
- ! luksmeta test -d "${DEV2}" && return 1
- local slt1 slt2
- slt1=$(used_luks1_metadata_slots "${DEV1}")
- slt2=$(used_luks1_metadata_slots "${DEV2}")
- if [ "${slt1}" != "${slt2}" ]; then
- echo "used slots did not match ($slt1) ($slt2)" >&2
- return 1
- fi
- local slt md1 md2
- for slt in ${slt1}; do
- md1="$(luksmeta load -d "${DEV}" -s "${slt}")"
- md2="$(luksmeta load -d "${DEV2}" -s "${slt}")"
- if [ "${md1}" != "${md2}" ]; then
- echo "metadata in slot ${slt} did not match" >&2
- return 1
- fi
- done
- return 0
- }
- compare_luks2_metadata() {
- DEV1="${1}"
- DEV2="${2}"
- local slt1 slt2
- slt1=$(used_luks2_metadata_slots "${DEV1}")
- slt2=$(used_luks2_metadata_slots "${DEV2}")
- if [ "${slt1}" != "${slt2}" ]; then
- echo "used slots did not match ($slt1) ($slt2)" >&2
- return 1
- fi
- local tkn1 tkn2
- tkn1=$(used_luks2_metadata_tokens "${DEV1}")
- tkn2=$(used_luks2_metadata_tokens "${DEV2}")
- if [ "${tkn1}" != "${tkn2}" ]; then
- echo "used tokens did not match ($tkn1) ($tkn2)" >&2
- return 1
- fi
- local tkn md1 md2
- for tkn in ${tkn1}; do
- md1="$(cryptsetup token export --token-id "${tkn}" "${DEV1}")"
- md2="$(cryptsetup token export --token-id "${tkn}" "${DEV2}")"
- if [ "${md1}" != "${md2}" ]; then
- echo "metadata in token ${tkn} did not match" >&2
- return 1
- fi
- done
- return 0
- }
- # Get a random port to be used with a test tang server.
- get_random_port() {
- shuf -i 1024-65535 -n 1
- }
- # Removes tang rotated keys from the test server.
- tang_remove_rotated_keys() {
- local basedir="${1}"
- if [ -z "${basedir}" ]; then
- echo "Please pass a valid base directory for tang"
- return 1
- fi
- [ -z "${TANGD_UPDATE}" ] && skip_test "WARNING: TANGD_UPDATE is not defined."
- local db="${basedir}/db"
- local cache="${basedir}/cache"
- mkdir -p "${db}"
- mkdir -p "${cache}"
- pushd "${db}"
- find . -name ".*.jwk" -exec rm -f {} \;
- popd
- "${TANGD_UPDATE}" "${db}" "${cache}"
- return 0
- }
- # Creates new keys for the test tang server.
- tang_new_keys() {
- local basedir="${1}"
- local rotate="${2}"
- if [ -z "${basedir}" ]; then
- echo "Please pass a valid base directory for tang"
- return 1
- fi
- [ -z "${TANGD_KEYGEN}" ] && skip_test "WARNING: TANGD_KEYGEN is not defined."
- [ -z "${TANGD_UPDATE}" ] && skip_test "WARNING: TANGD_UPDATE is not defined."
- local db="${basedir}/db"
- local cache="${basedir}/cache"
- mkdir -p "${db}"
- if [ -n "${rotate}" ]; then
- pushd "${db}"
- local k
- k=$(find . -name "*.jwk" | wc -l)
- if [ "${k}" -gt 0 ]; then
- for k in *.jwk; do
- mv -f -- "${k}" ".${k}"
- done
- fi
- popd
- fi
- "${TANGD_KEYGEN}" "${db}"
- "${TANGD_UPDATE}" "${db}" "${cache}"
- return 0
- }
- # Start a test tang server.
- tang_run() {
- local basedir="${1}"
- local port="${2}"
- if [ -z "${basedir}" ]; then
- echo "Please pass a valid base directory for tang" >&2
- return 1
- fi
- if [ -z "${port}" ]; then
- echo "Please pass a valid port for tang" >&2
- return 1
- fi
- if ! tang_new_keys "${basedir}"; then
- echo "Error creating new keys for tang server" >&2
- return 1
- fi
- local KEYS="${basedir}/cache"
- local inetd='--inetd'
- [ "${SD_ACTIVATE##*/}" = "systemd-activate" ] && inetd=
- local pid pidfile
- pidfile="${basedir}/tang.pid"
- "${SD_ACTIVATE}" ${inetd} -l "${TANG_HOST}":"${port}" \
- -a "${TANGD}" "${KEYS}" &
- pid=$!
- echo "${pid}" > "${pidfile}"
- }
- # Stop tang server.
- tang_stop() {
- local basedir="${1}"
- local pidfile="${basedir}/tang.pid"
- [ -f "${pidfile}" ] || return 0
- local pid
- pid=$(<"${pidfile}")
- kill "${pid}"
- }
- # Wait for the tang server to be operational.
- tang_wait_until_ready() {
- local port="${1}"
- while ! curl --output /dev/null --silent --fail \
- http://"${TANG_HOST}":"${port}"/adv; do
- sleep 0.1
- echo -n . >&2
- done
- }
- # Get tang advertisement.
- tang_get_adv() {
- local port="${1}"
- local adv="${2}"
- curl -o "${adv}" http://"${TANG_HOST}":"${port}"/adv
- }
- export TANG_HOST=127.0.0.1
- export DEFAULT_PASS='just-some-test-password-here'
|