diff --git a/.fmf/version b/.fmf/version new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/.fmf/version @@ -0,0 +1 @@ +1 diff --git a/.gitignore b/.gitignore index 06aaee1..92de226 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,10 @@ -volume_key-0.3.3.tar.xz +/.project +/volume_key-0.3.3.tar.xz /volume_key-0.3.4.tar.xz /volume_key-0.3.5.tar.xz /volume_key-0.3.6.tar.xz /volume_key-0.3.7.tar.xz /volume_key-0.3.8.tar.xz /volume_key-0.3.9.tar.xz +/volume_key-0.3.10.tar.xz +/volume_key-0.3.12.tar.xz diff --git a/plans/ci.fmf b/plans/ci.fmf new file mode 100644 index 0000000..c1627f9 --- /dev/null +++ b/plans/ci.fmf @@ -0,0 +1,5 @@ +summary: Basic smoke test +discover: + how: fmf +execute: + how: tmt diff --git a/sources b/sources index 9741a26..5250147 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -a2d14931177c660e1f3ebbcf5f47d8e2 volume_key-0.3.9.tar.xz +SHA512 (volume_key-0.3.12.tar.xz) = d056154c9b9d23e4eb661946dd59ed97e116903a3afcff9d9e29258408082f33dcbb69958724143f6bf191a3da488a03b6c02af287790990ed6459e29d66553c diff --git a/tests/Sanity/basic-sanity/main.fmf b/tests/Sanity/basic-sanity/main.fmf new file mode 100644 index 0000000..e87514e --- /dev/null +++ b/tests/Sanity/basic-sanity/main.fmf @@ -0,0 +1,16 @@ +--- +component: volume_key +summary: basic sanity test for volume_key utility +contact: + - Jan Blazek + - Jiri Kucera +description: basic sanity test for volume_key utility +require: + - volume_key + - cryptsetup + - nss-tools + - expect + - tcllib +framework: beakerlib +test: ./runtest.sh +duration: 10m diff --git a/tests/Sanity/basic-sanity/runtest.sh b/tests/Sanity/basic-sanity/runtest.sh new file mode 100755 index 0000000..23f72d5 --- /dev/null +++ b/tests/Sanity/basic-sanity/runtest.sh @@ -0,0 +1,353 @@ +#!/bin/bash +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# File: ./tests/Sanity/basic-sanity/runtest.sh +# Author: Jan Blazek +# Jiri Kucera +# Brief: Basic sanity test for volume_key utility +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2017-2020 Red Hat, Inc. +# +# 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 2 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/. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +_TESTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" + +# Include Beaker environment +. /usr/share/beakerlib/beakerlib.sh || exit 1 + +# Include utils +. ${_TESTDIR}/../../utils/utils.sh || { + echo "${_TESTDIR}/../../utils/utils.sh cannot be included." >&2 + exit 1 +} + +# Include test settings: +. ${_TESTDIR}/../../settings/environment.sh || { + errmsg "${_TESTDIR}/../../settings/environment.sh cannot be included." + exit 1 +} + +TEST="${TEST:-/CoreOS/volume_key/tests/Sanity/basic-sanity}" +TESTVERSION="${TESTVERSION:-1.0}" +PACKAGES="${PACKAGES:-volume_key}" +REQUIRES="${REQUIRES:-cryptsetup nss-tools expect tcllib}" + +_GNUPG_DIR="${HOME}/.gnupg" +_IMAGE="${_IMAGE:-image}" +_IMAGE_IMG="${_IMAGE}.img" +_PACKET="${_PACKET:-packet}" +_NEW_PACKET="${_NEW_PACKET:-new-packet}" +_PACKET_ASYM="${_PACKET_ASYM:-packet-asym}" +_NEW_PACKET_ASYM="${_NEW_PACKET_ASYM:-new-packet-asym}" +_ESCROW="${_ESCROW:-escrow}" +_ESCROW_PEM="${_ESCROW}.pem" +_NSSDB="${_NSSDB:-nssdb}" + +_LUKS_PASS="${_LUKS_PASS:-lukspass}" +_PACKET_PASS="${_PACKET_PASS:-packetpass}" +_NEW_PACKET_PASS="${_NEW_PACKET_PASS:-newpacketpass}" +_CERT_PASS="${_CERT_PASS:-certpass}" +_NEW_LUKS_PASS="${_NEW_LUKS_PASS:-newlukspass}" +_NEW_LUKS_PASS_ASYM="${_NEW_LUKS_PASS_ASYM:-newlukspass-asym}" + +_TEMP_DIR="" +_VOLUME="" + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ Setup +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +function Setup() { + LANG=C + LC_ALL=C + export EXPECT_SCRIPTS_PATH="${SCRIPTDIR}" + + rlAssertRpm --all || return $? + + if [[ -d "${_GNUPG_DIR}" ]]; then + rlFileBackup "${_GNUPG_DIR}" || return $? + AtCleanup rlFileRestore + else + AtCleanup Cleanup_RemoveGnuPG + fi + + rlRun CreateTemporaryDirectory || return $? + _TEMP_DIR="${Result}" + AtCleanup Cleanup_RemoveTemporaryDirectory + + PushDir "${_TEMP_DIR}" || return $? + AtCleanup PopDir + + CreateEncryptedVolume \ + --image "${_IMAGE_IMG}" \ + --password "${_LUKS_PASS}" \ + ${USE_LOSETUP:+--with-losetup} \ + || return $? + _VOLUME="${Result}" + AtCleanup Cleanup_DestroyVolume + + CreateCertificate --name "${_ESCROW}" || return $? + + SetupNSSDatabase --dest "${_TEMP_DIR}/${_NSSDB}" \ + --cert-name "${_ESCROW}" --password "${_CERT_PASS}" +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ Cleanup +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +function Cleanup_RemoveGnuPG() { + RunCmd rm -rfv "${_GNUPG_DIR}" +} + +function Cleanup_RemoveTemporaryDirectory() { + RunCmd rm -rfv "${_TEMP_DIR}" +} + +function Cleanup_DestroyVolume() { + if [[ "${USE_LOSETUP:+yes}" == "yes" ]]; then + RunCmd losetup -d "${_VOLUME}" + fi +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ Tests +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +function TestVolumeKeySave() { + RunCmdViaExpect + Command volume_key + Command --save "${_VOLUME}" --output-format=passphrase -o "${_PACKET}" + Input --lukspass "${_LUKS_PASS}" + Input --packetpass "${_PACKET_PASS}" + Input ${USING_PINENTRY:+--pinentry} + FinishRun +} +AddTest TestVolumeKeySave "save" + +function TestVolumeKeyRestore() { + rlAssertExists "${_PACKET}" || return $? + + ClearGpgAgentsCache + RunCmdViaExpect + Command volume_key --restore "${_VOLUME}" "${_PACKET}" + Input --lukspass "${_NEW_LUKS_PASS}" + Input --packetpass "${_PACKET_PASS}" + Input ${USING_PINENTRY:+--pinentry} + FinishRun || return $? + + RunCmdViaExpect + Command cryptsetup luksOpen "${_VOLUME}" "${_IMAGE}" + Input --password "${_NEW_LUKS_PASS}" + FinishRun || return $? + + RunCmd ls -la /dev/mapper + rlAssertExists "/dev/mapper/${_IMAGE}" + + RunCmd cryptsetup luksClose "${_IMAGE}" +} +AddTest TestVolumeKeyRestore "restore" + +function TestVolumeKeySetupVolume() { + rlAssertExists "${_PACKET}" || return $? + + ClearGpgAgentsCache + RunCmdViaExpect + Command volume_key --setup-volume "${_VOLUME}" "${_PACKET}" "${_IMAGE}" + Input --packetpass "${_PACKET_PASS}" + Input ${USING_PINENTRY:+--pinentry} + FinishRun || return $? + + RunCmd ls -la /dev/mapper + rlAssertExists "/dev/mapper/${_IMAGE}" + + RunCmd cryptsetup luksClose "${_IMAGE}" +} +AddTest TestVolumeKeySetupVolume "setup-volume" + +function TestVolumeKeyReencrypt() { + rlAssertExists "${_PACKET}" || return $? + + ClearGpgAgentsCache + RunCmdViaExpect + Command volume_key --reencrypt "${_PACKET}" -o "${_NEW_PACKET}" + Input --packetpass "${_PACKET_PASS}" + Input --newpacketpass "${_NEW_PACKET_PASS}" + Input ${USING_PINENTRY:+--pinentry} + FinishRun || return $? + + ClearGpgAgentsCache + RunCmdViaExpect + Command volume_key --setup-volume "${_VOLUME}" "${_NEW_PACKET}" "${_IMAGE}" + Input --packetpass "${_NEW_PACKET_PASS}" + Input ${USING_PINENTRY:+--pinentry} + FinishRun || return $? + + RunCmd ls -la /dev/mapper + rlAssertExists "/dev/mapper/${_IMAGE}" + + RunCmd cryptsetup luksClose "${_IMAGE}" +} +AddTest TestVolumeKeyReencrypt "reencrypt" + +function TestVolumeKeyDump() { + local __uuid="" + + rlAssertExists "${_PACKET}" || return $? + + ClearGpgAgentsCache + RunCmdViaExpect + rlRunOptions -s + Command volume_key --dump "${_PACKET}" + Input --packetpass "${_PACKET_PASS}" + Input ${USING_PINENTRY:+--pinentry} + FinishRun || return $? + + __uuid="$(blkid -o value -s UUID "${_VOLUME}")" + + rlAssertGrep '^Packet format:\W+Passphrase-encrypted' "${rlRun_LOG}" -E + rlAssertGrep '^Volume format:\W+crypt_LUKS' "${rlRun_LOG}" -E + rlAssertGrep "^Volume UUID:\W+${__uuid}" "${rlRun_LOG}" -E + rlAssertGrep "^Volume path:\W+${_VOLUME}" "${rlRun_LOG}" -E +} +AddTest TestVolumeKeyDump "dump" + +function TestVolumeKeySecrets() { + rlAssertExists "${_PACKET}" || return $? + + ClearGpgAgentsCache + RunCmdViaExpect + rlRunOptions -s + Command volume_key --secrets "${_PACKET}" + Input --packetpass "${_PACKET_PASS}" + Input ${USING_PINENTRY:+--pinentry} + FinishRun || return $? + + rlAssertGrep 'Data encryption key:\W+[0-9A-F]+' "${rlRun_LOG}" -E +} +AddTest TestVolumeKeySecrets "secrets" + +function TestVolumeKeySaveAsymmetric() { + RunCmdViaExpect + Command volume_key + Command --save "${_VOLUME}" --output-format=asymmetric + Command -c "${_ESCROW_PEM}" -o "${_PACKET_ASYM}" + Input --lukspass "${_LUKS_PASS}" + FinishRun +} +AddTest TestVolumeKeySaveAsymmetric "save asymmetric" + +function TestVolumeKeyRestoreAsymmetric() { + rlAssertExists "${_PACKET_ASYM}" || return $? + + RunCmdViaExpect + Command volume_key --restore "${_VOLUME}" "${_PACKET_ASYM}" -d "${_NSSDB}" + Input --certpass "${_CERT_PASS}" + Input --lukspass "${_NEW_LUKS_PASS_ASYM}" + FinishRun || return $? + + RunCmdViaExpect + Command cryptsetup luksOpen "${_VOLUME}" "${_IMAGE}" + Input --password "${_NEW_LUKS_PASS_ASYM}" + FinishRun || return $? + + RunCmd ls -la /dev/mapper + rlAssertExists "/dev/mapper/${_IMAGE}" + + RunCmd cryptsetup luksClose "${_IMAGE}" +} +AddTest TestVolumeKeyRestoreAsymmetric "restore asymmetric" + +function TestVolumeKeySetupVolumeAsymmetric() { + rlAssertExists "${_PACKET_ASYM}" || return $? + + RunCmdViaExpect + Command volume_key + Command --setup-volume "${_VOLUME}" "${_PACKET_ASYM}" "${_IMAGE}" + Command -d "${_NSSDB}" + Input --certpass "${_CERT_PASS}" + FinishRun || return $? + + RunCmd ls -la /dev/mapper + rlAssertExists "/dev/mapper/${_IMAGE}" + + RunCmd cryptsetup luksClose "${_IMAGE}" +} +AddTest TestVolumeKeySetupVolumeAsymmetric "setup-volume asymmetric" + +function TestVolumeKeyReencryptAsymmetric() { + rlAssertExists "${_PACKET_ASYM}" || return $? + + ClearGpgAgentsCache + RunCmdViaExpect + Command volume_key --reencrypt + Command -d "${_NSSDB}" "${_PACKET_ASYM}" -o "${_NEW_PACKET_ASYM}" + Input --certpass "${_CERT_PASS}" + Input --newpacketpass "${_NEW_PACKET_PASS}" + Input ${USING_PINENTRY:+--pinentry} + FinishRun || return $? + + ClearGpgAgentsCache + RunCmdViaExpect + Command volume_key + Command --setup-volume "${_VOLUME}" "${_NEW_PACKET_ASYM}" "${_IMAGE}" + Input --packetpass "${_NEW_PACKET_PASS}" + Input ${USING_PINENTRY:+--pinentry} + FinishRun || return $? + + RunCmd ls -la /dev/mapper + rlAssertExists "/dev/mapper/${_IMAGE}" + + RunCmd cryptsetup luksClose "${_IMAGE}" +} +AddTest TestVolumeKeyReencryptAsymmetric "reencrypt asymmetric" + +function TestVolumeKeyDumpAsymmetric() { + local __uuid="" + + rlAssertExists "${_PACKET_ASYM}" || return $? + + RunCmdViaExpect + rlRunOptions -s + Command volume_key --dump "${_PACKET_ASYM}" -d "${_NSSDB}" + Input --certpass "${_CERT_PASS}" + FinishRun || return $? + + __uuid="$(blkid -o value -s UUID "${_VOLUME}")" + + rlAssertGrep '^Packet format:\W+Public key-encrypted' "${rlRun_LOG}" -E + rlAssertGrep '^Volume format:\W+crypt_LUKS' "${rlRun_LOG}" -E + rlAssertGrep "^Volume UUID:\W+${__uuid}" "${rlRun_LOG}" -E + rlAssertGrep "^Volume path:\W+${_VOLUME}" "${rlRun_LOG}" -E +} +AddTest TestVolumeKeyDumpAsymmetric "dump asymmetric" + +function TestVolumeKeySecretsAsymmetric() { + rlAssertExists "${_PACKET_ASYM}" || return $? + + RunCmdViaExpect + rlRunOptions -s + Command volume_key --secrets "${_PACKET_ASYM}" -d "${_NSSDB}" + Input --certpass "${_CERT_PASS}" + FinishRun || return $? + + rlAssertGrep 'Data encryption key:\W+[0-9A-F]+' "${rlRun_LOG}" -E +} +AddTest TestVolumeKeySecretsAsymmetric "secrets asymmetric" + +RunTest diff --git a/tests/settings/environment.sh b/tests/settings/environment.sh new file mode 100644 index 0000000..b11b9da --- /dev/null +++ b/tests/settings/environment.sh @@ -0,0 +1,28 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# File: ./tests/settings/environment.sh +# Author: Jiri Kucera +# Brief: Environment variables with distribution specific values +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2020 Red Hat, Inc. +# +# 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 2 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/. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +export USE_LOSETUP="${USE_LOSETUP:-}" +export USING_PINENTRY="${USING_PINENTRY:-}" +export CLEAR_GPG_AGENTS_CACHE="${CLEAR_GPG_AGENTS_CACHE:-1}" diff --git a/tests/utils/common.tcl b/tests/utils/common.tcl new file mode 100644 index 0000000..f9362b5 --- /dev/null +++ b/tests/utils/common.tcl @@ -0,0 +1,39 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# File: ./tests/utils/common.tcl +# Author: Jiri Kucera +# Brief: Common utilities for expect scripts +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2020 Red Hat, Inc. +# +# 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 2 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/. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +proc oneof {l1 l2} { + foreach x $l1 { + if {$x in $l2} { + return 1 + } + } + return 0 +} + +proc verify_password {password} { + if {$password == ""} { + return -code error "password required!" + } +} diff --git a/tests/utils/cryptsetup.exp b/tests/utils/cryptsetup.exp new file mode 100755 index 0000000..8da7496 --- /dev/null +++ b/tests/utils/cryptsetup.exp @@ -0,0 +1,66 @@ +#!/usr/bin/expect -f +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# File: ./tests/utils/cryptsetup.exp +# Author: Jiri Kucera +# Brief: Expect wrapper around cryptsetup +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2020 Red Hat, Inc. +# +# 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 2 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/. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +package require cmdline + +source [file join [file dirname [info script]] "common.tcl"] + +set options { + {password.arg "" "Password required by some cryptsetup actions"} +} + +set usage "\[options\] -- cryptsetup_options\noptions:" + +if {[catch { + array set params [::cmdline::getoptions argv $options $usage] +} result]} { + if {$::errorCode eq {CMDLINE USAGE}} { + puts $result + exit 0 + } + puts $::errorCode + puts $::errorInfo + exit 1 +} + +set password $params(password) + +eval spawn cryptsetup $::argv +if {"luksFormat" in $::argv} { + verify_password $password + expect -re "^Are you sure.*:" + send -- "YES\r" + expect -re "Enter( LUKS)? passphrase.*" + send -- "$password\r" + expect -re "Verify passphrase.*" + send -- "$password\r" + expect eof +} elseif {"luksOpen" in $::argv} { + verify_password $password + expect -re "Enter passphrase for.*" + send -- "$password\r" + expect eof +} diff --git a/tests/utils/rlwrap.sh b/tests/utils/rlwrap.sh new file mode 100644 index 0000000..dd9b97b --- /dev/null +++ b/tests/utils/rlwrap.sh @@ -0,0 +1,508 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# File: ./tests/utils/rlwrap.sh +# Author: Jiri Kucera +# Brief: Wrapper around beakerlib (rlX) functions +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2020 Red Hat, Inc. +# +# 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 2 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/. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +## +# Result, ResultA, ResultB +# +# If a function has an output, it is stored inside these variables. +Result="" +ResultA="" +ResultB="" + +# Internal variables used by RunCmdViaExpect family of functions. +_rlwrap_expect_script_path="" +declare -ag _rlwrap_rlRun_options=() +declare -ag _rlwrap_expect_options=() +_rlwrap_expect_script="" +_rlwrap_expect_script_scommand="" +declare -ag _rlwrap_expect_script_command_args=() +declare -ag _rlwrap_expect_script_input_args=() +_rlwrap_rlRun_status="0" +_rlwrap_rlRun_comment="" + +# Internal variables used by RunTest family of functions. +declare -ag _CleanupActions=() +declare -ag _Tests=() + +## +# errmsg ERRMSG +# +# ERRMSG +# error message +# +# Print ERRMSG to standard error output. +function errmsg() { + echo "$1" >&2 +} + +## +# required_options OPTION_LIST +# +# OPTION_LIST +# a list of option names (without --) +# +# For every X from OPTION_LIST, test if --$X was specified by checking that +# __${X//-/_} is not empty. +function required_options() { + local __optvar="" + + while [[ $# -gt 0 ]]; do + __optvar="__${1//-/_}" + if [[ -z "${!__optvar}" ]]; then + errmsg "${FUNCTION[1]}: Missing required option: --$1" + return 1 + fi + shift + done +} + +## +# invalid_argument ARG_NAME +# +# ARG_NAME +# argument name +# +# Report to standard error output that ARG_NAME is not a valid argument and +# return 1. +function invalid_argument() { + errmsg "${FUNCTION[1]}: Invalid argument '$1'." + return 1 +} + +## +# Concat ARGS +# +# ARGS +# list of arguments +# +# Make a string by concatenating all arguments from ARGS. Useful for creating +# long comments for rlRun. +function Concat() { + echo "$*" +} + +## +# RunCmd COMMAND [COMMAND_ARGS] +# +# COMMAND +# command that should be run +# COMMAND_ARGS +# command arguments +# +# Shorthand for RunCmdX -- COMMAND COMMAND_ARGS. +function RunCmd() { + RunCmdX -- "$@" +} + +## +# RunCmdX [-t] [-l] [-c] [-s] [STATUS] [COMMENT] [--] COMMAND [COMMAND_ARGS] +# +# -t, -l, -c, -s +# see rlRun +# STATUS +# see rlRun +# COMMENT +# see rlRun +# -- +# options-command delimiter +# COMMAND +# command that should be run +# COMMAND_ARGS +# command arguments +# +# Wrapper around beakerlib's rlRun that allows COMMAND and its arguments to be +# passed separately and not as one long string. +function RunCmdX() { + local __tflag="" + local __lflag="" + local __cflag="" + local __sflag="" + local __command="" + local __status="0" + local __comment="" + + # Handle short options: + while [[ $# -gt 0 ]]; do + case "$1" in + -t) __tflag="$1" ;; + -l) __lflag="$1" ;; + -c) __cflag="$1" ;; + -s) __sflag="$1" ;; + *) break ;; + esac + shift + done + + # First positional argument before -- is expected status code: + if [[ -n "${1:-}" ]] && [[ "$1" != "--" ]]; then + __status="$1" + shift + fi + + # Second positional argument before -- is comment: + if [[ -n "${1:-}" ]] && [[ "$1" != "--" ]]; then + __comment="$1" + shift + fi + + # Consume options-command delimiter: + if [[ "${1:-}" == "--" ]]; then + shift + fi + + # Command name is required: + if [[ -z "${1:-}" ]]; then + errmsg "Expected command." + return 1 + fi + __command="$1"; shift + + # The rest of options are command arguments: + while [[ $# -gt 0 ]]; do + __command="${__command} $1" + shift + done + + # Let the game begin: + rlRun ${__tflag} ${__lflag} ${__cflag} ${__sflag} \ + "${__command}" "${__status}" "${__comment}" +} + +## +# RunCmdViaExpect +# +# Starts a specification of command that should be run via expect. This is +# handy for interactive commands. General usage is +# +# RunCmdViaExpect +# Path ${ScriptsDir} +# rlRunOptions -s +# ExpectOptions -f +# Command cryptsetup luksFormat ${VOLUME} +# Input --password ${PASSWD} +# Status 0 +# Comment "Format ${VOLUME}" +# FinishRun || return $? +# +# In the example above, Path specifies the directory where the expect script +# is located. If it is omitted, EXPECT_SCRIPTS_PATH environment variable is +# read. If EXPECT_SCRIPTS_PATH is not set, `.` is used. +# +# rlRunOptions are options for rlRun, like -s and -t (see beakerlib manual). +# +# ExpectOptions are options for expect tool or Tcl interpreter, not for the +# script. +# +# Command is a command together with its arguments that will be run via expect. +# The first command argument, the command itself, is used as a name of expect +# script so in the example above the name of expect script will be +# cryptsetup.exp. This script must exist in directory specified by Path. The +# rest of Command arguments will be passed to the end of this script's command +# line and it is up to script's implementation what happen to them. +# +# Input gather arguments that specify input data that are feed to command by +# expect tool when they are asked for. +# +# Input, Command, ExpectOptions, and rlRunOptions work in accumulative way. +# That is, you can write `Command cryptsetup luksFormat ${VOLUME}` as a two +# Command calls, e.g. `Command cryptsetup` and `luksFormat ${VOLUME}`. This +# allow to split long commands accross multiple lines without using backslash +# character, which has the benefit of writing comments for particular command +# options. +# +# Status is the expected status/return code (default is 0). +# +# Comment is the comment as described in rlRun documentation. The default +# comment is a string made from arguments of Command separated by spaces. +# +# FinishRun makes a final arguments for rlRun and execute it. In our case, the +# rlRun call will look like this +# +# rlRun -s "${ScriptsDir}/cryptsetup.exp -f -- --password ${PASSWD} -- +# luksFormat ${VOLUME}" 0 "Format ${VOLUME}" +# +# The return code of rlRun is the return code of FinishRun. To parse its +# command line, cryptsetup.exp uses cmdline package from tcllib. +function RunCmdViaExpect() { + _rlwrap_expect_script_path="${EXPECT_SCRIPTS_PATH:-.}" + _rlwrap_rlRun_options=() + _rlwrap_expect_options=() + _rlwrap_expect_script="" + _rlwrap_expect_script_scommand="" + _rlwrap_expect_script_command_args=() + _rlwrap_expect_script_input_args=() + _rlwrap_rlRun_status="0" + _rlwrap_rlRun_comment="" + + alias Path=_rlwrap_Path + alias rlRunOptions=_rlwrap_rlRunOptions + alias ExpectOptions=_rlwrap_ExpectOptions + alias Command=_rlwrap_Command + alias Input=_rlwrap_Input + alias Status=_rlwrap_Status + alias Comment=_rlwrap_Comment +} + +## +# _rlwrap_Path [PATH] +# +# PATH +# PATH to script directory +# +# See Path in RunCmdViaExpect. +function _rlwrap_Path() { + if [[ $# -gt 0 ]]; then + _rlwrap_expect_script_path="${1}" + fi +} + +## +# _rlwrap_rlRunOptions [OPTIONS] +# +# OPTIONS +# options for rlRun +# +# See rlRunOptions in RunCmdViaExpect. +function _rlwrap_rlRunOptions() { + _rlwrap_rlRun_options+=( "$@" ) +} + +## +# _rlwrap_ExpectOptions [OPTIONS] +# +# OPTIONS +# options for expect tool +# +# See ExpectOptions in RunCmdViaExpect. +function _rlwrap_ExpectOptions() { + _rlwrap_expect_options+=( "$@" ) +} + +## +# _rlwrap_Command [COMMAND_OR_OPTION] [COMMAND_OPTIONS] +# +# COMMAND_OR_OPTION +# command name or option (depending on a number of Command invocations) +# COMMAND_OPTIONS +# command options +# +# See Command in RunCmdViaExpect. +function _rlwrap_Command() { + if [[ -z "${_rlwrap_expect_script}" ]]; then + if [[ -n "${1:-}" ]]; then + _rlwrap_expect_script="${1}.exp" + _rlwrap_expect_script_scommand="${1}" + shift + fi + fi + + if [[ $# -gt 0 ]]; then + _rlwrap_expect_script_command_args+=( "$@" ) + _rlwrap_expect_script_scommand="${_rlwrap_expect_script_scommand} $*" + fi +} + +## +# _rlwrap_Input [OPTIONS] +# +# OPTIONS +# options for expect script that are used for passing input values to +# commands that are run from within the script +# +# See Input in RunCmdViaExpect. +function _rlwrap_Input() { + _rlwrap_expect_script_input_args+=( "$@" ) +} + +## +# _rlwrap_Status [STATUS_CODE] +# +# STATUS_CODE +# expected status/return code of expect script +# +# See Status in RunCmdViaExpect. +function _rlwrap_Status() { + if [[ $# -gt 0 ]]; then + _rlwrap_rlRun_status="${1}" + fi +} + +## +# _rlwrap_Comment [COMMENT] +# +# COMMENT +# comment to be passed to rlRun +# +# See Comment in RunCmdViaExpect. +function _rlwrap_Comment() { + if [[ $# -gt 0 ]]; then + _rlwrap_rlRun_comment="${1}" + fi +} + +## +# FinishRun +# +# See RunCmdViaExpect. +function FinishRun() { + local __command="" + + unalias Path + unalias rlRunOptions + unalias ExpectOptions + unalias Command + unalias Input + unalias Status + unalias Comment + + if [[ -z "${_rlwrap_expect_script}" ]]; then + errmsg "RunCmdViaExpect: Missing name of expect script!" + errmsg "| The name of expect script is deduced from the first" + errmsg "| argument given to Command." + return 1 + fi + + if [[ -z "${_rlwrap_rlRun_comment}" ]]; then + _rlwrap_rlRun_comment="${_rlwrap_expect_script_scommand}" + fi + + __command="${_rlwrap_expect_script_path}/${_rlwrap_expect_script}" + __command="${__command} ${_rlwrap_expect_options[*]} --" + __command="${__command} ${_rlwrap_expect_script_input_args[*]} --" + __command="${__command} ${_rlwrap_expect_script_command_args[*]}" + + rlRun "${_rlwrap_rlRun_options[@]}" "${__command}" \ + "${_rlwrap_rlRun_status}" "${_rlwrap_rlRun_comment}" +} + +## +# CreateTemporaryDirectory +# +# Create a temporary directory and store its path to Result. +function CreateTemporaryDirectory() { + Result="$(mktemp -d)" +} + +## +# PushDir DIRECTORY +# +# DIRECTORY +# path to directory +# +# Perform `rlRun pushd DIRECTORY`. +function PushDir() { + RunCmd pushd "\"$1\"" +} + +## +# PopDir +# +# Perform `rlRun popd`. +function PopDir() { + RunCmd popd +} + +## +# AtCleanup COMMAND +# +# COMMAND +# cleanup action as a command +# +# Insert COMMAND to the beginning of the list of cleanup actions. +function AtCleanup() { + _CleanupActions=( "$1" "${_CleanupActions[@]}" ) +} + +## +# AddTest TESTFUNC [DESCRIPTION] +# +# TESTFUNC +# function that performs the test +# DESCRIPTION +# test description +# +# Add test to the list of tests. +function AddTest() { + _Tests+=( "$1=${2:-}" ) +} + +## +# DoSetup +# +# Invoke Setup function and return its status code. Setup must be defined +# before. +function DoSetup() { + local __status=0 + + rlPhaseStartSetup + if [[ "$(LC_ALL=C type -t Setup)" != "function" ]]; then + rlFail "Function 'Setup' is not defined. Please, define it." + else + Setup + fi + __status=$? + rlPhaseEnd + return ${__status} +} + +## +# DoTests +# +# Run all tests from the tests list. +function DoTests() { + for __testspec in "${_Tests[@]}"; do + # __testspec has the format 'testfunc=test description': + rlPhaseStartTest "${__testspec#*=}" + "${__testspec%%=*}" || : + rlPhaseEnd + done +} + +## +# DoCleanup +# +# Run all registered cleanup actions in the reverse order than they were +# registered by AtCleanup. +function DoCleanup() { + rlPhaseStartCleanup + for __action in "${_CleanupActions[@]}"; do + "${__action}" || : + done + rlPhaseEnd +} + +## +# RunTest +# +# Test runner entry point. Perform setup, run all tests, and perform cleanup. +function RunTest() { + rlJournalStart + + DoSetup && DoTests + DoCleanup + + rlJournalPrintText + rlJournalEnd +} diff --git a/tests/utils/utils.sh b/tests/utils/utils.sh new file mode 100644 index 0000000..9eca043 --- /dev/null +++ b/tests/utils/utils.sh @@ -0,0 +1,195 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# File: ./tests/utils/utils.sh +# Author: Jiri Kucera +# Brief: Common shell utilities that helps test volume_key +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2020 Red Hat, Inc. +# +# 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 2 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/. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +## +# SCRIPTDIR +# +# Path to the directory with auxiliary scripts. +SCRIPTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" + +# Include beakerlib wrapper: +. "${SCRIPTDIR}/rlwrap.sh" || { + echo "${SCRIPTDIR}/rlwrap.sh cannot be included." >&2 + exit 1 +} + +## +# ClearGpgAgentsCache +# +# If CLEAR_GPG_AGENTS_CACHE is set, clear gpg-agent's password cache. +function ClearGpgAgentsCache() { + local __pid="" + + if [[ "${CLEAR_GPG_AGENTS_CACHE:-}" == "1" ]] \ + && __pid="$(pidof -s gpg-agent)" + then + kill -s SIGHUP ${__pid} || : + fi +} + +## +# CreateEncryptedVolume --image IMAGE --password PASS [--with-losetup] +# +# --image IMAGE +# path to image file from which volume is created; IMAGE is created by +# dd so it should not to point to an existing file +# --password PASS +# password needed to encrypt the volume +# --with-losetup +# create a volume as loop device +# +# Create encrypted volume from IMAGE (use PASS for the encryption). The name of +# created volume is stored to Result. +function CreateEncryptedVolume() { + local __image="" + local __volume="" + local __password="" + local __with_losetup="" + + while [[ $# -gt 0 ]]; do + case "$1" in + --image) shift; __image="$1" ;; + --password) shift; __password="$1" ;; + --with-losetup) __with_losetup="yes" ;; + *) invalid_argument "$1"; return $? ;; + esac + shift + done + + required_options image password || return $? + + RunCmd dd if=/dev/zero of="${__image}" bs=1M count=256 || return $? + + __volume="${__image}" + if [[ "${__with_losetup}" == "yes" ]]; then + RunCmd losetup -v -f "${__image}" || return $? + __volume="$( + set -o pipefail + losetup -a | grep "${__image}" | cut -d: -f1 + )" || return $? + fi + + RunCmdViaExpect + Command cryptsetup luksFormat "${__volume}" + Input --password "${__password}" + FinishRun || return $? + + Result="${__volume}" +} + +## +# CreateCertificate --name NAME [--rsa-bits BITS] +# +# --name NAME +# certificate name +# --rsa-bits BITS +# RSA bits (default: 1024) +# +# Create NAME.key, NAME.cert, and NAME.pem inside current working directory. +function CreateCertificate() { + local __name="" + local __rsa_bits=1024 + local __key="" + local __cert="" + local __pem="" + local __subject="" + + while [[ $# -gt 0 ]]; do + case "$1" in + --name) shift; __name="$1" ;; + --rsa-bits) shift; __rsa_bits="$1" ;; + *) invalid_argument "$1"; return $? ;; + esac + shift + done + + required_options name || return $? + + __key="${__name}.key" + __cert="${__name}.cert" + __pem="${__name}.pem" + + RunCmd openssl genrsa ${__rsa_bits} \> "${__key}" || return $? + + __subject="/C=XX/ST=FooState/L=FooLocality/O=FooOrg/OU=FooOrgUnit" + __subject="${__subject}/CN=John/SN=Doe/emailAddress=jdoe@foo.bar" + + RunCmd openssl req -new -x509 -nodes -sha1 -days 365 \ + -key "${__key}" -subj "'${__subject}'" \> "${__cert}" \ + || return $? + + RunCmd cat "${__cert}" "${__key}" \> "${__pem}" +} + +## +# SetupNSSDatabase --dest DEST --cert-name NAME --password PASS +# +# --dest DEST +# path to directory that become NSS database +# --cert-name NAME +# the name of the certificate +# --password PASS +# a password (common for certificate and for NSS database) +# +# Create and initialize NSS database DEST with certificate NAME and secure it +# with password PASS. +function SetupNSSDatabase() { + local __dest="" + local __cert_name="" + local __password="" + local __pwdfile="" + local __pem="" + local __p12="" + + while [[ $# -gt 0 ]]; do + case "$1" in + --dest) shift; __dest="$1" ;; + --cert-name) shift; __cert_name="$1" ;; + --password) shift; __password="$1" ;; + *) invalid_argument "$1"; return $? ;; + esac + shift + done + + required_options dest cert-name password || return $? + + RunCmd mkdir -p "${__dest}" || return $? + + __pwdfile="$(mktemp "./pwdfileXXXXX")" || return $? + + __pem="${__cert_name}.pem" + __p12="${__cert_name}.p12" + + RunCmd echo "${__password}" \> "${__pwdfile}" || return $? + + RunCmd certutil -N -d "${__dest}" -f "${__pwdfile}" || return $? + + RunCmd openssl pkcs12 -export -in "${__pem}" -out "${__p12}" \ + -name "${__cert-name}" -password "pass:${__password}" \ + || return $? + + RunCmd pk12util -i "${__p12}" -d "${__dest}" \ + -K "${__password}" -W "${__password}" +} diff --git a/tests/utils/volume_key.exp b/tests/utils/volume_key.exp new file mode 100755 index 0000000..82ae878 --- /dev/null +++ b/tests/utils/volume_key.exp @@ -0,0 +1,143 @@ +#!/usr/bin/expect -f +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# File: ./tests/utils/volume_key.exp +# Author: Jiri Kucera +# Brief: Expect wrapper around volume_key +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2020 Red Hat, Inc. +# +# 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 2 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/. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +package require cmdline + +source [file join [file dirname [info script]] "common.tcl"] + +set options { + {certpass.arg "" "Password for certificate"} + {lukspass.arg "" "Password for LUKS encryption/decryption"} + {packetpass.arg "" "Password for escrow packet encryption/decryption"} + {newpacketpass.arg "" "New password for escrow packet reencryption"} + {pinentry "gpg-agent may ask for password via pinentry"} +} + +set usage "\[options\] -- volume_key_options\noptions:" + +if {[catch { + array set params [::cmdline::getoptions argv $options $usage] +} result]} { + if {$::errorCode eq {CMDLINE USAGE}} { + puts $result + exit 0 + } + puts $::errorCode + puts $::errorInfo + exit 1 +} + +set certpass $params(certpass) +set lukspass $params(lukspass) +set packetpass $params(packetpass) +set newpacketpass $params(newpacketpass) +set pinentry $params(pinentry) + +proc prompt_cert_password {password} { + verify_password $password + expect -re "Enter password for.*" + sleep 1 + send -- "$password\r" +} + +proc prompt_luks_password {password} { + verify_password $password + expect -re "Passphrase for.*" + sleep 1 + send -- "$password\r" +} + +proc prompt_new_luks_password {password} { + verify_password $password + expect -re "New passphrase for.*" + sleep 1 + send -- "$password\r" + expect -re "Repeat new passphrase for.*" + sleep 1 + send -- "$password\r" +} + +proc prompt_packet_password {password pinentry} { + verify_password $password + expect -re "Escrow packet passphrase.*" + sleep 1 + send -- "$password\r" + if {$pinentry} { + expect -re ".*Passphrase.*" + sleep 1 + send -- "$password\r" + } +} + +proc prompt_new_packet_password {password pinentry} { + verify_password $password + expect -re "New packet passphrase.*" + sleep 1 + send -- "$password\r" + expect -re "Repeat new packet passphrase.*" + sleep 1 + send -- "$password\r" + if {$pinentry} { + expect -re ".*Passphrase.*" + sleep 1 + send -- "$password\r" + expect -re ".*Passphrase.*" + sleep 1 + send -- "$password\r" + } +} + +eval spawn volume_key $::argv +if {"--reencrypt" in $::argv} { + if {"-d" in $::argv} { + prompt_cert_password $certpass + } else { + prompt_packet_password $packetpass $pinentry + } + prompt_new_packet_password $newpacketpass $pinentry + expect eof +} elseif {"--restore" in $::argv} { + if {"-d" in $::argv} { + prompt_cert_password $certpass + } else { + prompt_packet_password $packetpass $pinentry + } + prompt_new_luks_password $lukspass + expect eof +} elseif {"--save" in $::argv} { + prompt_luks_password $lukspass + if {"-c" ni $::argv} { + prompt_new_packet_password $packetpass $pinentry + } + expect eof +} elseif {[oneof {"--dump" "--secrets" "--setup-volume"} $::argv]} { + if {"-d" in $::argv} { + prompt_cert_password $certpass + } else { + prompt_packet_password $packetpass $pinentry + } + expect eof +} diff --git a/volume_key-0.3.12-fix_resource_leaks.patch b/volume_key-0.3.12-fix_resource_leaks.patch new file mode 100644 index 0000000..31d69d7 --- /dev/null +++ b/volume_key-0.3.12-fix_resource_leaks.patch @@ -0,0 +1,59 @@ +diff --git a/lib/kmip.c b/lib/kmip.c +index dda819a..333603c 100644 +--- a/lib/kmip.c ++++ b/lib/kmip.c +@@ -1152,6 +1152,7 @@ kmip_decode_key_value (struct kmip_decoding_state *kmip, + break; + + default: ++ kmip_key_value_free (res); + g_return_val_if_reached (-1); + } + res->attributes = g_ptr_array_new (); +@@ -1348,6 +1349,7 @@ kmip_decode_object_symmetric_key (struct kmip_decoding_state *kmip, + g_snprintf (num, sizeof (num), "%" G_GUINT32_FORMAT, res->block->type); + g_set_error (error, LIBVK_ERROR, LIBVK_ERROR_KMIP_UNSUPPORTED_VALUE, + _("Unsupported symmetric key format %s"), num); ++ kmip_object_symmetric_key_free (res); + return -1; + } + *obj = res; +@@ -1384,6 +1386,7 @@ kmip_decode_object_secret_data (struct kmip_decoding_state *kmip, + g_snprintf (num, sizeof (num), "%" G_GUINT32_FORMAT, res->block->type); + g_set_error (error, LIBVK_ERROR, LIBVK_ERROR_KMIP_UNSUPPORTED_VALUE, + _("Unsupported symmetric key format %s"), num); ++ kmip_object_secret_data_free (res); + return -1; + } + *obj = res; +diff --git a/lib/volume_luks.c b/lib/volume_luks.c +index d1c5d47..4d32d9b 100644 +--- a/lib/volume_luks.c ++++ b/lib/volume_luks.c +@@ -547,8 +547,8 @@ luks_apply_secret (struct libvk_volume *vol, const struct libvk_volume *packet, + } + g_free (last_log_entry); + +- g_return_val_if_fail (vol->v.luks->key_bytes == packet->v.luks->key_bytes, +- -1); ++ if (vol->v.luks->key_bytes != packet->v.luks->key_bytes) ++ goto err_passphrase; + luks_replace_key (vol, packet->v.luks->key); + luks_replace_passphrase (vol, passphrase); + vol->v.luks->passphrase_slot = res; +diff --git a/src/volume_key.c b/src/volume_key.c +index 074b187..24b70d6 100644 +--- a/src/volume_key.c ++++ b/src/volume_key.c +@@ -735,6 +735,11 @@ write_packet (struct packet_output_state *pos, const char *filename, + || g_file_set_contents (filename, packet, size, error) == FALSE) + { + g_prefix_error (error, _("Error creating `%s': "), filename); ++ if (packet != NULL) { ++ if (output_format_cleartext != 0) ++ memset (packet, 0, size); ++ g_free (packet); ++ } + return -1; + } + if (output_format_cleartext != 0) diff --git a/volume_key-0.3.12-support_LUKS2_and_more.patch b/volume_key-0.3.12-support_LUKS2_and_more.patch new file mode 100644 index 0000000..1e5be56 --- /dev/null +++ b/volume_key-0.3.12-support_LUKS2_and_more.patch @@ -0,0 +1,24 @@ +diff --git a/lib/volume_luks.c b/lib/volume_luks.c +index f4bf2c8..d1c5d47 100644 +--- a/lib/volume_luks.c ++++ b/lib/volume_luks.c +@@ -30,6 +30,10 @@ Author: Miloslav Trmač */ + #include "volume.h" + #include "volume_luks.h" + ++#ifndef CRYPT_LUKS ++#define CRYPT_LUKS NULL ++#endif ++ + /* LUKS - specific code */ + + /* Return an error message for ERR_NO, for g_free (). */ +@@ -105,7 +109,7 @@ open_crypt_device (const char *path, char **last_log_entry, GError **error) + if (r < 0) + goto err; + crypt_set_log_callback(cd, record_cryptsetup_log_entry, last_log_entry); +- r = crypt_load (cd, CRYPT_LUKS1, NULL); ++ r = crypt_load (cd, CRYPT_LUKS, NULL); + if (r < 0) + goto err_cd; + return cd; diff --git a/volume_key-0.3.9-config.h.patch b/volume_key-0.3.9-config.h.patch deleted file mode 100644 index 98a70f9..0000000 --- a/volume_key-0.3.9-config.h.patch +++ /dev/null @@ -1,25 +0,0 @@ -The library's header file distributed in the devel package cannot include -the config.h file that is only available during build otherwise it's not -possible to use the library outside of the volume_key build process. - -Signed-off-by: Vratislav Podzimek ---- - lib/libvolume_key.h | 2 -- - 1 file changed, 2 deletions(-) - -diff --git a/lib/libvolume_key.h b/lib/libvolume_key.h -index 657b626..513f923 100644 ---- a/lib/libvolume_key.h -+++ b/lib/libvolume_key.h -@@ -18,8 +18,6 @@ Author: Miloslav Trmač */ - #ifndef LIBVOLUME_KEY_H__ - #define LIBVOLUME_KEY_H__ - --#include -- - #include - #include - --- -2.1.0 - diff --git a/volume_key-0.3.9-fips-crash.patch b/volume_key-0.3.9-fips-crash.patch deleted file mode 100644 index 0f79d4e..0000000 --- a/volume_key-0.3.9-fips-crash.patch +++ /dev/null @@ -1,33 +0,0 @@ -This case can be triggered by encrypting in FIPS mode, where the default -algorithm is unsupported and gpg crashes in response. - -diff --git a/lib/crypto.c b/lib/crypto.c -index 06eb482..905d583 100644 ---- a/lib/crypto.c -+++ b/lib/crypto.c -@@ -709,6 +709,12 @@ encrypt_with_passphrase (size_t *res_size, const void *data, size_t size, - } - gpgme_data_release (src_data); - gpgme_res = gpgme_data_release_and_get_mem (dest_data, res_size); -+ if (gpgme_res == NULL) -+ { -+ g_set_error (error, LIBVK_ERROR, LIBVK_ERROR_CRYPTO, -+ _("Unknown error getting encryption result")); -+ goto err_ctx; -+ } - res = g_memdup (gpgme_res, *res_size); - gpgme_free (gpgme_res); - -@@ -759,6 +765,12 @@ decrypt_with_passphrase (size_t *res_size, const void *data, size_t size, - } - gpgme_data_release (src_data); - gpgme_res = gpgme_data_release_and_get_mem (dest_data, res_size); -+ if (gpgme_res == NULL) -+ { -+ g_set_error (error, LIBVK_ERROR, LIBVK_ERROR_CRYPTO, -+ _("Unknown error getting decryption result")); -+ goto err_ctx; -+ } - res = g_memdup (gpgme_res, *res_size); - gpgme_free (gpgme_res); - diff --git a/volume_key.spec b/volume_key.spec index f5986c5..1e7129f 100644 --- a/volume_key.spec +++ b/volume_key.spec @@ -1,126 +1,309 @@ -%{!?python_sitearch: %global python_sitearch %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)")} +# Define `python3_sitearch' if there is no one: +%{!?python3_sitearch:%global python3_sitearch %(%{__python3} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(1))")} + +# Enable Python 3 in Fedora and RHEL > 7 as default: +%if 0%{?fedora} || 0%{?rhel} > 7 +# Add `--without python3' option (enable python3 by default): +%bcond_without python3 +%else +# Add `--with python3' option (disable python3 by default): +%bcond_with python3 +%endif + +# Drop Python 2 in Fedora >= 30 and RHEL > 7 as default: +%if 0%{?fedora} >= 30 || 0%{?rhel} > 7 +%global drop_python2 1 +%global configure_with_python2 no +%else +# Define `python2_sitearch' if there is no one: +%{!?python2_sitearch:%global python2_sitearch %(%{__python2} -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)")} +%global configure_with_python2 yes +%endif + +%if %{with python3} +%global configure_with_python3 yes +%else +%global configure_with_python3 no +%endif + +# Additional configure options: +%global with_pythons --with-python=%{configure_with_python2} --with-python3=%{?configure_with_python3} Summary: An utility for manipulating storage encryption keys and passphrases Name: volume_key -Version: 0.3.9 -Release: 10%{?dist} -License: GPLv2 -Group: Applications/System -URL: https://fedorahosted.org/volume_key/ -Requires: volume_key-libs%{?_isa} = %{version}-%{release} +Version: 0.3.12 +Release: 29%{?dist} +License: GPL-2.0-only AND (MPL-1.1 OR GPL-2.0-or-later OR LGPL-2.1-or-later) +URL: https://pagure.io/%{name}/ +Requires: %{name}-libs%{?_isa} = %{version}-%{release} -Source0: https://fedorahosted.org/releases/v/o/volume_key/volume_key-%{version}.tar.xz -# Upstream commit 04991fe8c4f77c4e5c7874c2db8ca32fb4655f6e -Patch1: volume_key-0.3.9-fips-crash.patch -# Upstream commit 8f8698aba19b501f01285e9eec5c18231fc6bcea -Patch2: volume_key-0.3.9-config.h.patch -BuildRequires: cryptsetup-luks-devel, gettext-devel, glib2-devel, /usr/bin/gpg -BuildRequires: gpgme-devel, libblkid-devel, nss-devel, python-devel +Source0: https://releases.pagure.org/%{name}/%{name}-%{version}.tar.xz +# Support all LUKS devices +# - backport of 26c09768662d8958debe8c9410dae9fda02292c3 +Patch0: volume_key-0.3.12-support_LUKS2_and_more.patch +# Fix resource leaks +# - backport of bf6618ec0b09b4e51fc97fa021e687fbd87599ba +Patch1: volume_key-0.3.12-fix_resource_leaks.patch +BuildRequires: autoconf, automake, libtool +BuildRequires: make +BuildRequires: gcc +BuildRequires: cryptsetup-devel, gettext-devel, glib2-devel, /usr/bin/gpg2 +BuildRequires: gpgme-devel, libblkid-devel, nss-devel +BuildRequires: python3-devel, python3-setuptools +%if 0%{?drop_python2} < 1 +BuildRequires: python2-devel +%endif +# Needed by %%check: +BuildRequires: nss-tools + +%global desc_common The main goal of the software is to allow restoring access to an encrypted\ +hard drive if the primary user forgets the passphrase. The encryption key\ +back up can also be useful for extracting data after a hardware or software\ +failure that corrupts the header of the encrypted volume, or to access the\ +company data after an employee leaves abruptly. + +%global desc_app This package provides a command-line tool for manipulating storage volume\ +encryption keys and storing them separately from volumes.\ +\ +%{desc_common} + +%global desc_lib This package provides lib%{name}, a library for manipulating storage volume\ +encryption keys and storing them separately from volumes.\ +\ +%{desc_common} + +%global desc_python(V:) This package provides %%{-V:Python %%{-V*}}%%{!-V:Python} bindings for lib%{name}, a library for\ +manipulating storage volume encryption keys and storing them separately from\ +volumes.\ +\ +%{desc_common}\ +\ +%{name} currently supports only the LUKS volume encryption format. Support\ +for other formats is possible, some formats are planned for future releases. %description -This package provides a command-line tool for manipulating storage volume -encryption keys and storing them separately from volumes. - -The main goal of the software is to allow restoring access to an encrypted -hard drive if the primary user forgets the passphrase. The encryption key -back up can also be useful for extracting data after a hardware or software -failure that corrupts the header of the encrypted volume, or to access the -company data after an employee leaves abruptly. +%{desc_app} %package devel Summary: A library for manipulating storage encryption keys and passphrases -Group: Development/Libraries -Requires: volume_key-libs%{?_isa} = %{version}-%{release} +Requires: %{name}-libs%{?_isa} = %{version}-%{release} %description devel -This package provides libvolume_key, a library for manipulating storage volume -encryption keys and storing them separately from volumes. - -The main goal of the software is to allow restoring access to an encrypted -hard drive if the primary user forgets the passphrase. The encryption key -back up can also be useful for extracting data after a hardware or software -failure that corrupts the header of the encrypted volume, or to access the -company data after an employee leaves abruptly. +%{desc_lib} %package libs Summary: A library for manipulating storage encryption keys and passphrases -Group: System Environment/Libraries -Requires: /usr/bin/gpg +Requires: /usr/bin/gpg2 %description libs -This package provides libvolume_key, a library for manipulating storage volume -encryption keys and storing them separately from volumes. +%{desc_lib} -The main goal of the software is to allow restoring access to an encrypted -hard drive if the primary user forgets the passphrase. The encryption key -back up can also be useful for extracting data after a hardware or software -failure that corrupts the header of the encrypted volume, or to access the -company data after an employee leaves abruptly. +%if 0%{?drop_python2} < 1 +%package -n python2-%{name} +%{?python_provide:%python_provide python2-%{name}} +Summary: Python bindings for lib%{name} +Requires: %{name}-libs%{?_isa} = %{version}-%{release} -%package -n python-volume_key -Summary: Python bindings for libvolume_key -Group: System Environment/Libraries -Requires: volume_key-libs%{?_isa} = %{version}-%{release} +%description -n python2-%{name} +%desc_python +%endif -%description -n python-volume_key -This package provides Python bindings for libvolume_key, a library for -manipulating storage volume encryption keys and storing them separately from -volumes. +%if %{with python3} +%package -n python3-%{name} +%{?python_provide:%python_provide python3-%{name}} +Summary: Python 3 bindings for lib%{name} +Requires: %{name}-libs%{?_isa} = %{version}-%{release} -The main goal of the software is to allow restoring access to an encrypted -hard drive if the primary user forgets the passphrase. The encryption key -back up can also be useful for extracting data after a hardware or software -failure that corrupts the header of the encrypted volume, or to access the -company data after an employee leaves abruptly. - -volume_key currently supports only the LUKS volume encryption format. Support -for other formats is possible, some formats are planned for future releases. +%description -n python3-%{name} +%desc_python -V 3 +%endif %prep %setup -q - -%patch1 -p1 -b .fips-crash -%patch2 -p1 -b .config.h +%patch -P0 -p1 +%patch -P1 -p1 +autoreconf -fiv %build -%configure -make %{?_smp_mflags} +%configure %{?with_pythons} +%make_build %install -make install DESTDIR=$RPM_BUILD_ROOT INSTALL='install -p' +%make_install -%find_lang volume_key +# Remove libtool archive +find %{buildroot} -type f -name "*.la" -delete -%clean -rm -rf $RPM_BUILD_ROOT +%find_lang %{name} -%post libs -p /sbin/ldconfig -%postun libs -p /sbin/ldconfig +%check +make check || { \ +echo "======================== ./test-suite.log ========================"; \ +cat ./test-suite.log; \ +echo "=================================================================="; \ +exit 1; \ +} + +%ldconfig_scriptlets libs %files -%defattr(-,root,root,-) %doc README contrib -%{_bindir}/volume_key -%{_mandir}/man8/volume_key.8* +%{_bindir}/%{name} +%{_mandir}/man8/%{name}.8* %files devel -%defattr(-,root,root,-) -%{_includedir}/volume_key -%exclude %{_libdir}/libvolume_key.la -%{_libdir}/libvolume_key.so +%{_includedir}/%{name} +%{_libdir}/lib%{name}.so -%files libs -f volume_key.lang -%defattr(-,root,root,-) +%files libs -f %{name}.lang %doc AUTHORS COPYING ChangeLog NEWS -%{_libdir}/libvolume_key.so.* +%{_libdir}/lib%{name}.so.* -%files -n python-volume_key -%defattr(-,root,root,-) -%exclude %{python_sitearch}/_volume_key.la -%{python_sitearch}/_volume_key.so -%{python_sitearch}/volume_key.py* +%if 0%{?drop_python2} < 1 +%files -n python2-%{name} +%{python2_sitearch}/_%{name}.so +%{python2_sitearch}/%{name}.py* +%endif + +%if %{with python3} +%files -n python3-%{name} +%{python3_sitearch}/_%{name}.so +%{python3_sitearch}/%{name}.py* +%{python3_sitearch}/__pycache__/%{name}.* +%endif %changelog +* Tue Nov 11 2025 Michal Hlavinka - 0.3.12-29 +- rebuild for gpgme 2.0 + +* Fri Sep 19 2025 Python Maint - 0.3.12-28 +- Rebuilt for Python 3.14.0rc3 bytecode + +* Fri Aug 15 2025 Python Maint - 0.3.12-27 +- Rebuilt for Python 3.14.0rc2 bytecode + +* Fri Jul 25 2025 Fedora Release Engineering - 0.3.12-26 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild + +* Mon Jun 02 2025 Python Maint - 0.3.12-25 +- Rebuilt for Python 3.14 + +* Sun Jan 19 2025 Fedora Release Engineering - 0.3.12-24 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild + +* Sat Jul 20 2024 Fedora Release Engineering - 0.3.12-23 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild + +* Fri Jun 07 2024 Python Maint - 0.3.12-22 +- Rebuilt for Python 3.13 + +* Sat Jan 27 2024 Fedora Release Engineering - 0.3.12-21 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + +* Sat Jul 22 2023 Fedora Release Engineering - 0.3.12-20 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild + +* Tue Jun 13 2023 Python Maint - 0.3.12-19 +- Rebuilt for Python 3.12 + +* Sat Jan 21 2023 Fedora Release Engineering - 0.3.12-18 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild + +* Sat Jul 23 2022 Fedora Release Engineering - 0.3.12-17 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild + +* Mon Jun 13 2022 Python Maint - 0.3.12-16 +- Rebuilt for Python 3.11 + +* Sat Jan 22 2022 Fedora Release Engineering - 0.3.12-15 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild + +* Wed Aug 04 2021 Jiri Kucera - 0.3.12-14 +- Fix FTBFS +- Move License tag back to GPLv2 (this is the effective license) +- Use make macros + +* Fri Jul 23 2021 Fedora Release Engineering - 0.3.12-13 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild + +* Fri Jun 04 2021 Python Maint - 0.3.12-12 +- Rebuilt for Python 3.10 + +* Wed Mar 31 2021 Jiri Kucera - 0.3.12-11 +- Fix resource leaks + +* Wed Jan 27 2021 Fedora Release Engineering - 0.3.12-10 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Wed Jul 29 2020 Fedora Release Engineering - 0.3.12-9 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Tue May 26 2020 Miro Hrončok - 0.3.12-8 +- Rebuilt for Python 3.9 + +* Fri Jan 31 2020 Fedora Release Engineering - 0.3.12-7 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Thu Oct 03 2019 Miro Hrončok - 0.3.12-6 +- Rebuilt for Python 3.8.0rc1 (#1748018) + +* Mon Aug 19 2019 Miro Hrončok - 0.3.12-5 +- Rebuilt for Python 3.8 + +* Sat Jul 27 2019 Fedora Release Engineering - 0.3.12-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Sun Feb 03 2019 Fedora Release Engineering - 0.3.12-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Wed Jan 23 2019 Jiri Kucera - 0.3.12-2 +- Add support for LUKS2 and more +- Fix License tag + +* Mon Oct 08 2018 Jiri Kucera - 0.3.12-1 +- Update to volume_key-0.3.12 + Resolves: #1634850 + +* Sat Jul 14 2018 Fedora Release Engineering - 0.3.10-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Wed May 16 2018 Jiri Kucera - 0.3.10-1 +- Update to volume_key-0.3.10 + Resolves: #1479349, #1517016 + +* Wed Feb 14 2018 Iryna Shcherbina - 0.3.9-20 +- Update Python 2 dependency declarations to new packaging standards + (See https://fedoraproject.org/wiki/FinalizingFedoraSwitchtoPython3) + +* Fri Feb 09 2018 Fedora Release Engineering - 0.3.9-19 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Sat Feb 03 2018 Igor Gnatenko - 0.3.9-18 +- Switch to %%ldconfig_scriptlets + +* Tue Nov 7 2017 Miloslav Trmač - 0.3.9-17 +- Update for libcryptsetup ABI change + +* Sat Aug 19 2017 Zbigniew Jędrzejewski-Szmek - 0.3.9-16 +- Python 2 binary package renamed to python2-volume_key + See https://fedoraproject.org/wiki/FinalizingFedoraSwitchtoPython3 + +* Thu Aug 03 2017 Fedora Release Engineering - 0.3.9-15 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Thu Jul 27 2017 Fedora Release Engineering - 0.3.9-14 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Mon May 29 2017 Miloslav Trmač - 0.3.9-13 +- Point URL: and Source: to the new home at pagure.io + Resolves: 1456378 + +* Sat Feb 11 2017 Fedora Release Engineering - 0.3.9-12 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Sat Dec 10 2016 Igor Gnatenko - 0.3.9-11 +- Rebuild for gpgme 1.18 + * Tue Jul 19 2016 Fedora Release Engineering - 0.3.9-10 - https://fedoraproject.org/wiki/Changes/Automatic_Provides_for_Python_RPM_Packages