Message ID | 20230126163812.1870942-3-roberto.sassu@huaweicloud.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | [ima-evm-utils] Add tests for MMAP_CHECK and MMAP_CHECK_REQPROT hooks | expand |
On 1/26/23 11:38, Roberto Sassu wrote: > From: Roberto Sassu <roberto.sassu@huawei.com> > > Add tests to ensure that, after applying the kernel patch 'ima: Align > ima_file_mmap() parameters with mmap_file LSM hook', the MMAP_CHECK hook > checks the protections applied by the kernel and not those requested by the > application. > > Also ensure that after applying 'ima: Introduce MMAP_CHECK_REQPROT hook', > the MMAP_CHECK_REQPROT hook checks the protections requested by the > application. below LGTM How do you tell the user that the patches need to be applied for the test to succeed and not worry about it when the patches are not applied? > > Test both with the test_mmap application that by default requests the > PROT_READ protection flag. Its syntax is: > > + > +check_mmap() { > + local hook="$1" > + local arg="$2" > + local test_file > + local fowner > + local rule > + local result > + local test_file_entry > + you can write them all in one line: 'local test_file fowner rule result test_file_entry'
On Thu, 2023-01-26 at 17:25 -0500, Stefan Berger wrote: > > On 1/26/23 11:38, Roberto Sassu wrote: > > From: Roberto Sassu <roberto.sassu@huawei.com> > > > > Add tests to ensure that, after applying the kernel patch 'ima: Align > > ima_file_mmap() parameters with mmap_file LSM hook', the MMAP_CHECK hook > > checks the protections applied by the kernel and not those requested by the > > application. > > > > Also ensure that after applying 'ima: Introduce MMAP_CHECK_REQPROT hook', > > the MMAP_CHECK_REQPROT hook checks the protections requested by the > > application. > > below LGTM > > How do you tell the user that the patches need to be applied for the test to > succeed and not worry about it when the patches are not applied? Uhm, I agree. I should at least write a comment as for EVM portable signatures, and maybe display a message in the test logs. > > Test both with the test_mmap application that by default requests the > > PROT_READ protection flag. Its syntax is: > > > > + > > +check_mmap() { > > + local hook="$1" > > + local arg="$2" > > + local test_file > > + local fowner > > + local rule > > + local result > > + local test_file_entry > > + > > you can write them all in one line: 'local test_file fowner rule result test_file_entry' Ok. Thanks Roberto
[Trimmed Cc list, since this is an ima-evm-utils discussion. Adding Petr.] On Fri, 2023-01-27 at 08:57 +0100, Roberto Sassu wrote: > On Thu, 2023-01-26 at 17:25 -0500, Stefan Berger wrote: > > How do you tell the user that the patches need to be applied for the test to > > succeed and not worry about it when the patches are not applied? > > Uhm, I agree. I should at least write a comment as for EVM portable > signatures, and maybe display a message in the test logs. This is a generic problem that needs to be addressed. FYI, LTP addressed it by introducing "struct test_tag" in commit ca2c76990 ("lib: Add support for test tags"). thanks, Mimi
On Mon, 2023-01-30 at 08:28 -0500, Mimi Zohar wrote: > [Trimmed Cc list, since this is an ima-evm-utils discussion. Adding > Petr.] > > On Fri, 2023-01-27 at 08:57 +0100, Roberto Sassu wrote: > > On Thu, 2023-01-26 at 17:25 -0500, Stefan Berger wrote: > > > How do you tell the user that the patches need to be applied for the test to > > > succeed and not worry about it when the patches are not applied? > > > > Uhm, I agree. I should at least write a comment as for EVM portable > > signatures, and maybe display a message in the test logs. > > This is a generic problem that needs to be addressed. FYI, LTP > addressed it by introducing "struct test_tag" in commit ca2c76990 > ("lib: Add support for test tags"). One idea could be to list all the patches the group of tests is going to check, and add an argument to expect_pass and expect_fail to specify the indexes of patches required for the test. We print the required patches in an error message. Roberto
On Mon, 2023-01-30 at 15:02 +0100, Roberto Sassu wrote: > On Mon, 2023-01-30 at 08:28 -0500, Mimi Zohar wrote: > > [Trimmed Cc list, since this is an ima-evm-utils discussion. Adding > > Petr.] > > > > On Fri, 2023-01-27 at 08:57 +0100, Roberto Sassu wrote: > > > On Thu, 2023-01-26 at 17:25 -0500, Stefan Berger wrote: > > > > How do you tell the user that the patches need to be applied for the test to > > > > succeed and not worry about it when the patches are not applied? > > > > > > Uhm, I agree. I should at least write a comment as for EVM portable > > > signatures, and maybe display a message in the test logs. > > > > This is a generic problem that needs to be addressed. FYI, LTP > > addressed it by introducing "struct test_tag" in commit ca2c76990 > > ("lib: Add support for test tags"). > > One idea could be to list all the patches the group of tests is going > to check, and add an argument to expect_pass and expect_fail to specify > the indexes of patches required for the test. We print the required > patches in an error message. Ok, here is an example for this patch set. I added the following changes to the mmap_check.test script: PATCHES=( 'ima: Align ima_file_mmap() parameters with mmap_file LSM hook' 'ima: Introduce MMAP_CHECK_REQPROT hook' ) [...] expect_fail check_mmap "MMAP_CHECK" "" expect_pass check_mmap "MMAP_CHECK" "exec" expect_pass_if '0' check_mmap "MMAP_CHECK" "read_implies_exec" expect_fail_if '1' check_mmap "MMAP_CHECK_REQPROT" "" expect_pass_if '1' check_mmap "MMAP_CHECK_REQPROT" "exec" expect_fail_if '1' check_mmap "MMAP_CHECK_REQPROT" "read_implies_exec" expect_pass check_deny "MMAP_CHECK" "mprotect" expect_pass_if '1' check_deny "MMAP_CHECK_REQPROT" "mprotect" expect_pass check_deny "MMAP_CHECK" "exec_on_writable" expect_pass_if '1' check_deny "MMAP_CHECK_REQPROT" "exec_on_writable" I didn't add the _if suffix for the tests that are expected to pass/fail without adding new patches. I introduced expect_pass_if() and expect_fail_if() that call respectively expect_pass() and expect_fail(), and additionally print an error message with the patches that might be required. The test results (without the two kernel patches applied) are: Test: check_mmap (hook="MMAP_CHECK", test_mmap arg: "") Result (expect not found): not found Test: check_mmap (hook="MMAP_CHECK", test_mmap arg: "exec") Result (expect found): found Test: check_mmap (hook="MMAP_CHECK", test_mmap arg: "read_implies_exec") Result (expect found): not found Possibly missing patches: - ima: Align ima_file_mmap() parameters with mmap_file LSM hook Test: check_mmap (hook="MMAP_CHECK_REQPROT", test_mmap arg: "") /home/roberto/repos/ima-evm-utils/tests/mmap_check.test: line 65: echo: write error: Invalid argument Failed to set IMA policy Possibly missing patches: - ima: Introduce MMAP_CHECK_REQPROT hook Test: check_mmap (hook="MMAP_CHECK_REQPROT", test_mmap arg: "exec") /home/roberto/repos/ima-evm-utils/tests/mmap_check.test: line 65: echo: write error: Invalid argument Failed to set IMA policy Possibly missing patches: - ima: Introduce MMAP_CHECK_REQPROT hook Test: check_mmap (hook="MMAP_CHECK_REQPROT", test_mmap arg: "read_implies_exec") /home/roberto/repos/ima-evm-utils/tests/mmap_check.test: line 65: echo: write error: Invalid argument Failed to set IMA policy Possibly missing patches: - ima: Introduce MMAP_CHECK_REQPROT hook Test: check_deny (hook="MMAP_CHECK", test_mmap arg: "mprotect") Result (expect denied): denied Test: check_deny (hook="MMAP_CHECK_REQPROT", test_mmap arg: "mprotect") /home/roberto/repos/ima-evm-utils/tests/mmap_check.test: line 65: echo: write error: Invalid argument Failed to set IMA policy Possibly missing patches: - ima: Introduce MMAP_CHECK_REQPROT hook Test: check_deny (hook="MMAP_CHECK", test_mmap arg: "exec_on_writable") Result (expect denied): denied Test: check_deny (hook="MMAP_CHECK_REQPROT", test_mmap arg: "exec_on_writable") /home/roberto/repos/ima-evm-utils/tests/mmap_check.test: line 65: echo: write error: Invalid argument Failed to set IMA policy Possibly missing patches: - ima: Introduce MMAP_CHECK_REQPROT hook ================================= Run with FAILEARLY=1 /home/roberto/repos/ima-evm-utils/tests/mmap_check.test _cleanup_env cleanup To stop after first failure ================================= PASS: 4 SKIP: 0 FAIL: 6 The test results (with the two kernel patches applied) are: Test: check_mmap (hook="MMAP_CHECK", test_mmap arg: "") Result (expect not found): not found Test: check_mmap (hook="MMAP_CHECK", test_mmap arg: "exec") Result (expect found): found Test: check_mmap (hook="MMAP_CHECK", test_mmap arg: "read_implies_exec") Result (expect found): found Test: check_mmap (hook="MMAP_CHECK_REQPROT", test_mmap arg: "") Result (expect not found): not found Test: check_mmap (hook="MMAP_CHECK_REQPROT", test_mmap arg: "exec") Result (expect found): found Test: check_mmap (hook="MMAP_CHECK_REQPROT", test_mmap arg: "read_implies_exec") Result (expect not found): not found Test: check_deny (hook="MMAP_CHECK", test_mmap arg: "mprotect") Result (expect denied): denied Test: check_deny (hook="MMAP_CHECK_REQPROT", test_mmap arg: "mprotect") Result (expect denied): denied Test: check_deny (hook="MMAP_CHECK", test_mmap arg: "exec_on_writable") Result (expect denied): denied Test: check_deny (hook="MMAP_CHECK_REQPROT", test_mmap arg: "exec_on_writable") Result (expect denied): denied PASS: 10 SKIP: 0 FAIL: 0 Roberto
On Mon, 2023-01-30 at 15:02 +0100, Roberto Sassu wrote: > On Mon, 2023-01-30 at 08:28 -0500, Mimi Zohar wrote: > > [Trimmed Cc list, since this is an ima-evm-utils discussion. Adding > > Petr.] > > > > On Fri, 2023-01-27 at 08:57 +0100, Roberto Sassu wrote: > > > On Thu, 2023-01-26 at 17:25 -0500, Stefan Berger wrote: > > > > How do you tell the user that the patches need to be applied for the test to > > > > succeed and not worry about it when the patches are not applied? > > > > > > Uhm, I agree. I should at least write a comment as for EVM portable > > > signatures, and maybe display a message in the test logs. > > > > This is a generic problem that needs to be addressed. FYI, LTP > > addressed it by introducing "struct test_tag" in commit ca2c76990 > > ("lib: Add support for test tags"). > > One idea could be to list all the patches the group of tests is going > to check, and add an argument to expect_pass and expect_fail to specify > the indexes of patches required for the test. We print the required > patches in an error message. It's not clear to me what is meant by "group of tests". Is this at the granularity of the test - portable signatures, fsverity, boot_aggregate, etc? Or, is this at a new grouping of tests?
On Mon, 2023-01-30 at 11:26 -0500, Mimi Zohar wrote: > On Mon, 2023-01-30 at 15:02 +0100, Roberto Sassu wrote: > > On Mon, 2023-01-30 at 08:28 -0500, Mimi Zohar wrote: > > > [Trimmed Cc list, since this is an ima-evm-utils discussion. Adding > > > Petr.] > > > > > > On Fri, 2023-01-27 at 08:57 +0100, Roberto Sassu wrote: > > > > On Thu, 2023-01-26 at 17:25 -0500, Stefan Berger wrote: > > > > > How do you tell the user that the patches need to be applied for the test to > > > > > succeed and not worry about it when the patches are not applied? > > > > > > > > Uhm, I agree. I should at least write a comment as for EVM portable > > > > signatures, and maybe display a message in the test logs. > > > > > > This is a generic problem that needs to be addressed. FYI, LTP > > > addressed it by introducing "struct test_tag" in commit ca2c76990 > > > ("lib: Add support for test tags"). > > > > One idea could be to list all the patches the group of tests is going > > to check, and add an argument to expect_pass and expect_fail to specify > > the indexes of patches required for the test. We print the required > > patches in an error message. > > It's not clear to me what is meant by "group of tests". Is this at > the granularity of the test - portable signatures, fsverity, > boot_aggregate, etc? Or, is this at a new grouping of tests? Sorry, it wasn't clear. I meant all the tests defined in a test script. The idea is to associate a list of array indexes with each test (argument of expect_pass() or expect_fail()). The indexes refer to the PATCHES variable. Theoretically, you could also define PATCHES in a common script, called by all test scripts, and specify indexes of that array in the test scripts. I already have a patch, I could send it. Maybe it is more clear. Roberto
On Mon, 2023-01-30 at 17:07 +0100, Roberto Sassu wrote: > On Mon, 2023-01-30 at 15:02 +0100, Roberto Sassu wrote: > > On Mon, 2023-01-30 at 08:28 -0500, Mimi Zohar wrote: > > > [Trimmed Cc list, since this is an ima-evm-utils discussion. Adding > > > Petr.] > > > > > > On Fri, 2023-01-27 at 08:57 +0100, Roberto Sassu wrote: > > > > On Thu, 2023-01-26 at 17:25 -0500, Stefan Berger wrote: > > > > > How do you tell the user that the patches need to be applied for the test to > > > > > succeed and not worry about it when the patches are not applied? > > > > > > > > Uhm, I agree. I should at least write a comment as for EVM portable > > > > signatures, and maybe display a message in the test logs. > > > > > > This is a generic problem that needs to be addressed. FYI, LTP > > > addressed it by introducing "struct test_tag" in commit ca2c76990 > > > ("lib: Add support for test tags"). > > > > One idea could be to list all the patches the group of tests is going > > to check, and add an argument to expect_pass and expect_fail to specify > > the indexes of patches required for the test. We print the required > > patches in an error message. > > Ok, here is an example for this patch set. I added the following > changes to the mmap_check.test script: > > PATCHES=( > 'ima: Align ima_file_mmap() parameters with mmap_file LSM hook' > 'ima: Introduce MMAP_CHECK_REQPROT hook' > ) This works for bug fixes, where the patch list is relatively small. I'm not sure this will work so well for new kernel features.
On Mon, 2023-01-30 at 11:54 -0500, Mimi Zohar wrote: > On Mon, 2023-01-30 at 17:07 +0100, Roberto Sassu wrote: > > On Mon, 2023-01-30 at 15:02 +0100, Roberto Sassu wrote: > > > On Mon, 2023-01-30 at 08:28 -0500, Mimi Zohar wrote: > > > > [Trimmed Cc list, since this is an ima-evm-utils discussion. Adding > > > > Petr.] > > > > > > > > On Fri, 2023-01-27 at 08:57 +0100, Roberto Sassu wrote: > > > > > On Thu, 2023-01-26 at 17:25 -0500, Stefan Berger wrote: > > > > > > How do you tell the user that the patches need to be applied for the test to > > > > > > succeed and not worry about it when the patches are not applied? > > > > > > > > > > Uhm, I agree. I should at least write a comment as for EVM portable > > > > > signatures, and maybe display a message in the test logs. > > > > > > > > This is a generic problem that needs to be addressed. FYI, LTP > > > > addressed it by introducing "struct test_tag" in commit ca2c76990 > > > > ("lib: Add support for test tags"). > > > > > > One idea could be to list all the patches the group of tests is going > > > to check, and add an argument to expect_pass and expect_fail to specify > > > the indexes of patches required for the test. We print the required > > > patches in an error message. > > > > Ok, here is an example for this patch set. I added the following > > changes to the mmap_check.test script: > > > > PATCHES=( > > 'ima: Align ima_file_mmap() parameters with mmap_file LSM hook' > > 'ima: Introduce MMAP_CHECK_REQPROT hook' > > ) > > This works for bug fixes, where the patch list is relatively small. > I'm not sure this will work so well for new kernel features. For new features, it is probably easier check at the beginning of the tests if the feature is available and, if not, skip them. Roberto
On Mon, 2023-01-30 at 17:36 +0100, Roberto Sassu wrote: > On Mon, 2023-01-30 at 11:26 -0500, Mimi Zohar wrote: > > On Mon, 2023-01-30 at 15:02 +0100, Roberto Sassu wrote: > > > On Mon, 2023-01-30 at 08:28 -0500, Mimi Zohar wrote: > > > > [Trimmed Cc list, since this is an ima-evm-utils discussion. Adding > > > > Petr.] > > > > > > > > On Fri, 2023-01-27 at 08:57 +0100, Roberto Sassu wrote: > > > > > On Thu, 2023-01-26 at 17:25 -0500, Stefan Berger wrote: > > > > > > How do you tell the user that the patches need to be applied for the test to > > > > > > succeed and not worry about it when the patches are not applied? > > > > > > > > > > Uhm, I agree. I should at least write a comment as for EVM portable > > > > > signatures, and maybe display a message in the test logs. > > > > > > > > This is a generic problem that needs to be addressed. FYI, LTP > > > > addressed it by introducing "struct test_tag" in commit ca2c76990 > > > > ("lib: Add support for test tags"). > > > > > > One idea could be to list all the patches the group of tests is going > > > to check, and add an argument to expect_pass and expect_fail to specify > > > the indexes of patches required for the test. We print the required > > > patches in an error message. > > > > It's not clear to me what is meant by "group of tests". Is this at > > the granularity of the test - portable signatures, fsverity, > > boot_aggregate, etc? Or, is this at a new grouping of tests? > > Sorry, it wasn't clear. I meant all the tests defined in a test script. No problems. I hadn't noticed your subsequent example. > > The idea is to associate a list of array indexes with each test > (argument of expect_pass() or expect_fail()). The indexes refer to the > PATCHES variable. > > Theoretically, you could also define PATCHES in a common script, called > by all test scripts, and specify indexes of that array in the test > scripts. > > I already have a patch, I could send it. Maybe it is more clear. Yes, I saw what you meant in the subsequent email. Mimi
diff --git a/tests/Makefile.am b/tests/Makefile.am index a0463b7b5b5d..ca9c4ca18380 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -2,7 +2,9 @@ check_SCRIPTS = TESTS = $(check_SCRIPTS) check_SCRIPTS += ima_hash.test sign_verify.test boot_aggregate.test \ - fsverity.test portable_signatures.test + fsverity.test portable_signatures.test mmap_check.test + +check_PROGRAMS := test_mmap .PHONY: check_logs check_logs: diff --git a/tests/mmap_check.test b/tests/mmap_check.test new file mode 100755 index 000000000000..2efdd8e01785 --- /dev/null +++ b/tests/mmap_check.test @@ -0,0 +1,282 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (C) 2022-2023 Roberto Sassu <roberto.sassu@huawei.com> +# +# Check the behavior of MMAP_CHECK and MMAP_CHECK_REQPROT + +trap '_report_exit_and_cleanup _cleanup_env cleanup' SIGINT SIGTERM SIGSEGV EXIT + +# Base VERBOSE on the environment variable, if set. +VERBOSE="${VERBOSE:-0}" + +cd "$(dirname "$0")" || exit "$FAIL" +export PATH=$PWD/../src:$PWD:$PATH +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH +. ./functions.sh +_require evmctl + +cleanup() { + if [ "$g_loop_mounted" = "1" ]; then + popd > /dev/null || exit "$FAIL" + umount "$g_mountpoint" + fi + + if [ -n "$g_dev" ]; then + losetup -d "$g_dev" + fi + + if [ -n "$g_image" ]; then + rm -f "$g_image" + fi + + if [ -n "$g_mountpoint" ]; then + rm -Rf "$g_mountpoint" + fi + + if [ -n "$key_path_der" ]; then + rm -f "$key_path_der" + fi +} + +# Use the fsuuid= IMA policy keyword to select only files created/used by the +# tests below. Also use fowner= to differentiate between files created/used by +# individual tests. +IMA_UUID="28b23254-9467-44c0-b6ba-34b12e85a26e" +MMAP_CHECK_FOWNER=2000 +MMAP_CHECK_REQPROT_FOWNER=2001 +MEASURE_MMAP_CHECK_RULE="measure func=MMAP_CHECK fsuuid=$IMA_UUID fowner=$MMAP_CHECK_FOWNER" +MEASURE_MMAP_CHECK_REQPROT_RULE="measure func=MMAP_CHECK_REQPROT fsuuid=$IMA_UUID fowner=$MMAP_CHECK_REQPROT_FOWNER" +APPRAISE_MMAP_CHECK_RULE="appraise func=MMAP_CHECK fsuuid=$IMA_UUID fowner=$MMAP_CHECK_FOWNER" +APPRAISE_MMAP_CHECK_REQPROT_RULE="appraise func=MMAP_CHECK_REQPROT fsuuid=$IMA_UUID fowner=$MMAP_CHECK_REQPROT_FOWNER" + +check_load_ima_rule() { + local rule_loaded + local result + local new_policy + + rule_loaded=$(grep "$1" /sys/kernel/security/ima/policy) + if [ -z "$rule_loaded" ]; then + new_policy=$(mktemp -p "$g_mountpoint") + echo "$1" > "$new_policy" + echo "$new_policy" > /sys/kernel/security/ima/policy + result=$? + rm -f "$new_policy" + + if [ "$result" -ne 0 ]; then + echo "${RED}Failed to set IMA policy${NORM}" + return "$FAIL" + fi + fi + + return "$OK" +} + +check_mmap() { + local hook="$1" + local arg="$2" + local test_file + local fowner + local rule + local result + local test_file_entry + + if ! test_file=$(mktemp -p "$PWD"); then + echo "${RED}Cannot write $test_file${NORM}" + return "$FAIL" + fi + + fowner="$MMAP_CHECK_FOWNER" + rule="$MEASURE_MMAP_CHECK_RULE" + + if [ "$hook" = "MMAP_CHECK_REQPROT" ]; then + fowner="$MMAP_CHECK_REQPROT_FOWNER" + rule="$MEASURE_MMAP_CHECK_REQPROT_RULE" + fi + + if ! chown "$fowner" "$test_file"; then + echo "${RED}Cannot change owner of $test_file${NORM}" + return "$FAIL" + fi + + check_load_ima_rule "$rule" + result=$? + if [ $result -ne "$OK" ]; then + return $result + fi + + test_mmap "$test_file" "$arg" + + if [ "$TFAIL" != "yes" ]; then + echo -n "Test (expect: found): " + else + echo -n "Test (expect: not found): " + fi + + echo -n "${FUNCNAME[0]} (hook=\"$hook\", test_mmap arg: \"$arg\") - " + + test_file_entry=$(awk '$5 == "'"$test_file"'"' < /sys/kernel/security/ima/ascii_runtime_measurements) + if [ -z "$test_file_entry" ]; then + echo "not found" + return "$FAIL" + fi + + echo "found" + return "$OK" +} + +check_deny() { + local hook="$1" + local arg="$2" + local test_file + local fowner + local rule + local result + + if ! test_file=$(mktemp -p "$PWD"); then + echo "${RED}Cannot write $test_file${NORM}" + return "$FAIL" + fi + + echo "test" > "$test_file" + + if ! evmctl ima_sign -a sha256 --key "$key_path" "$test_file" &> /dev/null; then + echo "${RED}Cannot sign $test_file${NORM}" + return "$FAIL" + fi + + fowner="$MMAP_CHECK_FOWNER" + rule="$APPRAISE_MMAP_CHECK_RULE" + + if [ "$hook" = "MMAP_CHECK_REQPROT" ]; then + fowner="$MMAP_CHECK_REQPROT_FOWNER" + rule="$APPRAISE_MMAP_CHECK_REQPROT_RULE" + fi + + if ! chown "$fowner" "$test_file"; then + echo "${RED}Cannot change owner of $test_file${NORM}" + return "$FAIL" + fi + + check_load_ima_rule "$rule" + result=$? + if [ $result -ne "$OK" ]; then + return $result + fi + + if ! test_mmap "$test_file" exec > /dev/null; then + echo "${RED}Cannot read $test_file${NORM}" + return "$FAIL" + fi + + echo -n "Test (expect: denied): ${FUNCNAME[0]} (hook=\"$hook\", test_mmap arg: \"$arg\") - " + if test_mmap "$test_file" "$arg"; then + echo "allowed" + return "$FAIL" + fi + + echo "denied" + + return "$OK" +} + +# Run in the new environment if TST_ENV is set. +_run_env "$TST_KERNEL" "$PWD/$(basename "$0")" "TST_ENV=$TST_ENV TST_KERNEL=$TST_KERNEL PATH=$PATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH VERBOSE=$VERBOSE" + +# Exit from the creator of the new environment. +_exit_env "$TST_KERNEL" + +# Mount filesystems in the new environment. +_init_env + +if [ "$(whoami)" != "root" ]; then + echo "${CYAN}This script must be executed as root${NORM}" + exit "$SKIP" +fi + +if [ ! -f /sys/kernel/security/ima/policy ]; then + echo "${CYAN}IMA policy file not found${NORM}" + exit "$SKIP" +fi + +if ! cat /sys/kernel/security/ima/policy &> /dev/null; then + echo "${CYAN}IMA policy file is not readable${NORM}" + exit "$SKIP" +fi + +if [ -n "$TST_KEY_PATH" ]; then + if [ "${TST_KEY_PATH:0:1}" != "/" ]; then + echo "${RED}Absolute path required for the signing key${NORM}" + exit "$FAIL" + fi + + if [ ! -f "$TST_KEY_PATH" ]; then + echo "${RED}Kernel signing key not found in $TST_KEY_PATH${NORM}" + exit "$FAIL" + fi + + key_path="$TST_KEY_PATH" +elif [ -f "$PWD/../signing_key.pem" ]; then + key_path="$PWD/../signing_key.pem" +elif [ -f "/lib/modules/$(uname -r)/source/certs/signing_key.pem" ]; then + key_path="/lib/modules/$(uname -r)/source/certs/signing_key.pem" +elif [ -f "/lib/modules/$(uname -r)/build/certs/signing_key.pem" ]; then + key_path="/lib/modules/$(uname -r)/build/certs/signing_key.pem" +else + echo "${CYAN}Kernel signing key not found${NORM}" + exit "$SKIP" +fi + +key_path_der=$(mktemp) + +openssl x509 -in "$key_path" -out "$key_path_der" -outform der +if ! keyctl padd asymmetric pubkey %keyring:.ima < "$key_path_der" &> /dev/null; then + echo "${RED}Public key cannot be added to the IMA keyring${NORM}" + exit "$FAIL" +fi + +g_mountpoint=$(mktemp -d) +g_image=$(mktemp) + +if [ -z "$g_mountpoint" ]; then + echo "${RED}Mountpoint directory not created${NORM}" + exit "$FAIL" +fi + +if ! dd if=/dev/zero of="$g_image" bs=1M count=20 &> /dev/null; then + echo "${RED}Cannot create test image${NORM}" + exit "$FAIL" +fi + +g_dev=$(losetup -f "$g_image" --show) +if [ -z "$g_dev" ]; then + echo "${RED}Cannot create loop device${NORM}" + exit "$FAIL" +fi + +if ! mkfs.ext4 -U "$IMA_UUID" -b 4096 "$g_dev" &> /dev/null; then + echo "${RED}Cannot format $g_dev${NORM}" + exit "$FAIL" +fi + +if ! mount -o i_version "$g_dev" "$g_mountpoint"; then + echo "${RED}Cannot mount loop device${NORM}" + exit "$FAIL" +fi + +g_loop_mounted=1 +pushd "$g_mountpoint" > /dev/null || exit "$FAIL" + +expect_fail check_mmap "MMAP_CHECK" "" +expect_pass check_mmap "MMAP_CHECK" "exec" +expect_pass check_mmap "MMAP_CHECK" "read_implies_exec" + +expect_fail check_mmap "MMAP_CHECK_REQPROT" "" +expect_pass check_mmap "MMAP_CHECK_REQPROT" "exec" +expect_fail check_mmap "MMAP_CHECK_REQPROT" "read_implies_exec" + +expect_pass check_deny "MMAP_CHECK" "mprotect" +expect_pass check_deny "MMAP_CHECK_REQPROT" "mprotect" + +expect_pass check_deny "MMAP_CHECK" "exec_on_writable" +expect_pass check_deny "MMAP_CHECK_REQPROT" "exec_on_writable" diff --git a/tests/test_mmap.c b/tests/test_mmap.c new file mode 100644 index 000000000000..c9396f66b3a9 --- /dev/null +++ b/tests/test_mmap.c @@ -0,0 +1,69 @@ +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/personality.h> + +int main(int argc, char *argv[]) +{ + struct stat st; + void *ptr, *ptr_write = NULL; + int ret, fd, fd_write, prot = PROT_READ; + + if (!argv[1]) + return -ENOENT; + + if (argv[2] && !strcmp(argv[2], "read_implies_exec")) { + ret = personality(READ_IMPLIES_EXEC); + if (ret < 0) + return ret; + } + + if (stat(argv[1], &st) == -1) + return -errno; + + if (argv[2] && !strcmp(argv[2], "exec_on_writable")) { + fd_write = open(argv[1], O_RDWR); + if (fd_write == -1) + return -errno; + + ptr_write = mmap(0, st.st_size, PROT_WRITE, MAP_SHARED, + fd_write, 0); + close(fd_write); + + if (ptr_write == (void *)-1) + return -errno; + } + + fd = open(argv[1], O_RDONLY); + if (fd == -1) { + if (ptr_write) + munmap(ptr_write, st.st_size); + + return -errno; + } + + if (argv[2] && !strncmp(argv[2], "exec", 4)) + prot |= PROT_EXEC; + + ptr = mmap(0, st.st_size, prot, MAP_PRIVATE, fd, 0); + + close(fd); + + if (ptr_write) + munmap(ptr_write, st.st_size); + + if (ptr == (void *)-1) + return -errno; + + ret = 0; + + if (argv[2] && !strcmp(argv[2], "mprotect")) + ret = mprotect(ptr, st.st_size, PROT_EXEC); + + munmap(ptr, st.st_size); + return ret; +}