diff mbox series

[ima-evm-utils,v2] Add ima_policy_check.awk and ima_policy_check.test

Message ID 20230214152258.3553294-1-roberto.sassu@huaweicloud.com (mailing list archive)
State New, archived
Headers show
Series [ima-evm-utils,v2] Add ima_policy_check.awk and ima_policy_check.test | expand

Commit Message

Roberto Sassu Feb. 14, 2023, 3:22 p.m. UTC
From: Roberto Sassu <roberto.sassu@huawei.com>

Add ima_policy_check.awk to check for possible overlapping of a rule being
added by a test with the existing IMA policy (policy replacement by IMA at
the first policy load is not taken into account).

ima_policy_check.awk expects as input the rule to be added, followed by the
IMA policy.

It 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.

Values can be individually checked by the test executing the awk script, to
determine what to do (abort loading, print a warning in case of overlap,
avoid adding an existing rule).

The bit mask allows the test to see multiple statements regarding the new
rule. For example, if the test added anyway an overlapping rule, it could
also see that the policy already contains it at the next test execution,
and does not add it again.

Since ima_policy_check.awk uses GNU extensions (such as the or() function,
or the fourth argument of split()), add gawk as dependency for the CI.

Finally add ima_policy_check.test, to ensure that the awk script behaves as
expected.

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---

Changelog

v1:
- Add gawk dependency
- Just check overlapping, so that possible interference can be reported
  (suggested by Mimi)
- Return a bit mask with different statements from the overlap analysis
- Consider different actions as overlapping (appraise rules could interfer
  with measure rules)
- Store the new and current rule in two separate arrays, and check
  differences for each possible key
- Correctly handle < > operators (format: key>value and key<value)
- Differentiate between < > operators and ^ modifier (modifier after =)
- Adjust tests according to the new goal, which is to detect overlapping

 ci/alpine.sh                |   3 +-
 ci/debian.sh                |   3 +-
 ci/tumbleweed.sh            |   3 +-
 tests/Makefile.am           |   2 +-
 tests/ima_policy_check.awk  | 176 ++++++++++++++++++++++++++++
 tests/ima_policy_check.test | 225 ++++++++++++++++++++++++++++++++++++
 6 files changed, 408 insertions(+), 4 deletions(-)
 create mode 100755 tests/ima_policy_check.awk
 create mode 100755 tests/ima_policy_check.test

Comments

Mimi Zohar Feb. 15, 2023, 11:39 p.m. UTC | #1
On Tue, 2023-02-14 at 16:22 +0100, Roberto Sassu wrote:
> From: Roberto Sassu <roberto.sassu@huawei.com>
> 
> Add ima_policy_check.awk to check for possible overlapping of a rule being
> added by a test with the existing IMA policy (policy replacement by IMA at
> the first policy load is not taken into account).
> 
> ima_policy_check.awk expects as input the rule to be added, followed by the
> IMA policy.
> 
> It 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.
> 
> Values can be individually checked by the test executing the awk script, to
> determine what to do (abort loading, print a warning in case of overlap,
> avoid adding an existing rule).
> 
> The bit mask allows the test to see multiple statements regarding the new
> rule. For example, if the test added anyway an overlapping rule, it could
> also see that the policy already contains it at the next test execution,
> and does not add it again.
> 
> Since ima_policy_check.awk uses GNU extensions (such as the or() function,
> or the fourth argument of split()), add gawk as dependency for the CI.
> 
> Finally add ima_policy_check.test, to ensure that the awk script behaves as
> expected.
> 
> Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>

Roberto, I dropped a couple of your patches from the "next-testing"
branch, assuming the "Introduce expect_pass_if() and expect_fail_if()" 
and this patch are prerequisites for the "Add tests for MMAP_CHECK and
MMAP_CHECK_REQPROT hooks" patch.

thanks,

Mimi
Mimi Zohar Feb. 16, 2023, 3:22 a.m. UTC | #2
Hi Roberto,

