mirror of
https://git.code.sf.net/p/linux-ima/ima-evm-utils
synced 2025-04-27 22:32:31 +02:00
Merge branch 'mmap-check-test' into next
- New ima_policy_check and mmap_check tests - Ability to indicate missing kernel patch on test failure
This commit is contained in:
commit
02c833339b
@ -45,7 +45,8 @@ apk add \
|
||||
util-linux \
|
||||
wget \
|
||||
which \
|
||||
xxd
|
||||
xxd \
|
||||
gawk
|
||||
|
||||
if [ ! "$TSS" ]; then
|
||||
apk add git
|
||||
|
@ -53,7 +53,8 @@ $apt \
|
||||
sudo \
|
||||
util-linux \
|
||||
wget \
|
||||
xsltproc
|
||||
xsltproc \
|
||||
gawk
|
||||
|
||||
$apt xxd || $apt vim-common
|
||||
$apt libengine-gost-openssl1.1$ARCH || true
|
||||
|
@ -42,7 +42,8 @@ zypper --non-interactive install --force-resolution --no-recommends \
|
||||
vim \
|
||||
wget \
|
||||
which \
|
||||
xsltproc
|
||||
xsltproc \
|
||||
gawk
|
||||
|
||||
zypper --non-interactive install --force-resolution --no-recommends \
|
||||
gnutls openssl-engine-libp11 softhsm || true
|
||||
|
@ -2,7 +2,10 @@ 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 ima_policy_check.test \
|
||||
mmap_check.test
|
||||
|
||||
check_PROGRAMS := test_mmap
|
||||
|
||||
.PHONY: check_logs
|
||||
check_logs:
|
||||
|
@ -100,6 +100,25 @@ expect_pass() {
|
||||
return $ret
|
||||
}
|
||||
|
||||
expect_pass_if() {
|
||||
local indexes="$1"
|
||||
local ret idx
|
||||
|
||||
shift
|
||||
|
||||
expect_pass "$@"
|
||||
ret=$?
|
||||
|
||||
if [ $ret -ne 0 ] && [ $ret -ne 77 ] && [ -n "$PATCHES" ]; then
|
||||
echo $YELLOW"Possibly missing patches:"$NORM
|
||||
for idx in $indexes; do
|
||||
echo $YELLOW" - ${PATCHES[$((idx))]}"$NORM
|
||||
done
|
||||
fi
|
||||
|
||||
return $ret
|
||||
}
|
||||
|
||||
# Eval negative test (one that should fail) and account its result
|
||||
expect_fail() {
|
||||
local ret
|
||||
@ -137,6 +156,25 @@ expect_fail() {
|
||||
return $ret
|
||||
}
|
||||
|
||||
expect_fail_if() {
|
||||
local indexes="$1"
|
||||
local ret idx
|
||||
|
||||
shift
|
||||
|
||||
expect_fail "$@"
|
||||
ret=$?
|
||||
|
||||
if { [ $ret -eq 0 ] || [ $ret -eq 99 ]; } && [ -n "$PATCHES" ]; then
|
||||
echo $YELLOW"Possibly missing patches:"$NORM
|
||||
for idx in $indexes; do
|
||||
echo $YELLOW" - ${PATCHES[$((idx))]}"$NORM
|
||||
done
|
||||
fi
|
||||
|
||||
return $ret
|
||||
}
|
||||
|
||||
# return true if current test is positive
|
||||
_test_expected_to_pass() {
|
||||
[ ! $TFAIL ]
|
||||
|
211
tests/ima_policy_check.awk
Executable file
211
tests/ima_policy_check.awk
Executable file
@ -0,0 +1,211 @@
|
||||
#! /usr/bin/gawk -f
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Copyright (C) 2023 Roberto Sassu <roberto.sassu@huawei.com>
|
||||
#
|
||||
# Check a new rule against the loaded IMA policy.
|
||||
#
|
||||
# Documentation/ABI/testing/ima_policy (Linux kernel)
|
||||
# base: [[func=] [mask=] [fsmagic=] [fsuuid=] [fsname=]
|
||||
# [uid=] [euid=] [gid=] [egid=]
|
||||
# [fowner=] [fgroup=]]
|
||||
# lsm: [[subj_user=] [subj_role=] [subj_type=]
|
||||
# [obj_user=] [obj_role=] [obj_type=]]
|
||||
# option: [digest_type=] [template=] [permit_directio]
|
||||
# [appraise_type=] [appraise_flag=]
|
||||
# [appraise_algos=] [keyrings=]
|
||||
#
|
||||
# Rules don't overlap if their actions are unrelated (cannot be matched without
|
||||
# dont_) and there is no combination of appraise with another do action (e.g.
|
||||
# measure, audit, hash). The second condition is due to the fact that appraise
|
||||
# might still forbid other actions expected to be performed by a test that did
|
||||
# not setup appraisal. Checking appraise for new rules is not sufficient,
|
||||
# because that rule could be added anyway. By checking existing rules as well,
|
||||
# a warning will be displayed when tests inserting rules with other do actions
|
||||
# are reexecuted.
|
||||
#
|
||||
# Also, rules don't overlap if both include the same policy keyword(s) (in base
|
||||
# or lsm, except func), at least one, with a different value. Different func
|
||||
# values don't imply non-overlap, due to the fact that a test command might
|
||||
# cause the execution of multiple hooks (e.g. FILE_CHECK in addition to
|
||||
# MMAP_CHECK). Despite one test is willing to test a particular hook, this could
|
||||
# have side effects on other tests (e.g. one test sets: appraise func=MMAP_CHECK
|
||||
# and another: measure func=FILE_CHECK; the second test might see an unexpected
|
||||
# measurement due to the first test being executed; or the second test cannot
|
||||
# unexpectedly do mmap).
|
||||
#
|
||||
# Currently, the < > operators are not supported and overlapping is asserted
|
||||
# even if intervals are disjoint. If supported, non-overlapping conditions could
|
||||
# be found. With the ^ modifier, no disjoint intervals can be found. Overlapping
|
||||
# is always reported.
|
||||
#
|
||||
# Rule equivalence is determined by checking each key/value pair, regardless of
|
||||
# their order. However, the action must always be at the beginning of the rules.
|
||||
# Rules with aliases are considered equivalent to those with their source (e.g.
|
||||
# rules with PATH_CHECK and FILE_MMAP are considered as equivalent to rules with
|
||||
# FILE_CHECK and MMAP_CHECK).
|
||||
#
|
||||
# Return a bit mask with the following values:
|
||||
# - 1: invalid new rule;
|
||||
# - 2: overlap of the new rule with an existing rule in the IMA policy;
|
||||
# - 4: new rule exists in the IMA policy.
|
||||
|
||||
BEGIN {
|
||||
# Policy definitions.
|
||||
actions_str="measure dont_measure appraise dont_appraise audit hash dont_hash"
|
||||
split(actions_str, actions_array);
|
||||
keywords_str="func mask fsmagic fsuuid fsname uid euid gid egid fowner fgroup subj_user subj_role subj_type obj_user obj_role obj_type";
|
||||
split(keywords_str, keywords_array);
|
||||
options_str="digest_type template permit_directio appraise_type appraise_flag appraise_algos keyrings";
|
||||
split(options_str, options_array);
|
||||
|
||||
# Key types.
|
||||
key_type_unknown=0;
|
||||
key_type_action=1;
|
||||
key_type_keyword=2;
|
||||
key_type_option=3;
|
||||
|
||||
# Result values.
|
||||
ret_invalid_rule=1;
|
||||
ret_rule_overlap=2;
|
||||
ret_same_rule_exists=4;
|
||||
|
||||
for (action_idx in actions_array)
|
||||
key_types[actions_array[action_idx]]=key_type_action;
|
||||
for (keyword_idx in keywords_array)
|
||||
key_types[keywords_array[keyword_idx]]=key_type_keyword;
|
||||
for (option_idx in options_array)
|
||||
key_types[options_array[option_idx]]=key_type_option;
|
||||
|
||||
new_rule=1;
|
||||
result=0;
|
||||
}
|
||||
{
|
||||
# Delete arrays from previous rule.
|
||||
if (!new_rule) {
|
||||
delete current_rule_array;
|
||||
delete current_rule_operator_array;
|
||||
}
|
||||
|
||||
# Check empty rules.
|
||||
if (!length($0)) {
|
||||
if (new_rule) {
|
||||
result=or(result, ret_invalid_rule);
|
||||
exit;
|
||||
}
|
||||
next;
|
||||
}
|
||||
|
||||
for (i=1; i<=NF; i++) {
|
||||
# Parse key/value pair.
|
||||
split($i, key_value_array, /[=,>,<]/, separator_array);
|
||||
key=key_value_array[1];
|
||||
value=key_value_array[2];
|
||||
|
||||
if (key == "func") {
|
||||
# Normalize values of IMA hooks to what IMA will print.
|
||||
if (value == "FILE_MMAP")
|
||||
value="MMAP_CHECK";
|
||||
else if (value == "PATH_CHECK")
|
||||
value="FILE_CHECK";
|
||||
}
|
||||
|
||||
# Basic validity check (not necessary in general for the IMA policy, but useful to find typos in the tests).
|
||||
if (key_types[key] == key_type_unknown ||
|
||||
(i == 1 && key_types[key] != key_type_action)) {
|
||||
result=or(result, ret_invalid_rule);
|
||||
exit;
|
||||
}
|
||||
|
||||
# Store key/value pair and operator into an array.
|
||||
if (new_rule) {
|
||||
new_rule_array[key]=value;
|
||||
new_rule_operator_array[key]=separator_array[1];
|
||||
} else {
|
||||
current_rule_array[key]=value;
|
||||
current_rule_operator_array[key]=separator_array[1];
|
||||
}
|
||||
|
||||
# Store original action and action without dont_.
|
||||
if (i == 1) {
|
||||
if (new_rule) {
|
||||
new_rule_action=key;
|
||||
new_rule_action_sub=key;
|
||||
gsub(/dont_/, "", new_rule_action_sub);
|
||||
} else {
|
||||
current_rule_action=key;
|
||||
current_rule_action_sub=key;
|
||||
gsub(/dont_/, "", current_rule_action_sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Go to the next line, to compare the new rule with rules in the IMA policy.
|
||||
if (new_rule) {
|
||||
new_rule=0;
|
||||
next;
|
||||
}
|
||||
|
||||
# No overlap by action (unrelated rules and no combination appraise - <do action>), new rule safe to add to the IMA policy.
|
||||
if (current_rule_action_sub != new_rule_action_sub &&
|
||||
(current_rule_action != "appraise" || new_rule_action ~ /^dont_/) &&
|
||||
(new_rule_action != "appraise" || current_rule_action ~ /^dont_/))
|
||||
next;
|
||||
|
||||
same_rule=1;
|
||||
overlap_rule=1;
|
||||
|
||||
for (key in key_types) {
|
||||
if (!(key in new_rule_array)) {
|
||||
# Key in current rule but not in new rule.
|
||||
if (key in current_rule_array)
|
||||
same_rule=0;
|
||||
# Key not in new rule and not in current rule.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(key in current_rule_array)) {
|
||||
# Key in new rule but not in current rule.
|
||||
if (key in new_rule_array)
|
||||
same_rule=0;
|
||||
# Key not in current rule and not in new rule.
|
||||
continue;
|
||||
}
|
||||
|
||||
# Same value and operator.
|
||||
if (new_rule_array[key] == current_rule_array[key] &&
|
||||
new_rule_operator_array[key] == current_rule_operator_array[key])
|
||||
continue;
|
||||
|
||||
# Different value and/or operator.
|
||||
same_rule=0;
|
||||
|
||||
# Not a policy keyword, not useful to determine overlap.
|
||||
if (key_types[key] != key_type_keyword)
|
||||
continue;
|
||||
|
||||
# > < operators are not supported, cannot determine overlap.
|
||||
if (new_rule_operator_array[key] != "=" || current_rule_operator_array[key] != "=")
|
||||
continue;
|
||||
|
||||
# ^ modifier does not make disjoint sets, cannot determine overlap.
|
||||
if (new_rule_array[key] ~ /^\^/ || current_rule_array[key] ~ /^\^/)
|
||||
continue;
|
||||
|
||||
# One test command can invoke multiple hooks, cannot determine overlap from func.
|
||||
if (key == "func")
|
||||
continue;
|
||||
|
||||
# No overlap by policy keyword, new rule safe to add to the IMA policy.
|
||||
overlap_rule=0;
|
||||
next;
|
||||
}
|
||||
|
||||
if (same_rule)
|
||||
result=or(result, ret_same_rule_exists);
|
||||
else if (overlap_rule)
|
||||
result=or(result, ret_rule_overlap);
|
||||
}
|
||||
END {
|
||||
exit result;
|
||||
}
|
245
tests/ima_policy_check.test
Executable file
245
tests/ima_policy_check.test
Executable file
@ -0,0 +1,245 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Copyright (C) 2023 Roberto Sassu <roberto.sassu@huawei.com>
|
||||
#
|
||||
# Test for ima_policy_check.awk
|
||||
|
||||
trap '_report_exit_and_cleanup' SIGINT SIGTERM EXIT
|
||||
|
||||
cd "$(dirname "$0")" || exit 1
|
||||
. ./functions.sh
|
||||
|
||||
export PATH=$PWD:$PATH
|
||||
|
||||
check_result() {
|
||||
local result
|
||||
|
||||
echo -e "\nTest: $1"
|
||||
echo "New rule: $2"
|
||||
echo "IMA policy: $3"
|
||||
|
||||
echo -n "Result (expect $4): "
|
||||
|
||||
echo -e "$2\n$3" | ima_policy_check.awk
|
||||
result=$?
|
||||
|
||||
if [ "$result" -ne "$4" ]; then
|
||||
echo "${RED}$result${NORM}"
|
||||
return "$FAIL"
|
||||
fi
|
||||
|
||||
echo "${GREEN}$result${NORM}"
|
||||
return "$OK"
|
||||
}
|
||||
|
||||
# ima_policy_check.awk returns a bit mask with the following values:
|
||||
# - 1: invalid new rule;
|
||||
# - 2: overlap of the new rule with an existing rule in the IMA policy;
|
||||
# - 4: new rule exists in the IMA policy.
|
||||
|
||||
# Basic checks.
|
||||
desc="empty IMA policy"
|
||||
rule="measure func=FILE_CHECK"
|
||||
ima_policy=""
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 0
|
||||
|
||||
desc="Empty new rule"
|
||||
rule=""
|
||||
ima_policy=""
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 1
|
||||
|
||||
desc="Unknown policy keyword fun"
|
||||
rule="measure fun=FILE_CHECK"
|
||||
ima_policy=""
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 1
|
||||
|
||||
desc="Missing action"
|
||||
rule="func=FILE_CHECK"
|
||||
ima_policy=""
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 1
|
||||
|
||||
# Non-overlapping rules.
|
||||
desc="Non-overlapping by action measure/dont_appraise, same func"
|
||||
rule="measure func=FILE_CHECK"
|
||||
ima_policy="dont_appraise func=FILE_CHECK"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 0
|
||||
|
||||
desc="Non-overlapping by action audit/dont_appraise, same func"
|
||||
rule="audit func=FILE_CHECK"
|
||||
ima_policy="dont_appraise func=FILE_CHECK"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 0
|
||||
|
||||
desc="Non-overlapping by action appraise/dont_measure, same func"
|
||||
rule="appraise func=FILE_CHECK"
|
||||
ima_policy="dont_measure func=FILE_CHECK"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 0
|
||||
|
||||
desc="Non-overlapping by action dont_measure/hash, same func"
|
||||
rule="dont_measure func=FILE_CHECK"
|
||||
ima_policy="hash func=FILE_CHECK"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 0
|
||||
|
||||
desc="Non-overlapping by uid, func is equal"
|
||||
rule="measure func=FILE_CHECK uid=0"
|
||||
ima_policy="measure uid=1 func=FILE_CHECK"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 0
|
||||
|
||||
desc="Non-overlapping by uid, func is equal, same policy options"
|
||||
rule="measure func=FILE_CHECK uid=0 permit_directio"
|
||||
ima_policy="measure uid=1 func=FILE_CHECK permit_directio"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 0
|
||||
|
||||
desc="Non-overlapping by mask, func and uid are equal, same policy options"
|
||||
rule="measure func=FILE_CHECK uid=0 permit_directio mask=MAY_READ"
|
||||
ima_policy="measure uid=0 mask=MAY_EXEC func=FILE_CHECK permit_directio"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 0
|
||||
|
||||
desc="Non-overlapping by mask, func and uid are equal, different policy options"
|
||||
rule="measure func=FILE_CHECK uid=0 permit_directio mask=MAY_READ"
|
||||
ima_policy="measure uid=0 mask=MAY_EXEC func=FILE_CHECK"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 0
|
||||
|
||||
# Overlapping and different rules.
|
||||
desc="same actions, different keywords"
|
||||
rule="appraise func=FILE_CHECK"
|
||||
ima_policy="appraise uid=0"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 2
|
||||
|
||||
desc="unrelated actions with appraise and a do action, same func"
|
||||
rule="appraise func=FILE_CHECK"
|
||||
ima_policy="measure func=FILE_CHECK"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 2
|
||||
|
||||
desc="unrelated actions with appraise and a do action, different func"
|
||||
rule="appraise func=FILE_CHECK"
|
||||
ima_policy="measure func=MMAP_CHECK"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 2
|
||||
|
||||
desc="related actions, same func"
|
||||
rule="measure func=FILE_CHECK"
|
||||
ima_policy="dont_measure func=FILE_CHECK"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 2
|
||||
|
||||
desc="related actions, same func, different policy options"
|
||||
rule="measure func=FILE_CHECK"
|
||||
ima_policy="dont_measure func=FILE_CHECK permit_directio"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 2
|
||||
|
||||
desc="related actions, same func, different policy options"
|
||||
rule="measure func=FILE_CHECK permit_directio"
|
||||
ima_policy="dont_measure func=FILE_CHECK"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 2
|
||||
|
||||
desc="same actions, same func, same mask with different modifier (no disjoint sets with the ^ modifier)"
|
||||
rule="measure func=FILE_CHECK mask=MAY_EXEC"
|
||||
ima_policy="measure func=FILE_CHECK mask=^MAY_EXEC"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 2
|
||||
|
||||
desc="same actions, same func, different mask with same modifier (no disjoint sets with the ^ modifier)"
|
||||
rule="measure func=FILE_CHECK mask=^MAY_READ"
|
||||
ima_policy="measure func=FILE_CHECK mask=^MAY_EXEC"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 2
|
||||
|
||||
desc="same actions, same func, different policy options"
|
||||
rule="measure func=FILE_CHECK"
|
||||
ima_policy="measure func=FILE_CHECK permit_directio"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 2
|
||||
|
||||
desc="same actions, same func, different policy options"
|
||||
rule="measure func=FILE_CHECK permit_directio"
|
||||
ima_policy="measure func=FILE_CHECK"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 2
|
||||
|
||||
desc="same actions, MMAP_CHECK and MMAP_CHECK_REQPROT hooks"
|
||||
rule="measure func=MMAP_CHECK"
|
||||
ima_policy="measure func=MMAP_CHECK_REQPROT"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 2
|
||||
|
||||
desc="related actions, same func, same mask with same modifier"
|
||||
rule="measure func=FILE_CHECK mask=^MAY_EXEC"
|
||||
ima_policy="dont_measure func=FILE_CHECK mask=^MAY_EXEC"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 2
|
||||
|
||||
desc="same actions, same func, different uid with same operator (overlap because operators are not supported)"
|
||||
rule="measure func=FILE_CHECK uid>0"
|
||||
ima_policy="measure func=FILE_CHECK uid>1"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 2
|
||||
|
||||
desc="same actions, same func, same uid with different operator (overlap because operators are not supported)"
|
||||
rule="measure func=FILE_CHECK uid>1"
|
||||
ima_policy="measure func=FILE_CHECK uid<1"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 2
|
||||
|
||||
# Overlapping and same rules.
|
||||
desc="same actions, same func"
|
||||
rule="appraise func=FILE_CHECK"
|
||||
ima_policy="appraise func=FILE_CHECK"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 4
|
||||
|
||||
desc="same actions, same func, same mask"
|
||||
rule="appraise mask=MAY_READ func=FILE_CHECK"
|
||||
ima_policy="appraise func=FILE_CHECK mask=MAY_READ"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 4
|
||||
|
||||
desc="same actions, same func, same mask, same policy options"
|
||||
rule="appraise mask=MAY_READ func=FILE_CHECK permit_directio appraise_type=imasig"
|
||||
ima_policy="appraise func=FILE_CHECK mask=MAY_READ permit_directio appraise_type=imasig"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 4
|
||||
|
||||
desc="same actions, same func"
|
||||
rule="measure func=MMAP_CHECK_REQPROT"
|
||||
ima_policy="measure func=MMAP_CHECK_REQPROT"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 4
|
||||
|
||||
desc="same actions, same func with alias (PATH_CHECK = FILE_CHECK)"
|
||||
rule="measure func=FILE_CHECK"
|
||||
ima_policy="measure func=PATH_CHECK"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 4
|
||||
|
||||
desc="same actions, same func with alias (PATH_CHECK = FILE_CHECK), same mask with same modifiers"
|
||||
rule="measure mask=^MAY_READ func=FILE_CHECK"
|
||||
ima_policy="measure func=PATH_CHECK mask=^MAY_READ"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 4
|
||||
|
||||
desc="same actions, same func with alias (PATH_CHECK = FILE_CHECK) and same mask with same modifiers, same uid with same operators"
|
||||
rule="measure mask=^MAY_READ uid>0 func=FILE_CHECK"
|
||||
ima_policy="measure func=PATH_CHECK mask=^MAY_READ uid>0"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 4
|
||||
|
||||
desc="same actions, same func with alias (PATH_CHECK = FILE_CHECK) and same mask with same modifiers, same uid with same operators"
|
||||
rule="measure mask=^MAY_READ uid<1 func=FILE_CHECK"
|
||||
ima_policy="measure func=PATH_CHECK mask=^MAY_READ uid<1"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 4
|
||||
|
||||
# Overlapping and two rules (one same, one different).
|
||||
desc="first: same actions, same func, second: unrelated actions with appraise and a do action"
|
||||
rule="appraise func=FILE_CHECK"
|
||||
ima_policy="appraise func=FILE_CHECK\nmeasure func=FILE_CHECK"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 6
|
||||
|
||||
desc="first: unrelated actions with appraise and a do action, same func, second: same actions"
|
||||
rule="appraise func=FILE_CHECK"
|
||||
ima_policy="measure func=FILE_CHECK\nappraise func=FILE_CHECK"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 6
|
||||
|
||||
desc="first: same actions, same func, same mask, second: different policy options"
|
||||
rule="appraise mask=MAY_READ func=FILE_CHECK"
|
||||
ima_policy="appraise func=FILE_CHECK mask=MAY_READ\nappraise func=FILE_CHECK mask=MAY_READ permit_directio"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 6
|
||||
|
||||
desc="first: same actions, same func with alias (PATH_CHECK = FILE_CHECK), same mask, second: different policy options"
|
||||
rule="appraise mask=MAY_READ func=FILE_CHECK"
|
||||
ima_policy="appraise func=PATH_CHECK mask=MAY_READ\nappraise func=FILE_CHECK mask=MAY_READ permit_directio"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 6
|
||||
|
||||
# Non-overlapping and three rules.
|
||||
desc="same actions, same func and mask, different uid"
|
||||
rule="appraise mask=MAY_READ func=FILE_CHECK uid=0"
|
||||
ima_policy="appraise mask=MAY_READ func=FILE_CHECK uid=1\nappraise mask=MAY_READ func=FILE_CHECK uid=2\nappraise mask=MAY_READ func=FILE_CHECK uid=3"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 0
|
||||
|
||||
desc="same actions, same func and mask, different uid, except one that is the same"
|
||||
rule="appraise mask=MAY_READ func=FILE_CHECK uid=0"
|
||||
ima_policy="appraise mask=MAY_READ func=FILE_CHECK uid=1\nappraise mask=MAY_READ func=FILE_CHECK uid=0\nappraise mask=MAY_READ func=FILE_CHECK uid=3"
|
||||
expect_pass check_result "$desc" "$rule" "$ima_policy" 4
|
407
tests/mmap_check.test
Executable file
407
tests/mmap_check.test
Executable file
@ -0,0 +1,407 @@
|
||||
#!/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
|
||||
|
||||
PATCHES=(
|
||||
'ima: Align ima_file_mmap() parameters with mmap_file LSM hook'
|
||||
'ima: Introduce MMAP_CHECK_REQPROT hook'
|
||||
)
|
||||
|
||||
RET_INVALID_RULE=$((0x0001))
|
||||
RET_RULE_OVERLAP=$((0x0002))
|
||||
RET_SAME_RULE_EXISTS=$((0x0004))
|
||||
|
||||
EVM_INIT_HMAC=$((0x0001))
|
||||
EVM_INIT_X509=$((0x0002))
|
||||
|
||||
# Base VERBOSE on the environment variable, if set.
|
||||
VERBOSE="${VERBOSE:-0}"
|
||||
|
||||
# Errors defined in test_mmap
|
||||
ERR_SETUP=1
|
||||
ERR_TEST=2
|
||||
|
||||
cd "$(dirname "$0")" || exit 1
|
||||
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 "$g_key_path_der" ]; then
|
||||
rm -f "$g_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"
|
||||
MEASURE_MMAP_CHECK_FOWNER=2000
|
||||
MEASURE_MMAP_CHECK_REQPROT_FOWNER=2001
|
||||
MEASURE_MMAP_CHECK_RULE="measure func=MMAP_CHECK fsmagic=0xef53 fsuuid=$IMA_UUID fowner=$MEASURE_MMAP_CHECK_FOWNER"
|
||||
MEASURE_MMAP_CHECK_REQPROT_RULE="measure func=MMAP_CHECK_REQPROT fsmagic=0xef53 fsuuid=$IMA_UUID fowner=$MEASURE_MMAP_CHECK_REQPROT_FOWNER"
|
||||
APPRAISE_MMAP_CHECK_FOWNER=2002
|
||||
APPRAISE_MMAP_CHECK_REQPROT_FOWNER=2003
|
||||
APPRAISE_MMAP_CHECK_RULE="appraise func=MMAP_CHECK fsmagic=0xef53 fsuuid=$IMA_UUID fowner=$APPRAISE_MMAP_CHECK_FOWNER"
|
||||
APPRAISE_MMAP_CHECK_REQPROT_RULE="appraise func=MMAP_CHECK_REQPROT fsmagic=0xef53 fsuuid=$IMA_UUID fowner=$APPRAISE_MMAP_CHECK_REQPROT_FOWNER"
|
||||
|
||||
check_load_ima_rule() {
|
||||
local result new_policy color
|
||||
|
||||
echo -e "$1\n$(cat /sys/kernel/security/ima/policy)" | ima_policy_check.awk
|
||||
result=$?
|
||||
|
||||
if [ $((result & RET_INVALID_RULE)) -eq $RET_INVALID_RULE ]; then
|
||||
echo "${RED}Invalid rule${NORM}"
|
||||
return "$HARDFAIL"
|
||||
fi
|
||||
|
||||
if [ $((result & RET_RULE_OVERLAP)) -eq $RET_RULE_OVERLAP ]; then
|
||||
color=${YELLOW}
|
||||
if [ -n "$TST_ENV" ]; then
|
||||
color=${RED}
|
||||
fi
|
||||
|
||||
echo "${color}Possible interference with existing IMA policy rule${NORM}"
|
||||
if [ -n "$TST_ENV" ]; then
|
||||
return "$HARDFAIL"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $((result & RET_SAME_RULE_EXISTS)) -eq $RET_SAME_RULE_EXISTS ]; then
|
||||
return "$OK"
|
||||
fi
|
||||
|
||||
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 "$HARDFAIL"
|
||||
fi
|
||||
|
||||
return "$OK"
|
||||
}
|
||||
|
||||
check_mmap() {
|
||||
local hook="$1"
|
||||
local arg="$2"
|
||||
local test_file fowner rule result test_file_entry
|
||||
|
||||
echo -e "\nTest: ${FUNCNAME[0]} (hook=\"$hook\", test_mmap arg: \"$arg\")"
|
||||
|
||||
if ! test_file=$(mktemp -p "$PWD"); then
|
||||
echo "${RED}Cannot create $test_file${NORM}"
|
||||
return "$HARDFAIL"
|
||||
fi
|
||||
|
||||
if ! echo "test" > "$test_file"; then
|
||||
echo "${RED}Cannot write $test_file${NORM}"
|
||||
return "$FAIL"
|
||||
fi
|
||||
|
||||
fowner="$MEASURE_MMAP_CHECK_FOWNER"
|
||||
rule="$MEASURE_MMAP_CHECK_RULE"
|
||||
|
||||
if [ "$hook" = "MMAP_CHECK_REQPROT" ]; then
|
||||
fowner="$MEASURE_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 "$HARDFAIL"
|
||||
fi
|
||||
|
||||
check_load_ima_rule "$rule"
|
||||
result=$?
|
||||
if [ $result -ne "$OK" ]; then
|
||||
return $result
|
||||
fi
|
||||
|
||||
test_mmap "$test_file" "$arg"
|
||||
result=$?
|
||||
|
||||
if [ $result -ne 0 ] && [ $result -ne "$ERR_TEST" ]; then
|
||||
echo "${RED}Unexpected exit status $result from test_mmap${NORM}"
|
||||
return "$HARDFAIL"
|
||||
fi
|
||||
|
||||
if [ "$TFAIL" != "yes" ]; then
|
||||
echo -n "Result (expect found): "
|
||||
else
|
||||
echo -n "Result (expect not found): "
|
||||
fi
|
||||
|
||||
test_file_entry=$(awk '$5 == "'"$test_file"'"' < /sys/kernel/security/ima/ascii_runtime_measurements)
|
||||
if [ -z "$test_file_entry" ]; then
|
||||
if [ "$TFAIL" != "yes" ]; then
|
||||
echo "${RED}not found${NORM}"
|
||||
else
|
||||
echo "${GREEN}not found${NORM}"
|
||||
fi
|
||||
return "$FAIL"
|
||||
fi
|
||||
|
||||
if [ "$TFAIL" != "yes" ]; then
|
||||
echo "${GREEN}found${NORM}"
|
||||
else
|
||||
echo "${RED}found${NORM}"
|
||||
fi
|
||||
|
||||
if [ "$VERBOSE" -gt 0 ]; then
|
||||
echo "$test_file_entry"
|
||||
fi
|
||||
|
||||
return "$OK"
|
||||
}
|
||||
|
||||
check_deny() {
|
||||
local hook="$1"
|
||||
local arg="$2"
|
||||
local test_file fowner rule result
|
||||
|
||||
echo -e "\nTest: ${FUNCNAME[0]} (hook=\"$hook\", test_mmap arg: \"$arg\")"
|
||||
|
||||
if ! test_file=$(mktemp -p "$PWD"); then
|
||||
echo "${RED}Cannot create $test_file${NORM}"
|
||||
return "$HARDFAIL"
|
||||
fi
|
||||
|
||||
if ! echo "test" > "$test_file"; then
|
||||
echo "${RED}Cannot write $test_file${NORM}"
|
||||
return "$FAIL"
|
||||
fi
|
||||
|
||||
if ! evmctl ima_sign -a sha256 --key "$g_key_path" "$test_file" &> /dev/null; then
|
||||
echo "${RED}Cannot sign $test_file${NORM}"
|
||||
return "$HARDFAIL"
|
||||
fi
|
||||
|
||||
fowner="$APPRAISE_MMAP_CHECK_FOWNER"
|
||||
rule="$APPRAISE_MMAP_CHECK_RULE"
|
||||
|
||||
if [ "$hook" = "MMAP_CHECK_REQPROT" ]; then
|
||||
fowner="$APPRAISE_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 "$HARDFAIL"
|
||||
fi
|
||||
|
||||
check_load_ima_rule "$rule"
|
||||
result=$?
|
||||
if [ $result -ne "$OK" ]; then
|
||||
return $result
|
||||
fi
|
||||
|
||||
test_mmap "$test_file" exec
|
||||
result=$?
|
||||
|
||||
if [ $result -ne 0 ] && [ $result -ne "$ERR_TEST" ]; then
|
||||
echo "${RED}Unexpected exit status $result from test_mmap${NORM}"
|
||||
return "$HARDFAIL"
|
||||
fi
|
||||
|
||||
test_mmap "$test_file" "$arg"
|
||||
result=$?
|
||||
|
||||
if [ $result -ne 0 ] && [ $result -ne "$ERR_TEST" ]; then
|
||||
echo "${RED}Unexpected exit status $result from test_mmap${NORM}"
|
||||
return "$HARDFAIL"
|
||||
fi
|
||||
|
||||
if [ "$TFAIL" != "yes" ]; then
|
||||
echo -n "Result (expect denied): "
|
||||
else
|
||||
echo -n "Result (expect allowed): "
|
||||
fi
|
||||
|
||||
if [ $result -eq 0 ]; then
|
||||
if [ "$TFAIL" != "yes" ]; then
|
||||
echo "${RED}allowed${NORM}"
|
||||
else
|
||||
echo "${GREEN}allowed${NORM}"
|
||||
fi
|
||||
return "$FAIL"
|
||||
fi
|
||||
|
||||
if [ "$TFAIL" != "yes" ]; then
|
||||
echo "${GREEN}denied${NORM}"
|
||||
else
|
||||
echo "${RED}denied${NORM}"
|
||||
fi
|
||||
|
||||
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 TST_KEY_PATH=$TST_KEY_PATH"
|
||||
|
||||
# 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
|
||||
|
||||
g_key_path="$TST_KEY_PATH"
|
||||
elif [ -f "$PWD/../signing_key.pem" ]; then
|
||||
g_key_path="$PWD/../signing_key.pem"
|
||||
elif [ -f "/lib/modules/$(uname -r)/source/certs/signing_key.pem" ]; then
|
||||
g_key_path="/lib/modules/$(uname -r)/source/certs/signing_key.pem"
|
||||
elif [ -f "/lib/modules/$(uname -r)/build/certs/signing_key.pem" ]; then
|
||||
g_key_path="/lib/modules/$(uname -r)/build/certs/signing_key.pem"
|
||||
else
|
||||
echo "${CYAN}Kernel signing key not found${NORM}"
|
||||
exit "$SKIP"
|
||||
fi
|
||||
|
||||
evm_value=$(cat /sys/kernel/security/evm)
|
||||
if [ $((evm_value & EVM_INIT_X509)) -eq "$EVM_INIT_X509" ]; then
|
||||
if [ $((evm_value & EVM_INIT_HMAC)) -ne "$EVM_INIT_HMAC" ]; then
|
||||
echo "${CYAN}Incompatible EVM mode $evm_value${NORM}"
|
||||
exit "$SKIP"
|
||||
fi
|
||||
fi
|
||||
|
||||
g_key_path_der=$(mktemp)
|
||||
|
||||
openssl x509 -in "$g_key_path" -out "$g_key_path_der" -outform der
|
||||
if ! keyctl padd asymmetric pubkey %keyring:.ima < "$g_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 iversion "$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"
|
||||
|
||||
# Ensure that IMA does not add a new measurement entry if an application calls
|
||||
# mmap() with PROT_READ, and a policy rule contains the MMAP_CHECK hook.
|
||||
# In this case, both the protections requested by the application and the final
|
||||
# protections applied by the kernel contain only PROT_READ, so there is no
|
||||
# match with the IMA rule, which expects PROT_EXEC to be set.
|
||||
expect_fail check_mmap "MMAP_CHECK" ""
|
||||
|
||||
# Ensure that IMA adds a new measurement entry if an application calls mmap()
|
||||
# with PROT_READ | PROT_EXEC, and a policy rule contains the MMAP_CHECK hook.
|
||||
expect_pass check_mmap "MMAP_CHECK" "exec"
|
||||
|
||||
# Same as in the first test, but in this case the application calls the
|
||||
# personality() system call with READ_IMPLIES_EXEC, which causes the kernel to
|
||||
# add PROT_EXEC in the final protections passed to the MMAP_CHECK hook.
|
||||
#
|
||||
# Ensure that the bug introduced by 98de59bfe4b2 ("take calculation of final
|
||||
# protections in security_mmap_file() into a helper") is fixed, by passing the
|
||||
# final protections again to the MMAP_CHECK hook. Due to the bug, the hook
|
||||
# received the protections requested by the application. Since those protections
|
||||
# don't have PROT_EXEC, IMA was not creating a measurement entry.
|
||||
expect_pass_if '0' check_mmap "MMAP_CHECK" "read_implies_exec"
|
||||
|
||||
# Repeat the previous three tests, but with the new MMAP_CHECK_REQPROT hook,
|
||||
# which behaves like the buggy MMAP_CHECK hook. In the third test, expect that
|
||||
# no new measurement entry is created, since the MMAP_CHECK_REQPROT hook sees
|
||||
# the protections requested by the application (PROT_READ).
|
||||
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"
|
||||
|
||||
# Ensure that IMA refuses an mprotect() with PROT_EXEC on a memory area
|
||||
# obtained with an mmap() with PROT_READ. This is due to the inability of IMA
|
||||
# to measure/appraise the file for which mmap() was called (locking issue).
|
||||
expect_pass check_deny "MMAP_CHECK" "mprotect"
|
||||
|
||||
# Ensure that MMAP_CHECK_REQPROT has the same behavior of MMAP_CHECK for the
|
||||
# previous test.
|
||||
expect_pass_if '1' check_deny "MMAP_CHECK_REQPROT" "mprotect"
|
||||
|
||||
# Ensure that there cannot be an mmap() with PROT_EXEC on a file with writable
|
||||
# mappings, due to the inability of IMA to make a reliable measurement of that
|
||||
# file.
|
||||
expect_pass check_deny "MMAP_CHECK" "exec_on_writable"
|
||||
|
||||
# Ensure that MMAP_CHECK_REQPROT has the same behavior of MMAP_CHECK for the
|
||||
# previous test.
|
||||
expect_pass_if '1' check_deny "MMAP_CHECK_REQPROT" "exec_on_writable"
|
128
tests/test_mmap.c
Normal file
128
tests/test_mmap.c
Normal file
@ -0,0 +1,128 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2023 Huawei Technologies Duesseldorf GmbH
|
||||
*
|
||||
* Tool to test IMA MMAP_CHECK and MMAP_CHECK_REQPROT hooks.
|
||||
*/
|
||||
#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>
|
||||
|
||||
/*
|
||||
* Convention: return 1 for errors that should not occur, as they are
|
||||
* setup-related, return 2 for errors that might occur due to testing
|
||||
* conditions.
|
||||
*/
|
||||
#define ERR_SETUP 1
|
||||
#define ERR_TEST 2
|
||||
|
||||
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]) {
|
||||
printf("Missing file parameter\n");
|
||||
return ERR_SETUP;
|
||||
}
|
||||
|
||||
if (argv[2] && !strcmp(argv[2], "read_implies_exec")) {
|
||||
ret = personality(READ_IMPLIES_EXEC);
|
||||
if (ret == -1) {
|
||||
printf("Failed to set personality, err: %d (%s)\n",
|
||||
-errno, strerror(errno));
|
||||
return ERR_SETUP;
|
||||
}
|
||||
}
|
||||
|
||||
if (stat(argv[1], &st) == -1) {
|
||||
printf("Failed to access %s, err: %d (%s)\n", argv[1], -errno,
|
||||
strerror(errno));
|
||||
return ERR_SETUP;
|
||||
}
|
||||
|
||||
if (argv[2] && !strcmp(argv[2], "exec_on_writable")) {
|
||||
fd_write = open(argv[1], O_RDWR);
|
||||
if (fd_write == -1) {
|
||||
printf("Failed to open %s in r/w, err: %d (%s)\n",
|
||||
argv[1], -errno, strerror(errno));
|
||||
return ERR_SETUP;
|
||||
}
|
||||
|
||||
ptr_write = mmap(0, st.st_size, PROT_WRITE, MAP_SHARED,
|
||||
fd_write, 0);
|
||||
close(fd_write);
|
||||
|
||||
if (ptr_write == MAP_FAILED) {
|
||||
printf("Failed mmap() with PROT_WRITE on %s, err: %d (%s)\n",
|
||||
argv[1], -errno, strerror(errno));
|
||||
return ERR_SETUP;
|
||||
}
|
||||
}
|
||||
|
||||
fd = open(argv[1], O_RDONLY);
|
||||
if (fd == -1) {
|
||||
printf("Failed to open %s in ro, err: %d (%s)\n", argv[1],
|
||||
-errno, strerror(errno));
|
||||
|
||||
if (ptr_write && munmap(ptr_write, st.st_size) == -1)
|
||||
printf("Failed munmap() of writable mapping on %s, err: %d (%s)\n",
|
||||
argv[1], -errno, strerror(errno));
|
||||
|
||||
return ERR_SETUP;
|
||||
}
|
||||
|
||||
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) == -1) {
|
||||
printf("Failed munmap() of writable mapping on %s, err: %d (%s)\n",
|
||||
argv[1], -errno, strerror(errno));
|
||||
return ERR_SETUP;
|
||||
}
|
||||
|
||||
if (ptr == MAP_FAILED) {
|
||||
ret = ERR_SETUP;
|
||||
if (argv[2] && !strcmp(argv[2], "exec_on_writable") &&
|
||||
errno == EACCES)
|
||||
ret = ERR_TEST;
|
||||
else
|
||||
printf("Failed mmap() with PROT_READ%s on %s, err: %d (%s)\n",
|
||||
(prot & PROT_EXEC) ? " | PROT_EXEC" : "",
|
||||
argv[1], -errno, strerror(errno));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
if (argv[2] && !strcmp(argv[2], "mprotect")) {
|
||||
ret = mprotect(ptr, st.st_size, PROT_EXEC);
|
||||
if (ret == -1) {
|
||||
ret = ERR_SETUP;
|
||||
if (errno == EPERM)
|
||||
ret = ERR_TEST;
|
||||
else
|
||||
printf("Unexpected mprotect() error on %s, err: %d (%s)\n",
|
||||
argv[1], -errno, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
if (munmap(ptr, st.st_size) == -1) {
|
||||
printf("Failed munmap() of mapping on %s, err: %d (%s)\n",
|
||||
argv[1], -errno, strerror(errno));
|
||||
return ERR_SETUP;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user