diff --git a/tests/.fmf/version b/tests/.fmf/version new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/.fmf/version @@ -0,0 +1 @@ +1 diff --git a/tests/Sanity/basic-sanity/main.fmf b/tests/Sanity/basic-sanity/main.fmf new file mode 100644 index 0000000..07ac2a8 --- /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 + - expect + - tcllib + - nss-tools +test: ./runtest.sh +duration: 10m +tier: 1 diff --git a/tests/Sanity/basic-sanity/runtest.sh b/tests/Sanity/basic-sanity/runtest.sh new file mode 100755 index 0000000..f265cfd --- /dev/null +++ b/tests/Sanity/basic-sanity/runtest.sh @@ -0,0 +1,192 @@ +#!/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/. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +set -uo pipefail + +_TESTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" + +# Include Beaker environment +. /usr/bin/rhts-environment.sh || exit 1 +. /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 +} + +PACKAGE="volume_key" + +_GNUPG_DIR="${HOME}/.gnupg" +_IMAGE="image" +_IMAGE_IMG="${_IMAGE}.img" +_PACKET="packet" +_NEW_PACKET="new-packet" +_PACKET_ASYM="packet-asym" +_NEW_PACKET_ASYM="new-packet-asym" +_ESCROW="escrow" +_ESCROW_KEY="${_ESCROW}.key" +_ESCROW_CERT="${_ESCROW}.cert" +_ESCROW_PEM="${_ESCROW}.pem" +_ESCROW_P12="${_ESCROW}.p12" +_NSSDB="nssdb" + +_LUKS_PASS="lukspass" +_PACKET_PASS="packetpass" +_NEW_PACKET_PASS="newpacketpass" +_CERT_PASS="certpass" +_NEW_LUKS_PASS="newlukspass" +_NEW_LUKS_PASS_ASYM="newlukspass-asym" + +_LANG_BACKUP="${LANG}" +_LC_ALL_BACKUP="${LC_ALL}" +_TEMP_DIR="" +_VOLUME="" + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ Setup +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +function Setup() { + LANG=C + LC_ALL=C + AtCleanup Cleanup_RestoreEnvironment + + if ! rlAssertRpm "${PACKAGE}"; then + return $? + fi + + if [[ -d "${_GNUPG_DIR}" ]]; then + if ! rlFileBackup "${_GNUPG_DIR}"; then + return $? + fi + AtCleanup rlFileRestore + else + AtCleanup Cleanup_RemoveGnuPG + fi + + if ! rlRun CreateTemporaryDirectory; then + return $? + fi + _TEMP_DIR="${_Result}" + AtCleanup Cleanup_RemoveTemporaryDirectory + + if ! PushDir "${_TEMP_DIR}"; then + return $? + fi + AtCleanup PopDir + + if ! CreateEncryptedVolume \ + --image "${_IMAGE_IMG}" \ + --password "${_LUKS_PASS}" \ + ${USE_LOSETUP:+--with-losetup} + then + return $? + fi + _VOLUME="${_Result}" + AtCleanup Cleanup_DestroyVolume + + if ! CreateCertificate --name "${_ESCROW}" --dest "${_TEMP_DIR}"; then + return $? + fi + + if ! SetupNSSDatabase --dest "${_TEMP_DIR}/${_NSSDB}" \ + --cert-name "${_ESCROW}" --password "${_CERT_PASS}" + then + return $? + fi +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ Cleanup +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +function Cleanup_RestoreEnvironment() { + LANG="${_LANG_BACKUP}" + LC_ALL="${_LC_ALL_BACKUP}" +} + +function Cleanup_RemoveGnuPG() { + CmdRun -- rm -rfv "${_GNUPG_DIR}" +} + +function Cleanup_RemoveTemporaryDirectory() { + CmdRun -- rm -rfv "${_TEMP_DIR}" +} + +function Cleanup_DestroyVolume() { + if [[ "${USE_LOSETUP:+yes}" == "yes" ]]; then + CmdRun -- losetup -d "${_VOLUME}" + fi +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ Tests +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +function TestVolumeKeySave() { + CmdRun 0 \ + "volume_key --save ${_VOLUME} --output-format=passphrase -o ${_PACKET}" \ + ${SCRIPTDIR}/volume_key.exp -- \ + --password1 "${_LUKS_PASS}" --password2 "${_PACKET_PASS}" \ + ${USING_PINENTRY:+--pinentry} -- \ + --save "${_VOLUME}" --output-format=passphrase -o "${_PACKET}" +} +AddTest TestVolumeKeySave "save" + +function TestVolumeKeyRestore() { + ClearGpgAgentsCache + if ! CmdRun 0 "volume_key --restore ${_VOLUME} ${_PACKET}" \ + ${SCRIPTDIR}/volume_key.exp -- \ + --password1 "${_PACKET_PASS}" --password2 "${_NEW_LUKS_PASS}" \ + ${USING_PINENTRY:+--pinentry} -- \ + --restore "${_VOLUME}" "${_PACKET}" + then + return $? + fi + + if ! CmdRun 0 "cryptsetup luksOpen ${_VOLUME} ${_IMAGE}" \ + ${SCRIPTDIR}/cryptsetup.exp -- --password "${_NEW_LUKS_PASS}" -- \ + luksOpen "${_VOLUME}" "${_IMAGE}" + then + return $? + fi + + CmdRun -- ls -la "/dev/mapper" + rlAssertExists "/dev/mapper/${_IMAGE}" + + CmdRun -- cryptsetup luksClose "${_IMAGE}" +} +AddTest TestVolumeKeyRestore "restore" + +RunTest diff --git a/tests/settings/environment.sh b/tests/settings/environment.sh new file mode 100644 index 0000000..79107c3 --- /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="" +export USING_PINENTRY="" +export CLEAR_GPG_AGENTS_CACHE=1 diff --git a/tests/tests.yml b/tests/tests.yml new file mode 100644 index 0000000..c113f70 --- /dev/null +++ b/tests/tests.yml @@ -0,0 +1,14 @@ +--- +- hosts: localhost + roles: + - role: standard-test-beakerlib + tags: + - classic + - container + fmf_filter: "tier: 1" + required_packages: + - volume_key + - cryptsetup + - expect + - tcllib + - nss-tools diff --git a/tests/utils/common.tcl b/tests/utils/common.tcl new file mode 100644 index 0000000..2b1117b --- /dev/null +++ b/tests/utils/common.tcl @@ -0,0 +1,30 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# 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 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..7de0574 --- /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 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 -exact "Are you sure? (Type uppercase yes): " + 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..c75f633 --- /dev/null +++ b/tests/utils/rlwrap.sh @@ -0,0 +1,280 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# 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/. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +set -uo pipefail + +## +# Result, ResultA, ResultB +# +# If a function has an output, it is stored inside these variables. +Result="" +ResultA="" +ResultB="" + +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 [-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 RunCmd() { + 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}" +} + +## +# 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 + + if DoSetup; then + DoTests + fi + DoCleanup + + rlJournalPrintText + rlJournalEnd +} diff --git a/tests/utils/utils.sh b/tests/utils/utils.sh new file mode 100644 index 0000000..f1336f3 --- /dev/null +++ b/tests/utils/utils.sh @@ -0,0 +1,224 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# 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/. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +set -uo pipefail + +## +# 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="" + local __status=0 + + 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 + + if ! required_options image password; then + return $? + fi + + if ! RunCmd -- dd if=/dev/zero of="${__image}" bs=1M count=256; then + return $? + fi + + __volume="${__image}" + if [[ "${__with_losetup}" == "yes" ]]; then + if ! RunCmd -- losetup -v -f "${__image}"; then + return $? + fi + __volume="$(losetup -a | grep "${__image}" | cut -d: -f1)"; __status=$? + if [[ ${__status} -ne 0 ]]; then + return ${__status} + fi + fi + + if ! RunCmd 0 "cryptsetup luksFormat ${__volume}" -- \ + ${SCRIPTDIR}/cryptsetup.exp -- --password "${__password}" -- \ + luksFormat "${__volume}" + then + return $? + fi + + 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 + + if ! required_options name; then + return $? + fi + + __key="${__name}.key" + __cert="${__name}.cert" + __pem="${__name}.pem" + + if ! RunCmd -- openssl genrsa ${__rsa_bits} \> "${__key}"; then + return $? + fi + + __subject="/C=FooCountry/ST=FooState/L=FooLocality/O=FooOrg/OU=FooOrgUnit" + __subject="${__subject}/CN=John/SN=Doe/emailAddress=jdoe@foo.bar" + + if ! RunCmd -- openssl req -new -x509 -nodes -sha1 -days 365 \ + -key "${__key}" -subj "'${__subject}'" \> "${__cert}" + then + return $? + fi + + 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 + + if ! required_options dest cert-name password; then + return $? + fi + + if ! RunCmd -- mkdir -p "${__dest}"; then + return $? + fi + + if ! __pwdfile="$(mktemp "./pwdfileXXXXX")"; then + return $? + fi + + __pem="${__cert_name}.pem" + __p12="${__cert_name}.p12" + + if ! RunCmd -- echo "${__password}" \> "${__pwdfile}"; then + return $? + fi + + if ! RunCmd -- certutil -N -d "${__dest}" -f "${__pwdfile}"; then + return $? + fi + + if ! RunCmd -- openssl pkcs12 -export -in "${__pem}" -out "${__p12}" \ + -name "${__cert-name}" -password "pass:${__password}" + then + return $? + fi + + 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..ace7ba0 --- /dev/null +++ b/tests/utils/volume_key.exp @@ -0,0 +1,110 @@ +#!/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 common.tcl + +set options { + {password1.arg "" "Password that volume_key may ask for"} + {password2.arg "" "Second password that volume_key may ask for"} + {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 password1 $params(password1) +set password2 $params(password2) + +proc prompt_volume_password {password} { + verify_password $password + expect -re "Passphrase for.*" + sleep 1 + send -- "$password\r" +} + +proc prompt_new_volume_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 {"--save" in $::argv} { + prompt_volume_password $password1 + prompt_new_packet_password $password2 $pinentry + expect eof +} elseif {"--restore" in $::argv} { + prompt_packet_password $password1 $pinentry + prompt_new_volume_password $password2 + expect eof +}