> diff --git a/tests/ima_policy_check.awk b/tests/ima_policy_check.awk
> new file mode 100755
> index 00000000000..73107d01083
> --- /dev/null
> +++ b/tests/ima_policy_check.awk
> @@ -0,0 +1,176 @@
> +#! /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 there is at least one policy keyword (in base or lsm)
> +# providing a different value.

The above comment needs to be updated to reflect the overlapping tests.

> Currently, the < > operators and the ^ modifier
> +# are not supported and overlap is asserted even if intervals are disjoint.
> +# Also, despite the MMAP_CHECK and MMAP_CHECK_REQPROT hooks have different
> +# names, they are basically the same hook but with different behavior depending
> +# on external factors, so also in this case overlap has to be asserted. Finally,
> +# the existing aliases PATH_CHECK and FILE_MMAP are converted to the current
> +# hook names, respectively FILE_CHECK and MMAP_CHECK.
> +#
> +# 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.
> +#
> +# 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.
> 
> diff --git a/tests/ima_policy_check.test b/tests/ima_policy_check.test
> new file mode 100755
> index 00000000000..ba8747a74b1
> --- /dev/null
> +++ b/tests/ima_policy_check.test
> @@ -0,0 +1,225 @@
> +#!/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"
> +}
> +
> +# Basic checks.
> +desc="empty IMA policy"
> +rule="measure func=FILE_CHECK"
> +ima_policy=""
> +expect_pass check_result "$desc" "$rule" "$ima_policy" 0

Include the comment, before the tests, as to what the expected return
values mean:
# 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.

> +desc="Empty new rule"
> +rule=""
> +ima_policy=""
> +expect_pass check_result "$desc" "$rule" "$ima_policy" 1
> +
> +desc="Wrong func"

"FILE_CHECK" is actually fine, but the condition keyword "fun" is
invalid. 

> +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 func"
> +rule="measure func=FILE_CHECK"
> +ima_policy="appraise func=MMAP_CHECK"
> +expect_pass check_result "$desc" "$rule" "$ima_policy" 0

All of the non-overlapping tests are non-overlapping by action as well.
Is this intentional?

> +
> +desc="Non-overlapping by uid, func is equal"
> +rule="measure func=FILE_CHECK uid=0"
> +ima_policy="appraise 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="appraise 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="appraise 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="appraise 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="different actions, same func"
> +rule="appraise func=FILE_CHECK"
> +ima_policy="measure func=FILE_CHECK"
> +expect_pass check_result "$desc" "$rule" "$ima_policy" 2

Ok, a "measure" rule overlapping with an existing "appraise" rule could
impact a test,  but the reverse an "appraise" rule overlapping with an
existing "measure" rule should not impact tests.  So overlapping rules
are not necessarily interferring.

> +desc="different actions, same func"
> +rule="appraise func=FILE_CHECK"
> +ima_policy="dont_measure func=FILE_CHECK"
> +expect_pass check_result "$desc" "$rule" "$ima_policy" 2

Similarly, an "appraise" rule should not be impacted by an existing
"dont_measure" rule.

> +desc="different actions, same func"
> +rule="measure func=FILE_CHECK"
> +ima_policy="dont_measure func=FILE_CHECK"
> +expect_pass check_result "$desc" "$rule" "$ima_policy" 2

Right, measure/dont_measure rules for the same func hook overlap.

> +
> +desc="different 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

Right, any combination of measure rules or measure/dont_measure rules
for the same func hook should overlap, if one rule is more restrictive
than the other.

> +desc="different 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"
> +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"
> +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

