From 46dc5904a9a6b0f1dcdb2511cc3f09671ef54b2a Mon Sep 17 00:00:00 2001 From: Sergio Correia Date: Wed, 20 May 2020 21:09:15 -0300 Subject: [PATCH] Add clevis luks edit command --- src/luks/clevis-luks-common-functions | 65 ++++-- src/luks/clevis-luks-edit | 242 +++++++++++++++++++++++ src/luks/clevis-luks-edit.1.adoc | 69 +++++++ src/luks/clevis-luks-regen | 125 ++---------- src/luks/meson.build | 3 + src/luks/tests/edit-tang-luks1 | 106 ++++++++++ src/luks/tests/edit-tang-luks2 | 106 ++++++++++ src/luks/tests/meson.build | 28 ++- src/luks/tests/regen-inplace-luks1 | 2 +- src/luks/tests/regen-inplace-luks2 | 2 +- src/luks/tests/regen-not-inplace-luks1 | 2 +- src/luks/tests/regen-not-inplace-luks2 | 2 +- src/luks/tests/tests-common-functions.in | 26 --- src/luks/tests/unlock-tang-luks1 | 7 +- src/luks/tests/unlock-tang-luks2 | 8 +- 15 files changed, 606 insertions(+), 187 deletions(-) create mode 100755 src/luks/clevis-luks-edit create mode 100644 src/luks/clevis-luks-edit.1.adoc create mode 100755 src/luks/tests/edit-tang-luks1 create mode 100755 src/luks/tests/edit-tang-luks2 diff --git a/src/luks/clevis-luks-common-functions b/src/luks/clevis-luks-common-functions index c9d712a..f3a875c 100644 --- a/src/luks/clevis-luks-common-functions +++ b/src/luks/clevis-luks-common-functions @@ -141,11 +141,7 @@ clevis_luks_decode_jwe() { local jwe="${1}" local coded - if ! coded=$(jose jwe fmt -i- <<< "${jwe}"); then - return 1 - fi - - coded=$(jose fmt -j- -g protected -u- <<< "${coded}" | tr -d '"') + read -r -d . coded <<< "${jwe}" jose b64 dec -i- <<< "${coded}" } @@ -291,6 +287,46 @@ clevis_luks_read_pins_from_slot() { printf "%s: %s\n" "${SLOT}" "${cfg}" } +# clevis_luks_is_key_valid() checks whether the given key is valid to unlock +# the given device. +clevis_luks_is_key_valid() { + local DEV="${1}" + local KEY="${2}" + + if ! cryptsetup open --test-passphrase "${DEV}" \ + --key-file <(echo -n "${KEY}") 2>/dev/null; then + return 1 + fi + return 0 +} + +# clevis_luks_unlock_device_by_slot() does the unlock of the device and slot +# passed as parameters and returns the decoded passphrase. +clevis_luks_unlock_device_by_slot() { + local DEV="${1}" + local SLT="${2}" + + [ -z "${DEV}" ] && return 1 + [ -z "${SLT}" ] && return 1 + + local jwe passphrase + if ! jwe="$(clevis_luks_read_slot "${DEV}" "${SLT}" 2>/dev/null)" \ + || [ -z "${jwe}" ]; then + return 1 + fi + + if ! passphrase="$(clevis decrypt < <(echo -n "${jwe}") 2>/dev/null)" \ + || [ -z "${passphrase}" ]; then + return 1 + fi + + if ! clevis_luks_is_key_valid "${DEV}" "${passphrase}"; then + return 1 + fi + echo -n "${passphrase}" + return 0 +} + # clevis_luks_unlock_device() does the unlock of the device passed as # parameter and returns the decoded passphrase. clevis_luks_unlock_device() { @@ -303,26 +339,15 @@ clevis_luks_unlock_device() { return 1 fi - local slt jwe passphrase + local slt pt for slt in ${used_slots}; do - if ! jwe="$(clevis_luks_read_slot "${DEV}" "${slt}" 2>/dev/null)" \ - || [ -z "${jwe}" ]; then - continue - fi - - if ! passphrase="$(clevis decrypt < <(echo -n "${jwe}"))" \ - || [ -z "${passphrase}" ]; then + if ! pt=$(clevis_luks_unlock_device_by_slot "${DEV}" "${slt}") \ + || [ -z "${pt}" ]; then continue fi - - if ! cryptsetup luksOpen --test-passphrase "${DEV}" \ - --key-file <(echo -n "${passphrase}"); then - continue - fi - echo -n "${passphrase}" + echo -n "${pt}" return 0 done - return 1 } diff --git a/src/luks/clevis-luks-edit b/src/luks/clevis-luks-edit new file mode 100755 index 0000000..fc95f75 --- /dev/null +++ b/src/luks/clevis-luks-edit @@ -0,0 +1,242 @@ +#!/bin/bash -e +# vim: set ts=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: +# +# Copyright (c) 2020 Red Hat, Inc. +# Author: Sergio Correia +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +. clevis-luks-common-functions + +SUMMARY="Edit a binding from a clevis-bound slot in a LUKS device" + +usage() { + echo >&2 + echo "Usage: clevis luks edit [-f] -d DEV -s SLT [-c CONFIG]" >&2 + echo >&2 + echo "$SUMMARY": >&2 + echo >&2 + echo " -d DEV The LUKS device to edit clevis-bound pins" >&2 + echo >&2 + echo " -s SLOT The slot to use when editing the clevis binding" >&2 + echo >&2 + echo " -f Proceed with the edit operation even if the configuration is the same" >&2 + echo >&2 + echo " -c CONFIG The updated config to use" >&2 + echo >&2 + exit 1 +} + +on_exit() { + [ -d "$TMP" ] && rm -rf "${TMP}" +} + +validate_cfg() { + local json="${1}" + jose fmt -j- -O <<< "${json}" 2>/dev/null +} + +edit_cfg() { + local cfg_file="${1}" + local editor="${EDITOR:-vi}" + + "${editor}" "${cfg_file}" || true + if ! validate_cfg "$(<"${cfg_file}")"; then + local ans= + while true; do + read -r -p \ + "Malformed configuration. Would you like to edit again? [ynYN] " \ + ans < /dev/tty + + [[ "${ans}" =~ ^[nN]$ ]] && return 1 + [[ "${ans}" =~ ^[yY]$ ]] && break + done + edit_cfg "${cfg_file}" + fi + return 0 +} + +if [ "${#}" -eq 1 ] && [ "${1}" = "--summary" ]; then + echo "${SUMMARY}" + exit 0 +fi + +CFG= +FRC= +while getopts ":fd:s:c:" o; do + case "$o" in + d) DEV=${OPTARG};; + s) SLT=${OPTARG};; + c) CFG=${OPTARG};; + f) FRC=-f;; + *) usage;; + esac +done + +if [ -z "${DEV}" ]; then + echo "Did not specify a device!" >&2 + usage +fi + +if [ -z "${SLT}" ]; then + echo "Did not specify a slot!" >&2 + usage +fi + +if ! binding="$(clevis luks list -d "${DEV}" -s "${SLT}" 2>/dev/null)" \ + || [ -z "${binding}" ]; then + echo "Error retrieving current configuration from ${DEV}:${SLT}" >&2 + exit 1 +fi + +read -r _ pin cfg <<< "${binding}" +# Remove single quotes. +cfg=${cfg//\'} +if ! pretty_cfg="$(jq . <<< "${cfg}")" || [ -z "${pretty_cfg}" ]; then + echo "Error reading the configuration from ${DEV}:${SLT}" >&2 + exit 1 +fi + +if ! TMP="$(mktemp -d)" || [ -z "${TMP}" ]; then + echo "Creating a temporary dir for editing binding failed" >&2 + exit 1 +fi + +trap 'on_exit' EXIT +trap 'on_exit' ERR + +if [ -z "${CFG}" ]; then + CFG_FILE="${TMP}/cfg" + echo "${pretty_cfg}" > "${CFG_FILE}" + if ! edit_cfg "${CFG_FILE}"; then + exit 1 + fi + + if ! new_cfg="$(jq . -S < "${CFG_FILE}")" || [ -z "${new_cfg}" ]; then + echo "Error reading the updated config for ${DEV}:${SLT}" >&2 + exit 1 + fi +else + if ! validate_cfg "${CFG}"; then + echo "Invalid configuration given as parameter with -c" >&2 + exit 1 + fi + new_cfg="$(jq . -S <<< "${CFG}")" +fi + +if [ "${new_cfg}" = "$(jq -S . <<< "${pretty_cfg}")" ] && [ -z "${FRC}" ]; then + echo "No changes detected; exiting" >&2 + exit 1 +fi + +if ! jcfg="$(jose fmt -j- -Oo- <<< "${new_cfg}" 2>/dev/null)" \ + || [ -z "${jcfg}" ]; then + echo "Error preparing the configuration for the binding update" >&2 + exit 1 +fi + +if [ -z "${CFG}" ]; then + printf "Pin: %s\nNew config:\n%s\n" "${pin}" "${new_cfg}" + while true; do + read -r -p \ + "Would you like to proceed with the updated configuration? [ynYN] " \ + ans < /dev/tty + [[ "${ans}" =~ ^[nN]$ ]] && exit 0 + [[ "${ans}" =~ ^[yY]$ ]] && break + done +fi + +echo "Updating binding..." + +# Create new key. +if ! new_pass=$(generate_key "${DEV}"); then + echo "Error generating new key for device ${DEV}" >&2 + exit 1 +fi + +# Reencrypt the new password. +if ! jwe="$(clevis encrypt "${pin}" "${jcfg}" -y <<< "${new_pass}")"; then + echo "Error using pin '${pin}' with config '${jcfg}'" >&2 + exit 1 +fi + +# Backup LUKS header. +if ! clevis_luks_backup_dev "${DEV}" "${TMP}"; then + echo "Error while trying to back up LUKS header from ${DEV}" >&2 + exit 1 +fi + +# Get passphrase. +if ! pt="$(clevis_luks_unlock_device "${DEV}")" \ + || [ -z "${pt}" ]; then + # Unable to retrieve a passphrase from the bindings, so let's query + # the user. + read -r -s -p "Enter existing LUKS password: " pt; echo + # Check if the key is valid. + if ! clevis_luks_is_key_valid "${DEV}" "${pt}"; then + echo "The key provided is not valid for ${DEV}" >&2 + exit 1 + fi +fi + +# Check if we can do the update in-place, i.e., if the key we got is the one +# for the slot we are interested in. +in_place= +if cryptsetup open --test-passphrase --key-slot "${SLT}" "${DEV}" \ + <<< "${pt}"; then + in_place=true +fi + +# Update the key slot with the new key. If we have the key for this slot, +# the change happens in-place. Otherwise, we kill the slot and re-add it. +if [ -n "${in_place}" ]; then + if ! cryptsetup luksChangeKey "${DEV}" --key-slot "${SLT}" \ + <(echo -n "${new_pass}") <<< "${pt}"; then + echo "Error updating LUKS passphrase in ${DEV}:${SLT}" >&2 + clevis_luks_restore_dev "${DEV}" "${TMP}" + exit 1 + fi +else + if ! cryptsetup luksKillSlot --batch-mode "${DEV}" "${SLT}"; then + echo "Error wiping slot ${SLT} from ${DEV}" >&2 + clevis_luks_restore_dev "${DEV}" "${TMP}" + exit 1 + fi + + if ! echo -n "${new_pass}" \ + | cryptsetup luksAddKey --key-slot "${SLT}" \ + --key-file <(echo -n "${pt}") "${DEV}"; then + echo "Error updating LUKS passphrase in ${DEV}:${SLT}." >&2 + clevis_luks_restore_dev "${DEV}" "${TMP}" + exit 1 + fi +fi + +# Update the metadata. +if ! clevis_luks_save_slot "${DEV}" "${SLT}" "${jwe}" "overwrite"; then + echo "Error updating metadata in ${DEV}:${SLT}" >&2 + clevis_luks_restore_dev "${DEV}" "${TMP}" + exit 1 +fi + +# Make sure we can unlock the device with this keyslot. +if ! clevis_luks_unlock_device_by_slot "${DEV}" "${SLT}" >/dev/null \ + 2>/dev/null; then + echo "Invalid configuration detected. Reverting changes." >&2 + clevis_luks_restore_dev "${DEV}" "${TMP}" + exit 1 +fi + +echo "Binding edited successfully." diff --git a/src/luks/clevis-luks-edit.1.adoc b/src/luks/clevis-luks-edit.1.adoc new file mode 100644 index 0000000..454de89 --- /dev/null +++ b/src/luks/clevis-luks-edit.1.adoc @@ -0,0 +1,69 @@ +CLEVIS-LUKS-EDIT(1) +=================== +:doctype: manpage + + +== NAME + +clevis-luks-edit - Edit a binding from a clevis-bound slot in a LUKS device + +== SYNOPSIS + +*clevis luks edit* -d DEV -s SLT [-c CONFIG] + +== OVERVIEW + +The *clevis luks edit* command edits clevis bindings from a LUKS device. +For example: + + clevis luks edit -d /dev/sda1 -s 1 + +== OPTIONS + +* *-d* _DEV_ : + The LUKS device to edit clevis-bound pins + +* *-s* _SLT_ : + The slot to use when editing the clevis binding + +* *-f* : + Proceed with the edit operation even if the config is the same as the current one + +* *-c* _CONFIG_ : + The updated config to use + + +== EXAMPLES + + clevis luks list -d /dev/sda1 + 1: tang '{"url":"addr"}' + +As we can see in the example above, */dev/sda1* has one slots bound, in this case, to a _tang_ pin. + +We can edit this binding by issuing the following command: + + clevis luks edit -d /dev/sda1 -s 1 + +This will open a text editor -- the one set in the $EDITOR environment variable, or _vi_, as a fallback -- with the current +configuration of this binding to be edited. In this case, we should have the following: + + { + "url": "addr" + } + +Once at the editor, we can edit the pin configuration. For _tang_, we could edit the _url_, for instance. After completing the change, +save the file and exit. The updated configuration will be validated for JSON, and if there are no errors, you will be shown the +updated configuration and prompted whether to proceed. + +By proceeding, the binding will be updated. There may be required to provide a valid LUKS passphrase for the device. + +In the second example, we will update the same device and slot, but we will be providing the updated configuration as well: + + clevis luks edit -d /dev/sda1 -s 1 -c '{"url":"new-addr-here"}' + +In this case, the binding update will be done in non-interactive mode, however it also may be required to provide a valud LUKS +passphrase for the device. + +== SEE ALSO + +link:clevis-luks-list.1.adoc[*clevis-luks-list*(1)], diff --git a/src/luks/clevis-luks-regen b/src/luks/clevis-luks-regen index 6071d85..8f32e08 100755 --- a/src/luks/clevis-luks-regen +++ b/src/luks/clevis-luks-regen @@ -41,15 +41,7 @@ function usage_and_exit () { exit "${1}" } -on_exit() { - if [ ! -d "${TMP}" ] || ! rm -rf "${TMP}"; then - echo "Delete temporary files failed!" >&2 - echo "You need to clean up: ${TMP}" >&2 - exit 1 - fi -} - -while getopts ":hfd:s:" o; do +while getopts ":hd:s:" o; do case "$o" in d) DEV="$OPTARG";; h) usage_and_exit 0;; @@ -68,117 +60,22 @@ if [ -z "$SLT" ]; then exit 1 fi -### ---------------------------------------------------------------------- -if ! pin_cfg=$(clevis luks list -d "${DEV}" -s "${SLT}" 2>/dev/null); then - echo "Error obtaining current configuration of device ${DEV}:${SLT}" >&2 +if ! binding="$(clevis luks list -d "${DEV}" -s "${SLT}" 2>/dev/null)" \ + || [ -z "${binding}" ]; then + echo "Error retrieving current configuration from ${DEV}:${SLT}" >&2 exit 1 fi -PIN=$(echo "${pin_cfg}" | awk '{ print $2 }') -CFG=$(echo "${pin_cfg}" | awk '{ print $3 }' | tr -d "'") +read -r _ pin cfg <<< "${binding}" echo "Regenerating with:" -echo "PIN: $PIN" -echo "CONFIG: $CFG" - -trap 'echo "Ignoring CONTROL-C!"' INT TERM - -# Get the existing key. -if ! existing_key=$(clevis luks pass -d "${DEV}" -s "${SLT}" 2>/dev/null); then - # We failed to obtain the passphrase for the slot -- perhaps - # it was rotated? --, so let's request user input. - read -r -s -p "Enter existing LUKS password: " existing_key; echo -fi - -# Check if the key is valid. -if ! cryptsetup open --test-passphrase "${DEV}" <<< "${existing_key}"; then - exit 1 -fi - -# Check if we can do the update in-place, i.e., if the key we got is the one -# for the slot we are interested in. -in_place= -if cryptsetup open --test-passphrase --key-slot "${SLT}" "${DEV}" \ - <<< "${existing_key}"; then - in_place=true -fi - -# Create new key. -if ! new_passphrase=$(generate_key "${DEV}"); then - echo "Error generating new key for device ${DEV}" >&2 - exit 1 -fi - -# Reencrypt the new password. -if ! jwe="$(clevis encrypt "${PIN}" "${CFG}" <<< "${new_passphrase}")"; then - echo "Error using pin '${PIN}' with config '${CFG}'" >&2 - exit 1 -fi - -# Updating the metadata and the actual passphrase are destructive operations, -# hence we will do a backup of the LUKS header and restore it later in case -# we have issues performing these operations. -if ! TMP="$(mktemp -d)"; then - echo "Creating a temporary dir for device backup/restore failed!" >&2 - exit 1 -fi -trap 'on_exit' EXIT - -# Backup LUKS header. -if ! clevis_luks_backup_dev "${DEV}" "${TMP}"; then - echo "Error while trying to back up LUKS header from ${DEV}" >&2 - exit 1 -fi - -restore_device() { - local DEV="${1}" - local TMP="${2}" - - if ! clevis_luks_restore_dev "${DEV}" "${TMP}"; then - echo "Error while trying to restore LUKS header from ${DEV}." >&2 - else - echo "LUKS header restored successfully." >&2 - fi -} - -# Update the key slot with the new key. If we have the key for this slot, -# the change happens in-place. Otherwise, we kill the slot and re-add it. -if [ -n "${in_place}" ]; then - if ! cryptsetup luksChangeKey "${DEV}" --key-slot "${SLT}" \ - <(echo -n "${new_passphrase}") <<< "${existing_key}"; then - echo "Error updating LUKS passphrase in ${DEV}:${SLT}" >&2 - restore_device "${DEV}" "${TMP}" - exit 1 - fi -else - if ! cryptsetup luksKillSlot --batch-mode "${DEV}" "${SLT}"; then - echo "Error wiping slot ${SLT} from ${DEV}" >&2 - restore_device "${DEV}" "${TMP}" - exit 1 - fi - - if ! echo -n "${new_passphrase}" \ - | cryptsetup luksAddKey --key-slot "${SLT}" \ - --key-file <(echo -n "${existing_key}") "${DEV}"; then - echo "Error updating LUKS passphrase in ${DEV}:${SLT}." >&2 - restore_device "${DEV}" "${TMP}" - exit 1 - fi -fi - -# Update the metadata. -if ! clevis_luks_save_slot "${DEV}" "${SLT}" "${jwe}" "overwrite"; then - echo "Error updating metadata in ${DEV}:${SLT}" >&2 - restore_device "${DEV}" "${TMP}" - exit 1 -fi +echo "PIN: ${pin}" +echo "CONFIG: ${cfg}" -# Now make sure that we can unlock this device after the change. -# If we can't, undo the changes. -if ! cryptsetup open --test-passphrase --key-slot "${SLT}" "${DEV}" 2>/dev/null \ - <<< "$(clevis luks pass -d "${DEV}" -s "${SLT}" 2>/dev/null)"; then - echo "Invalid configuration detected after rebinding. Reverting changes." - restore_device "${DEV}" "${TMP}" +# Remove single quotes. +cfg=${cfg//\'} +if ! clevis luks edit -f -d "${DEV}" -s "${SLT}" -c "${cfg}" >/dev/null; then + echo "Error rotating keys in ${DEV}:${SLT}" >&2 exit 1 fi diff --git a/src/luks/meson.build b/src/luks/meson.build index ee588c3..ba02bd9 100644 --- a/src/luks/meson.build +++ b/src/luks/meson.build @@ -54,6 +54,9 @@ if libcryptsetup.found() and luksmeta.found() and pwmake.found() bins += join_paths(meson.current_source_dir(), 'clevis-luks-report-sss') bins += join_paths(meson.current_source_dir(), 'clevis-luks-report-tang') mans += join_paths(meson.current_source_dir(), 'clevis-luks-report.1') + + bins += join_paths(meson.current_source_dir(), 'clevis-luks-edit') + mans += join_paths(meson.current_source_dir(), 'clevis-luks-edit.1') else warning('Will not install LUKS support due to missing dependencies!') endif diff --git a/src/luks/tests/edit-tang-luks1 b/src/luks/tests/edit-tang-luks1 new file mode 100755 index 0000000..3d42d68 --- /dev/null +++ b/src/luks/tests/edit-tang-luks1 @@ -0,0 +1,106 @@ +#!/bin/bash -ex +# vim: set ts=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: +# +# Copyright (c) 2020 Red Hat, Inc. +# Author: Sergio Correia +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +TEST=$(basename "${0}") +. tests-common-functions + +. clevis-luks-common-functions + +on_exit() { + local d + for d in "${TMP}" "${TMP2}"; do + [ ! -d "${d}" ] && continue + tang_stop "${d}" + rm -rf "${d}" + done +} + +trap 'on_exit' EXIT +trap 'on_exit' ERR + +TMP="$(mktemp -d)" + +port=$(get_random_port) +tang_run "${TMP}" "${port}" & +tang_wait_until_ready "${port}" + +url="http://${TANG_HOST}:${port}" + +cfg=$(printf '{"url":"%s"}' "${url}") + +# LUKS1. +DEV="${TMP}/luks1-device" +new_device "luks1" "${DEV}" + +if ! clevis luks bind -y -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then + error "${TEST}: Bind should have succeeded." +fi + +# Now let's try to change the config but using the same one we already have. +if clevis luks edit -d "${DEV}" -s 1 -c "${cfg}"; then + error "${TEST}: edit should not have succeeded because the config is the same." +fi + +# And now, just a broken config. +new_cfg=$(printf '{"url&:"%s"}' "${url}") +if clevis luks edit -d "${DEV}" -s 1 -c "${new_cfg}"; then + error "${TEST}: edit should have failed because of invalid JSON" +fi + +# Now let's have another tang instance running and change the config to use +# the new one. +port2=$(get_random_port) +TMP2="$(mktemp -d)" +tang_run "${TMP2}" "${port2}" & +tang_wait_until_ready "${port2}" +new_url="http://${TANG_HOST}:${port2}" +new_cfg=$(printf '{"url":"%s"}' "${new_url}") + +if ! clevis luks edit -d "${DEV}" -s 1 -c "${new_cfg}"; then + error "${TEST}: edit should have succeeded." +fi + +# And now let's use sss and start with a single tang server, then add a second +# one. +new_device "luks1" "${DEV}" +cfg=$(printf '{"t":1,"pins":{"tang":[{"url":"%s"}]}}' "${url}") +if ! clevis luks bind -y -d "${DEV}" sss "${cfg}" <<< "${DEFAULT_PASS}"; then + error "${TEST}: Bind should have succeeded." +fi +new_cfg=$(printf '{"t":1,"pins":{"tang":[{"url":"%s"},{"url":"%s"}]}}' \ + "${url}" "${new_url}") + +if ! clevis luks edit -d "${DEV}" -s 1 -c "${new_cfg}"; then + error "${TEST}: edit should have succeeded and added a new tang server" +fi + +# Now let's change the threshold to 2. +new_cfg=$(printf '{"t":2,"pins":{"tang":[{"url":"%s"},{"url":"%s"}]}}' \ + "${url}" "${new_url}") + +if ! clevis luks edit -d "${DEV}" -s 1 -c "${new_cfg}"; then + error "${TEST}: edit should have succeeded and added a new tang server" +fi + +# And finally, let's try a broken config, with a wrong threshold. +new_cfg=$(printf '{"t":3,"pins":{"tang":[{"url":"%s"},{"url":"%s"}]}}' \ + "${url}" "${new_url}") +if clevis luks edit -d "${DEV}" -s 1 -c "${new_cfg}"; then + error "${TEST}: edit should have failed because threshold > number of servers" +fi diff --git a/src/luks/tests/edit-tang-luks2 b/src/luks/tests/edit-tang-luks2 new file mode 100755 index 0000000..9000053 --- /dev/null +++ b/src/luks/tests/edit-tang-luks2 @@ -0,0 +1,106 @@ +#!/bin/bash -ex +# vim: set ts=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: +# +# Copyright (c) 2020 Red Hat, Inc. +# Author: Sergio Correia +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +TEST=$(basename "${0}") +. tests-common-functions + +. clevis-luks-common-functions + +on_exit() { + local d + for d in "${TMP}" "${TMP2}"; do + [ ! -d "${d}" ] && continue + tang_stop "${d}" + rm -rf "${d}" + done +} + +trap 'on_exit' EXIT +trap 'on_exit' ERR + +TMP="$(mktemp -d)" + +port=$(get_random_port) +tang_run "${TMP}" "${port}" & +tang_wait_until_ready "${port}" + +url="http://${TANG_HOST}:${port}" + +cfg=$(printf '{"url":"%s"}' "${url}") + +# LUKS2. +DEV="${TMP}/luks2-device" +new_device "luks2" "${DEV}" + +if ! clevis luks bind -y -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then + error "${TEST}: Bind should have succeeded." +fi + +# Now let's try to change the config but using the same one we already have. +if clevis luks edit -d "${DEV}" -s 1 -c "${cfg}"; then + error "${TEST}: edit should not have succeeded because the config is the same." +fi + +# And now, just a broken config. +new_cfg=$(printf '{"url&:"%s"}' "${url}") +if clevis luks edit -d "${DEV}" -s 1 -c "${new_cfg}"; then + error "${TEST}: edit should have failed because of invalid JSON" +fi + +# Now let's have another tang instance running and change the config to use +# the new one. +port2=$(get_random_port) +TMP2="$(mktemp -d)" +tang_run "${TMP2}" "${port2}" & +tang_wait_until_ready "${port2}" +new_url="http://${TANG_HOST}:${port2}" +new_cfg=$(printf '{"url":"%s"}' "${new_url}") + +if ! clevis luks edit -d "${DEV}" -s 1 -c "${new_cfg}"; then + error "${TEST}: edit should have succeeded." +fi + +# And now let's use sss and start with a single tang server, then add a second +# one. +new_device "luks2" "${DEV}" +cfg=$(printf '{"t":1,"pins":{"tang":[{"url":"%s"}]}}' "${url}") +if ! clevis luks bind -y -d "${DEV}" sss "${cfg}" <<< "${DEFAULT_PASS}"; then + error "${TEST}: Bind should have succeeded." +fi +new_cfg=$(printf '{"t":1,"pins":{"tang":[{"url":"%s"},{"url":"%s"}]}}' \ + "${url}" "${new_url}") + +if ! clevis luks edit -d "${DEV}" -s 1 -c "${new_cfg}"; then + error "${TEST}: edit should have succeeded and added a new tang server" +fi + +# Now let's change the threshold to 2. +new_cfg=$(printf '{"t":2,"pins":{"tang":[{"url":"%s"},{"url":"%s"}]}}' \ + "${url}" "${new_url}") + +if ! clevis luks edit -d "${DEV}" -s 1 -c "${new_cfg}"; then + error "${TEST}: edit should have succeeded and added a new tang server" +fi + +# And finally, let's try a broken config, with a wrong threshold. +new_cfg=$(printf '{"t":3,"pins":{"tang":[{"url":"%s"},{"url":"%s"}]}}' \ + "${url}" "${new_url}") +if clevis luks edit -d "${DEV}" -s 1 -c "${new_cfg}"; then + error "${TEST}: edit should have failed because threshold > number of servers" +fi diff --git a/src/luks/tests/meson.build b/src/luks/tests/meson.build index 4795488..67fa597 100644 --- a/src/luks/tests/meson.build +++ b/src/luks/tests/meson.build @@ -4,8 +4,9 @@ actv = find_program( required: false ) -# We use jq for comparing the pin config in the clevis luks list tests. -jq = find_program('jq', required: false) +# We use jq for comparing the pin config in the clevis luks list tests +# and also in clevis luks edit. +jq = find_program('jq', required: true) # we use systemd-socket-activate for running test tang servers. actv = find_program( @@ -74,19 +75,16 @@ test('bind-pass-with-newline', find_program('bind-pass-with-newline-luks1'), env test('bind-pass-with-newline-keyfile', find_program('bind-pass-with-newline-keyfile-luks1'), env: env) # Bug #70. test('bind-already-used-luksmeta-slot', find_program('bind-already-used-luksmeta-slot'), env: env, timeout: 60) - -if jq.found() - test('list-recursive-luks1', find_program('list-recursive-luks1'), env: env) - test('list-tang-luks1', find_program('list-tang-luks1'), env: env) - test('list-sss-tang-luks1', find_program('list-sss-tang-luks1'), env: env) -else - warning('Will not run "clevis luks list" tests due to missing jq dependency') -endif +test('list-recursive-luks1', find_program('list-recursive-luks1'), env: env) +test('list-tang-luks1', find_program('list-tang-luks1'), env: env) +test('list-sss-tang-luks1', find_program('list-sss-tang-luks1'), env: env) if has_tang test('unlock-tang-luks1', find_program('unlock-tang-luks1'), env: env, timeout: 90) test('assume-yes-luks1', find_program('assume-yes-luks1'), env: env) + test('edit-tang-luks1', find_program('edit-tang-luks1'), env: env, timeout: 90) endif + test('pass-tang-luks1', find_program('pass-tang-luks1'), env: env) test('backup-restore-luks1', find_program('backup-restore-luks1'), env: env) test('regen-inplace-luks1', find_program('regen-inplace-luks1'), env: env, timeout: 90) @@ -100,16 +98,14 @@ if luksmeta_data.get('OLD_CRYPTSETUP') == '0' test('bind-luks2', find_program('bind-luks2'), env: env, timeout: 60) test('unbind-unbound-slot-luks2', find_program('unbind-unbound-slot-luks2'), env: env) test('unbind-luks2', find_program('unbind-luks2'), env: env, timeout: 60) - - if jq.found() - test('list-recursive-luks2', find_program('list-recursive-luks2'), env: env, timeout: 60) - test('list-tang-luks2', find_program('list-tang-luks2'), env: env, timeout: 60) - test('list-sss-tang-luks2', find_program('list-sss-tang-luks2'), env: env, timeout: 60) - endif + test('list-recursive-luks2', find_program('list-recursive-luks2'), env: env, timeout: 60) + test('list-tang-luks2', find_program('list-tang-luks2'), env: env, timeout: 60) + test('list-sss-tang-luks2', find_program('list-sss-tang-luks2'), env: env, timeout: 60) if has_tang test('unlock-tang-luks2', find_program('unlock-tang-luks2'), env: env, timeout: 120) test('assume-yes-luks2', find_program('assume-yes-luks2'), env: env, timeout: 60) + test('edit-tang-luks2', find_program('edit-tang-luks2'), env: env, timeout: 120) endif test('pass-tang-luks2', find_program('pass-tang-luks2'), env: env, timeout: 60) test('backup-restore-luks2', find_program('backup-restore-luks2'), env:env, timeout: 90) diff --git a/src/luks/tests/regen-inplace-luks1 b/src/luks/tests/regen-inplace-luks1 index 3a42ced..32072c4 100755 --- a/src/luks/tests/regen-inplace-luks1 +++ b/src/luks/tests/regen-inplace-luks1 @@ -76,7 +76,7 @@ fi old_key=$(clevis luks pass -d "${DEV}" -s "${SLT}") # Now let's try regen. -if ! clevis_regen "${DEV}" "${SLT}" "${DEFAULT_PASS}"; then +if ! clevis luks regen -d "${DEV}" -s "${SLT}"; then error "${TEST}: clevis luks regen failed" fi diff --git a/src/luks/tests/regen-inplace-luks2 b/src/luks/tests/regen-inplace-luks2 index 1cb7a29..d1bc182 100755 --- a/src/luks/tests/regen-inplace-luks2 +++ b/src/luks/tests/regen-inplace-luks2 @@ -77,7 +77,7 @@ fi old_key=$(clevis luks pass -d "${DEV}" -s "${SLT}") # Now let's try regen. -if ! clevis_regen "${DEV}" "${SLT}" "${DEFAULT_PASS}"; then +if ! clevis luks regen -d "${DEV}" -s "${SLT}"; then error "${TEST}: clevis luks regen failed" fi diff --git a/src/luks/tests/regen-not-inplace-luks1 b/src/luks/tests/regen-not-inplace-luks1 index 1b65ca7..fba61dc 100755 --- a/src/luks/tests/regen-not-inplace-luks1 +++ b/src/luks/tests/regen-not-inplace-luks1 @@ -79,7 +79,7 @@ if [ "${enabled}" -ne 2 ]; then fi # Now let's try regen. -if ! clevis_regen "${DEV}" "${SLT}" "${DEFAULT_PASS}"; then +if ! clevis luks regen -d "${DEV}" -s "${SLT}" <<< "${DEFAULT_PASS}"; then error "${TEST}: clevis luks regen failed" fi diff --git a/src/luks/tests/regen-not-inplace-luks2 b/src/luks/tests/regen-not-inplace-luks2 index dc91449..6e3b012 100755 --- a/src/luks/tests/regen-not-inplace-luks2 +++ b/src/luks/tests/regen-not-inplace-luks2 @@ -80,7 +80,7 @@ if [ "${enabled}" -ne 2 ]; then fi # Now let's try regen. -if ! clevis_regen "${DEV}" "${SLT}" "${DEFAULT_PASS}"; then +if ! clevis luks regen -d "${DEV}" -s "${SLT}" <<< "${DEFAULT_PASS}"; then error "${TEST}: clevis luks regen failed" fi diff --git a/src/luks/tests/tests-common-functions.in b/src/luks/tests/tests-common-functions.in index 6101f28..7b3fdad 100755 --- a/src/luks/tests/tests-common-functions.in +++ b/src/luks/tests/tests-common-functions.in @@ -229,31 +229,5 @@ tang_get_adv() { curl -o "${adv}" http://"${TANG_HOST}":"${port}"/adv } -# Regenerate binding. -clevis_regen() { - local DEV="${1}" - local SLT="${2}" - local PASS="${3}" - - expect -d << CLEVIS_REGEN - set timeout 120 - spawn sh -c "clevis luks regen -d $DEV -s $SLT" - expect { - "Enter existing LUKS password" { - send "$PASS\r" - exp_continue - } - "Do you wish to trust these keys" { - send "y\r" - exp_continue - } - expect eof - wait - } -CLEVIS_REGEN - ret=$? - return "${ret}" -} - export TANG_HOST=127.0.0.1 export DEFAULT_PASS='just-some-test-password-here' diff --git a/src/luks/tests/unlock-tang-luks1 b/src/luks/tests/unlock-tang-luks1 index 841ba01..6ede47b 100755 --- a/src/luks/tests/unlock-tang-luks1 +++ b/src/luks/tests/unlock-tang-luks1 @@ -31,6 +31,10 @@ on_exit() { trap 'on_exit' EXIT trap 'on_exit' ERR +# LUKS1. +DEV="${TMP}/luks1-device" +new_device "luks1" "${DEV}" + TMP="$(mktemp -d)" port=$(get_random_port) @@ -43,9 +47,6 @@ tang_get_adv "${port}" "${adv}" cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv") -# LUKS1. -DEV="${TMP}/luks1-device" -new_device "luks1" "${DEV}" if ! clevis luks bind -f -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then error "${TEST}: Bind should have succeeded." diff --git a/src/luks/tests/unlock-tang-luks2 b/src/luks/tests/unlock-tang-luks2 index 81822fb..b32c5aa 100755 --- a/src/luks/tests/unlock-tang-luks2 +++ b/src/luks/tests/unlock-tang-luks2 @@ -31,6 +31,10 @@ on_exit() { trap 'on_exit' EXIT trap 'on_exit' ERR +# LUKS2. +DEV="${TMP}/luks2-device" +new_device "luks2" "${DEV}" + TMP="$(mktemp -d)" port=$(get_random_port) @@ -43,10 +47,6 @@ tang_get_adv "${port}" "${adv}" cfg=$(printf '{"url":"%s","adv":"%s"}' "$url" "$adv") -# LUKS2. -DEV="${TMP}/luks2-device" -new_device "luks2" "${DEV}" - if ! clevis luks bind -f -d "${DEV}" tang "${cfg}" <<< "${DEFAULT_PASS}"; then error "${TEST}: Bind should have succeeded." fi -- 2.18.4