[PATCH v2 4/8] evmtest: test kexec signature policy

From: djacobs7
Date: Fri Mar 22 2019 - 04:35:09 EST


From: David Jacobson <djacobs7@xxxxxxxxxxxxxx>

With secure boot enabled, the bootloader verifies the kernel image's
signature before transferring control to it. With Linux as the
bootloader running with secure boot enabled, kexec needs to verify the
kernel image's signature.

This patch defined a new test named "kexec_sig", which first attempts to
kexec an unsigned kernel image with an IMA policy that requires
signatures on any kernel image. Then, the test attempts to kexec the
signed kernel image, which should succeed.

Signed-off-by: David Jacobson <djacobs7@xxxxxxxxxxxxxx>

Changelog:
* Added policy_sig to test list
* shellcheck compliant
* move from functions to tests
* suggestions from Mimi
* checkbashisms complaint
* removed begin
* removed long opts
* restructed to use functions
---
evmtest/README | 3 +-
evmtest/evmtest | 1 +
evmtest/files/policies/kexec_policy | 3 +
evmtest/tests/kexec_sig.sh | 167 ++++++++++++++++++++++++++++
4 files changed, 173 insertions(+), 1 deletion(-)
create mode 100644 evmtest/files/policies/kexec_policy
create mode 100755 evmtest/tests/kexec_sig.sh

diff --git a/evmtest/README b/evmtest/README
index 8c63630..91c8cda 100644
--- a/evmtest/README
+++ b/evmtest/README
@@ -39,7 +39,8 @@ TEST NAMES
env_validate - verify kernel build
example_test - example test
policy_sig - verify loading IMA policies
- policy_sig - test IMA-appraise on policies
+ kexec_sig - test IMA-appraise on kexec image loading
+ kmod_sig - test IMA-appraise on kernel module loading