Right, these rules are equally restrictive, but would overlap when a
file is opened RW.
Roberto Sassu Feb. 16, 2023, 8:18 a.m. UTC | #3
On Wed, 2023-02-15 at 18:39 -0500, Mimi Zohar wrote:
> On Tue, 2023-02-14 at 16:22 +0100, Roberto Sassu wrote:
> > From: Roberto Sassu <roberto.sassu@huawei.com>
> > 
> > Add ima_policy_check.awk to check for possible overlapping of a rule being
> > added by a test with the existing IMA policy (policy replacement by IMA at
> > the first policy load is not taken into account).
> > 
> > ima_policy_check.awk expects as input the rule to be added, followed by the
> > IMA policy.
> > 
> > It 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.
> > 
> > Values can be individually checked by the test executing the awk script, to
> > determine what to do (abort loading, print a warning in case of overlap,
> > avoid adding an existing rule).
> > 
> > The bit mask allows the test to see multiple statements regarding the new
> > rule. For example, if the test added anyway an overlapping rule, it could
> > also see that the policy already contains it at the next test execution,
> > and does not add it again.
> > 
> > Since ima_policy_check.awk uses GNU extensions (such as the or() function,
> > or the fourth argument of split()), add gawk as dependency for the CI.
> > 
> > Finally add ima_policy_check.test, to ensure that the awk script behaves as
> > expected.
> > 
> > Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> 
> Roberto, I dropped a couple of your patches from the "next-testing"
> branch, assuming the "Introduce expect_pass_if() and expect_fail_if()" 
> and this patch are prerequisites for the "Add tests for MMAP_CHECK and
> MMAP_CHECK_REQPROT hooks" patch.

Yes, I send the latter after the former two are in the repo.

Thanks

Roberto
Roberto Sassu Feb. 16, 2023, 8:39 a.m. UTC | #4
On Wed, 2023-02-15 at 22:22 -0500, Mimi Zohar wrote:
> Hi Roberto,
> 
> > diff --git a/tests/ima_policy_check.awk b/tests/ima_policy_check.awk
> > new file mode 100755
> > index 00000000000..73107d01083
> > --- /dev/null
> > +++ b/tests/ima_policy_check.awk
> > @@ -0,0 +1,176 @@
> > +#! /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 there is at least one policy keyword (in base or lsm)
> > +# providing a different value.
> 
> The above comment needs to be updated to reflect the overlapping tests.

Not sure what is missing. Maybe: rules don't overlap also when they are
equivalent (they have the same keys and values)?

> > Currently, the < > operators and the ^ modifier
> > +# are not supported and overlap is asserted even if intervals are disjoint.
> > +# Also, despite the MMAP_CHECK and MMAP_CHECK_REQPROT hooks have different
> > +# names, they are basically the same hook but with different behavior depending
> > +# on external factors, so also in this case overlap has to be asserted. Finally,
> > +# the existing aliases PATH_CHECK and FILE_MMAP are converted to the current
> > +# hook names, respectively FILE_CHECK and MMAP_CHECK.
> > +#
> > +# 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.
> > +#
> > +# 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.
> > 
> > diff --git a/tests/ima_policy_check.test b/tests/ima_policy_check.test
> > new file mode 100755
> > index 00000000000..ba8747a74b1
> > --- /dev/null
> > +++ b/tests/ima_policy_check.test
> > @@ -0,0 +1,225 @@
> > +#!/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"
> > +}
> > +
> > +# Basic checks.
> > +desc="empty IMA policy"
> > +rule="measure func=FILE_CHECK"
> > +ima_policy=""
> > +expect_pass check_result "$desc" "$rule" "$ima_policy" 0
> 
> Include the comment, before the tests, as to what the expected return
> values mean:
> # 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.

Ok.

> > +desc="Empty new rule"
> > +rule=""
> > +ima_policy=""
> > +expect_pass check_result "$desc" "$rule" "$ima_policy" 1
> > +
> > +desc="Wrong func"
> 
> "FILE_CHECK" is actually fine, but the condition keyword "fun" is
> invalid. 

Ok, will fix the description.

> > +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 func"
> > +rule="measure func=FILE_CHECK"
> > +ima_policy="appraise func=MMAP_CHECK"
> > +expect_pass check_result "$desc" "$rule" "$ima_policy" 0
> 
> All of the non-overlapping tests are non-overlapping by action as well.
> Is this intentional?

Yes. Originally, I was considering only related actions (with/without
dont_). But then, appraise rules could interfer with the rest too.

Maybe I should do this instead: consider again related actions and
combinations of actions that include appraise.

> +
> > +desc="Non-overlapping by uid, func is equal"
> > +rule="measure func=FILE_CHECK uid=0"
> > +ima_policy="appraise 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="appraise 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="appraise 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="appraise 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="different actions, same func"
> > +rule="appraise func=FILE_CHECK"
> > +ima_policy="measure func=FILE_CHECK"
> > +expect_pass check_result "$desc" "$rule" "$ima_policy" 2
> 
> Ok, a "measure" rule overlapping with an existing "appraise" rule could
> impact a test,  but the reverse an "appraise" rule overlapping with an
> existing "measure" rule should not impact tests.  So overlapping rules
> are not necessarily interferring.

Uhm, probably it does, when you reexecute the tests again and the
appraise rule is already added. ima_match_policy() parses the policy
until actmask is cleared.

Actually, this was the situation for the MMAP_CHECK and
MMAP_CHECK_REQPROT hooks test.

Roberto

> > +desc="different actions, same func"
> > +rule="appraise func=FILE_CHECK"
> > +ima_policy="dont_measure func=FILE_CHECK"
> > +expect_pass check_result "$desc" "$rule" "$ima_policy" 2
> 
> Similarly, an "appraise" rule should not be impacted by an existing
> "dont_measure" rule.
> 
> > +desc="different actions, same func"
> > +rule="measure func=FILE_CHECK"
> > +ima_policy="dont_measure func=FILE_CHECK"
> > +expect_pass check_result "$desc" "$rule" "$ima_policy" 2
> 
> Right, measure/dont_measure rules for the same func hook overlap.
> 
> > +
> > +desc="different 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
> 
> Right, any combination of measure rules or measure/dont_measure rules
> for the same func hook should overlap, if one rule is more restrictive
> than the other.
> 
> > +desc="different 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"
> > +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"
> > +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
> 
> Right, these rules are equally restrictive, but would overlap when a
> file is opened RW. 
>
Mimi Zohar Feb. 16, 2023, 1:13 p.m. UTC | #5
On Thu, 2023-02-16 at 09:39 +0100, Roberto Sassu wrote:
> On Wed, 2023-02-15 at 22:22 -0500, Mimi Zohar wrote:
> > Hi Roberto,
> > 
> > > diff --git a/tests/ima_policy_check.awk b/tests/ima_policy_check.awk
> > > new file mode 100755
> > > index 00000000000..73107d01083
> > > --- /dev/null
> > > +++ b/tests/ima_policy_check.awk
> > > @@ -0,0 +1,176 @@
> > > +#! /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 there is at least one policy keyword (in base or lsm)
> > > +# providing a different value.
> > 
> > The above comment needs to be updated to reflect the overlapping tests.
> 
> Not sure what is missing. Maybe: rules don't overlap also when they are
> equivalent (they have the same keys and values)?

The above "overlap" definition doesn't take into account one rule being
more restrictive (having more "keys" than the other.)

> 
> > > Currently, the < > operators and the ^ modifier
> > > +# are not supported and overlap is asserted even if intervals are disjoint.
> > > +# Also, despite the MMAP_CHECK and MMAP_CHECK_REQPROT hooks have different
> > > +# names, they are basically the same hook but with different behavior depending
> > > +# on external factors, so also in this case overlap has to be asserted. Finally,
> > > +# the existing aliases PATH_CHECK and FILE_MMAP are converted to the current
> > > +# hook names, respectively FILE_CHECK and MMAP_CHECK.
> > > +#
> > > +# 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.
> > > +#
> > > +# 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.
> > > 
> > > diff --git a/tests/ima_policy_check.test b/tests/ima_policy_check.test
> > > new file mode 100755
> > > index 00000000000..ba8747a74b1
> > > --- /dev/null
> > > +++ b/tests/ima_policy_check.test
> > > @@ -0,0 +1,225 @@
> > > +#!/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"
> > > +}
> > > +
> > > +# Basic checks.
> > > +desc="empty IMA policy"
> > > +rule="measure func=FILE_CHECK"
> > > +ima_policy=""
> > > +expect_pass check_result "$desc" "$rule" "$ima_policy" 0
> > 
> > Include the comment, before the tests, as to what the expected return
> > values mean:
> > # 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.
> 
> Ok.
> 
> > > +desc="Empty new rule"
> > > +rule=""
> > > +ima_policy=""
> > > +expect_pass check_result "$desc" "$rule" "$ima_policy" 1
> > > +
> > > +desc="Wrong func"
> > 
> > "FILE_CHECK" is actually fine, but the condition keyword "fun" is
> > invalid. 
> 
> Ok, will fix the description.
> 
> > > +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 func"
> > > +rule="measure func=FILE_CHECK"
> > > +ima_policy="appraise func=MMAP_CHECK"
> > > +expect_pass check_result "$desc" "$rule" "$ima_policy" 0
> > 
> > All of the non-overlapping tests are non-overlapping by action as well.
> > Is this intentional?
> 
> Yes. Originally, I was considering only related actions (with/without
> dont_). But then, appraise rules could interfer with the rest too.