Introduction
diff --git a/evmtest/evmtest b/evmtest/evmtest
index 49b162d..cd5e238 100755
--- a/evmtest/evmtest
+++ b/evmtest/evmtest
@@ -28,6 +28,7 @@ usage (){
# placement of a script in tests/
echo "[R] env_validate"
echo "[ ] examples_test"
+ echo "[R] kexec_sig"
echo "[R] kmod_sig"
echo "[R] policy_sig"

diff --git a/evmtest/files/policies/kexec_policy b/evmtest/files/policies/kexec_policy
new file mode 100644
index 0000000..dc00fa7
--- /dev/null
+++ b/evmtest/files/policies/kexec_policy
@@ -0,0 +1,3 @@
+appraise func=KEXEC_KERNEL_CHECK appraise_type=imasig
+measure func=KEXEC_KERNEL_CHECK
+audit func=KEXEC_KERNEL_CHECK
diff --git a/evmtest/tests/kexec_sig.sh b/evmtest/tests/kexec_sig.sh
new file mode 100755
index 0000000..3a9459d
--- /dev/null
+++ b/evmtest/tests/kexec_sig.sh
@@ -0,0 +1,167 @@
+#!/bin/bash
+# Author: David Jacobson <davidj@xxxxxxxxxxxxx>
+TEST="kexec_sig"
+ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )/.."
+source "$ROOT"/files/common.sh
+VERBOSE=0
+POLICY_LOAD="$ROOT"/files/load_policy.sh
+
+# This test validates that IMA measures and appraises signatures on kernel
+# images when trying to kexec, if the current policy requires that.
+usage() {
+ echo ""
+ echo "kexec_sig -k <key> [-i <kernel_image]"
+ echo " [-vh]"
+ echo ""
+ echo " This test must be run as root"
+ echo ""
+ echo " This test validates that IMA prevents kexec-ing to an"
+ echo " unsigned kernel image."
+ echo ""
+ echo ""
+ echo " -k The key for the certificate on the IMA keyring"
+ echo " -i An unsigned kernel image"
+ echo " -h Display this help message"
+ echo " -v Verbose logging"
+ echo ""
+ echo " Note: kexec may require PECOFF signature"
+}
+
+parse_args () {
+ TEMP=$(getopt -o 'k:i:hv' -n 'kexec_sig' -- "$@")
+ eval set -- "$TEMP"
+
+ while true ; do
+ case "$1" in
+ -h) usage; exit 0 ; shift;;
+ -i) KERNEL_IMAGE=$2; shift 2;;
+ -k) IMA_KEY=$2; shift 2;;
+ -v) VERBOSE=1; shift;;
+ --) shift; break;;
+ *) echo "[*] Unrecognized option $1"; exit 1;;
+ esac
+ done
+
+ if [ -z "$IMA_KEY" ]; then
+ usage
+ exit 1
+ else
+ if [ ! -e "$IMA_KEY" ]; then
+ fail "Please provide valid keys"
+ fi
+ fi
+}
+
+
+
+# If the user doesn't provide a kernel image for kexec, get the current
+get_image () {
+ if [ -z "$KERNEL_IMAGE" ]; then
+ v_out "No kernel provided, looking for running kernel"
+ RUNNING_KERNEL=$(uname -r)
+ if [ -e /boot/vmlinuz-"$RUNNING_KERNEL" ]; then
+ KERNEL_IMAGE=/boot/vmlinuz-"$RUNNING_KERNEL"
+ TEMP_LOCATION=$(mktemp)
+ v_out "Copying kernel ($KERNEL_IMAGE) to $TEMP_LOCATION"
+ cp "$KERNEL_IMAGE" "$TEMP_LOCATION"
+ KERNEL_IMAGE="$TEMP_LOCATION"
+ fi
+ else
+ if [ ! -e "$KERNEL_IMAGE" ]; then
+ fail "Kernel image not found..."
+ else
+ v_out "Valid Kernel provided, continuing"
+ fi
+ fi
+}
+
+write_hash () {
+ v_out "Writing file hash on kernel image"
+ evmctl ima_hash -a sha256 -f "$KERNEL_IMAGE"
+}
+
+load_policy () {
+ v_out "Attempting to sign policy..."
+ evmctl ima_sign -f "$ROOT"/files/policies/kexec_policy -k "$IMA_KEY"
+
+ v_out "Loading kexec policy..."
+ if ! "$POLICY_LOAD" kexec_policy &>> /dev/null; then
+ fail "Could not update policy - verify keys"
+ fi
+}
+
+check_unsigned_KEXEC_FILE_LOAD () {
+ v_out "Testing loading an unsigned kernel image using KEXEC_FILE_LOAD"\
+ "syscall"
+ # -s uses the kexec_file_load syscall
+ if ! kexec -s -l "$KERNEL_IMAGE" &>> /dev/null; then
+ v_out "Correctly prevented kexec of an unsigned image"
+ else
+ kexec -s -u
+ fail "kexec loaded instead of rejecting. Unloading and exiting."
+ fi
+}
+
+check_unsigned_KEXEC_LOAD () {
+ v_out "Testing loading an unsigned kernel image using KEXEC_LOAD"\
+ "syscall"
+ if kexec -l "$KERNEL_IMAGE" &>> /dev/null; then
+ kexec -u
+ fail "Kexec loaded unsigned image - unloading"
+ else
+ v_out "Correctly prevented kexec of an unsigned image"
+ fi
+}
+
+sign_image () {
+ v_out "Signing kernel image with provided key..."
+ evmctl ima_sign -f "$KERNEL_IMAGE" -k "$IMA_KEY"
+}
+
+check_signed_KEXEC_FILE_LOAD () {
+ v_out "Testing loading a signed kernel image using KEXEC_FILE_LOAD"\
+ "syscall"
+ if ! kexec -s -l "$KERNEL_IMAGE" &>> /dev/null; then
+ fail "kexec rejected a signed image - possibly due to PECOFF"\
+ "signature"
+ else
+ v_out "kexec correctly loaded signed image...unloading"
+ fi
+
+ kexec -s -u
+}
+
+check_signed_KEXEC_LOAD () {
+ v_out "Testing loading a signed kernel image \
+ (without file descriptor) using KEXEC_LOAD syscall"
+
+ if kexec -l "$KERNEL_IMAGE" &>> /dev/null; then
+ kexec -u
+ fail "Signed image was allowed to load without file descriptor"\
+ "for appraisal. Unloading."
+ fi
+
+ v_out "Correctly prevented loading"
+}
+
+cleanup () {
+v_out "Cleaning up..."
+if [ -n "$TEMP_LOCATION" ]; then
+ rm "$TEMP_LOCATION"
+fi
+}
+
+
+EVMTEST_require_root
+echo "[*] Starting test: $TEST"
+parse_args "$@"
+get_image
+write_hash
+load_policy
+check_unsigned_KEXEC_FILE_LOAD
+check_unsigned_KEXEC_LOAD
+sign_image
+check_signed_KEXEC_FILE_LOAD
+check_signed_KEXEC_LOAD
+cleanup
+passed
--
2.20.1