Ok.  To clarify, an "appraise" rule on an earlier hook (e.g.
file_check), could prevent a "measure" policy rule on a later hook
(e.g. bprm_check, mmap_check).

> 
> Maybe I should do this instead: consider again related actions and
> combinations of actions that include appraise.

Agreed

> 
> > +
> > > +desc="Non-overlapping by uid, func is equal"
> > > +rule="measure func=FILE_CHECK uid=0"
> > > +ima_policy="appraise 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="appraise 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="appraise 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="appraise 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="different actions, same func"
> > > +rule="appraise func=FILE_CHECK"
> > > +ima_policy="measure func=FILE_CHECK"
> > > +expect_pass check_result "$desc" "$rule" "$ima_policy" 2
> > 
> > Ok, a "measure" rule overlapping with an existing "appraise" rule could
> > impact a test,  but the reverse an "appraise" rule overlapping with an
> > existing "measure" rule should not impact tests.  So overlapping rules
> > are not necessarily interferring.
> 
> Uhm, probably it does, when you reexecute the tests again and the
> appraise rule is already added. ima_match_policy() parses the policy
> until actmask is cleared.

Ok

> Actually, this was the situation for the MMAP_CHECK and
> MMAP_CHECK_REQPROT hooks test.
Roberto Sassu Feb. 16, 2023, 1:30 p.m. UTC | #6
On Thu, 2023-02-16 at 08:13 -0500, Mimi Zohar wrote:
> On Thu, 2023-02-16 at 09:39 +0100, Roberto Sassu wrote:
> > On Wed, 2023-02-15 at 22:22 -0500, Mimi Zohar wrote:
> > > Hi Roberto,
> > > 
> > > > diff --git a/tests/ima_policy_check.awk b/tests/ima_policy_check.awk
> > > > new file mode 100755
> > > > index 00000000000..73107d01083
> > > > --- /dev/null
> > > > +++ b/tests/ima_policy_check.awk
> > > > @@ -0,0 +1,176 @@
> > > > +#! /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 there is at least one policy keyword (in base or lsm)
> > > > +# providing a different value.
> > > 
> > > The above comment needs to be updated to reflect the overlapping tests.
> > 
> > Not sure what is missing. Maybe: rules don't overlap also when they are
> > equivalent (they have the same keys and values)?
> 
> The above "overlap" definition doesn't take into account one rule being
> more restrictive (having more "keys" than the other.)

Ok, I see.

Rules don't overlap if both include the same policy keyword (in base or
lsm), at least one, with a different value.

Roberto

> > > > Currently, the < > operators and the ^ modifier
> > > > +# are not supported and overlap is asserted even if intervals are disjoint.
> > > > +# Also, despite the MMAP_CHECK and MMAP_CHECK_REQPROT hooks have different
> > > > +# names, they are basically the same hook but with different behavior depending
> > > > +# on external factors, so also in this case overlap has to be asserted. Finally,
> > > > +# the existing aliases PATH_CHECK and FILE_MMAP are converted to the current
> > > > +# hook names, respectively FILE_CHECK and MMAP_CHECK.
> > > > +#
> > > > +# 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.
> > > > +#
> > > > +# 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.
> > > > 
> > > > diff --git a/tests/ima_policy_check.test b/tests/ima_policy_check.test
> > > > new file mode 100755
> > > > index 00000000000..ba8747a74b1
> > > > --- /dev/null
> > > > +++ b/tests/ima_policy_check.test
> > > > @@ -0,0 +1,225 @@
> > > > +#!/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"
> > > > +}
> > > > +
> > > > +# Basic checks.
> > > > +desc="empty IMA policy"
> > > > +rule="measure func=FILE_CHECK"
> > > > +ima_policy=""
> > > > +expect_pass check_result "$desc" "$rule" "$ima_policy" 0
> > > 
> > > Include the comment, before the tests, as to what the expected return
> > > values mean:
> > > # 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.
> > 
> > Ok.
> > 
> > > > +desc="Empty new rule"
> > > > +rule=""
> > > > +ima_policy=""
> > > > +expect_pass check_result "$desc" "$rule" "$ima_policy" 1
> > > > +
> > > > +desc="Wrong func"
> > > 
> > > "FILE_CHECK" is actually fine, but the condition keyword "fun" is
> > > invalid. 
> > 
> > Ok, will fix the description.
> > 
> > > > +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 func"
> > > > +rule="measure func=FILE_CHECK"
> > > > +ima_policy="appraise func=MMAP_CHECK"
> > > > +expect_pass check_result "$desc" "$rule" "$ima_policy" 0
> > > 
> > > All of the non-overlapping tests are non-overlapping by action as well.
> > > Is this intentional?
> > 
> > Yes. Originally, I was considering only related actions (with/without
> > dont_). But then, appraise rules could interfer with the rest too.
> 
> Ok.  To clarify, an "appraise" rule on an earlier hook (e.g.
> file_check), could prevent a "measure" policy rule on a later hook
> (e.g. bprm_check, mmap_check).
> 
> > Maybe I should do this instead: consider again related actions and
> > combinations of actions that include appraise.
> 
> Agreed
> 
> > > +
> > > > +desc="Non-overlapping by uid, func is equal"
> > > > +rule="measure func=FILE_CHECK uid=0"
> > > > +ima_policy="appraise 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="appraise 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="appraise 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="appraise 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="different actions, same func"
> > > > +rule="appraise func=FILE_CHECK"
> > > > +ima_policy="measure func=FILE_CHECK"
> > > > +expect_pass check_result "$desc" "$rule" "$ima_policy" 2
> > > 
> > > Ok, a "measure" rule overlapping with an existing "appraise" rule could
> > > impact a test,  but the reverse an "appraise" rule overlapping with an
> > > existing "measure" rule should not impact tests.  So overlapping rules
> > > are not necessarily interferring.
> > 
> > Uhm, probably it does, when you reexecute the tests again and the
> > appraise rule is already added. ima_match_policy() parses the policy
> > until actmask is cleared.
> 
> Ok
> 
> > Actually, this was the situation for the MMAP_CHECK and
> > MMAP_CHECK_REQPROT hooks test.
Mimi Zohar Feb. 16, 2023, 1:40 p.m. UTC | #7
On Thu, 2023-02-16 at 14:30 +0100, Roberto Sassu wrote:
> > > > > +# Rules don't overlap if there is at least one policy keyword (in base or lsm)
> > > > > +# providing a different value.
> > > > 
> > > > The above comment needs to be updated to reflect the overlapping tests.
> > > 
> > > Not sure what is missing. Maybe: rules don't overlap also when they are
> > > equivalent (they have the same keys and values)?
> > 
> > The above "overlap" definition doesn't take into account one rule being
> > more restrictive (having more "keys" than the other.)
> 
> Ok, I see.
> 
> Rules don't overlap if both include the same policy keyword (in base or
> lsm), at least one, with a different value.

^keyword(s)
diff mbox series

Patch

diff --git a/ci/alpine.sh b/ci/alpine.sh
index 6b17942a43f..0ab648e45e8 100755
--- a/ci/alpine.sh
+++ b/ci/alpine.sh
@@ -45,7 +45,8 @@  apk add \
 	util-linux \
 	wget \
 	which \
-	xxd
+	xxd \
+	gawk
 
 if [ ! "$TSS" ]; then
 	apk add git
diff --git a/ci/debian.sh b/ci/debian.sh
index 431203aabb4..76761912a81 100755
--- a/ci/debian.sh
+++ b/ci/debian.sh
@@ -53,7 +53,8 @@  $apt \
 	sudo \
 	util-linux \
 	wget \
-	xsltproc
+	xsltproc \
+	gawk
 
 $apt xxd || $apt vim-common
 $apt libengine-gost-openssl1.1$ARCH || true
diff --git a/ci/tumbleweed.sh b/ci/tumbleweed.sh
index 6f70b0fcc76..c4bd75ea413 100755
--- a/ci/tumbleweed.sh
+++ b/ci/tumbleweed.sh
@@ -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
diff --git a/tests/Makefile.am b/tests/Makefile.am
index a0463b7b5b5..9a7d8a1f989 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -2,7 +2,7 @@  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
 
 .PHONY: check_logs
 check_logs:
diff --git a/tests/ima_policy_check.awk b/tests/ima_policy_check.awk
new file mode 100755
index 00000000000..73107d01083
--- /dev/null
+++ b/tests/ima_policy_check.awk
@@ -0,0 +1,176 @@ 
+#! /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 there is at least one policy keyword (in base or lsm)
+# providing a different value. Currently, the < > operators and the ^ modifier
+# are not supported and overlap is asserted even if intervals are disjoint.
+# Also, despite the MMAP_CHECK and MMAP_CHECK_REQPROT hooks have different
+# names, they are basically the same hook but with different behavior depending
+# on external factors, so also in this case overlap has to be asserted. Finally,
+# the existing aliases PATH_CHECK and FILE_MMAP are converted to the current
+# hook names, respectively FILE_CHECK and MMAP_CHECK.
+#
+# 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.
+#
+# 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 in actions_array)
+		key_types[actions_array[action]]=key_type_action;
+	for (keyword in keywords_array)
+		key_types[keywords_array[keyword]]=key_type_keyword;
+	for (option in options_array)
+		key_types[options_array[option]]=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];
+		}
+	}
+
+	# Go to the next line, to compare the new rule with rules in the IMA policy.
+	if (new_rule) {
+		new_rule=0;
+		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 is not supported, cannot determine overlap.
+		if (new_rule_array[key] ~ /^\^/ || current_rule_array[key] ~ /^\^/)
+			continue;
+
+		# MMAP_CHECK and MMAP_CHECK_REQPROT are basically the same hook but with different behavior, cannot determine overlap.
+		if (key == "func" && new_rule_array[key] ~ /MMAP_CHECK/ && current_rule_array[key] ~ /MMAP_CHECK/) {
+			continue;
+		}
+
+		# No overlap, 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;
+}
diff --git a/tests/ima_policy_check.test b/tests/ima_policy_check.test
new file mode 100755
index 00000000000..ba8747a74b1
--- /dev/null
+++ b/tests/ima_policy_check.test
@@ -0,0 +1,225 @@ 
+#!/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"
+}
+
+# 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="Wrong func"
+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 func"
+rule="measure func=FILE_CHECK"
+ima_policy="appraise func=MMAP_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="appraise 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="appraise 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="appraise 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="appraise 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="different actions, same func"
+rule="appraise func=FILE_CHECK"
+ima_policy="measure func=FILE_CHECK"
+expect_pass check_result "$desc" "$rule" "$ima_policy" 2
+
+desc="different actions, same func"
+rule="appraise func=FILE_CHECK"
+ima_policy="dont_measure func=FILE_CHECK"
+expect_pass check_result "$desc" "$rule" "$ima_policy" 2
+
+desc="different 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="different 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="different 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"
+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"
+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 action, 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="different 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"
+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"
+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 action, 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 action, same func with alias"
+rule="measure func=FILE_CHECK"
+ima_policy="measure func=PATH_CHECK"
+expect_pass check_result "$desc" "$rule" "$ima_policy" 4
+
+desc="same action, same func with alias, 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 action, same func with alias 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 action, same func 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: different actions"
+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: different actions, 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, 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, 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, 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