diff mbox series

IMA: Add test for selinux measurement

Message ID 20210222023805.12846-1-nramas@linux.microsoft.com
State Accepted
Headers show
Series IMA: Add test for selinux measurement | expand

Commit Message

Lakshmi Ramasubramanian Feb. 22, 2021, 2:38 a.m. UTC
New functionality has been added in IMA to measure data that is
critical to the integrity of the system.  SELinux uses this feature
to measure the hash of the SELinux policy loaded in kernel memory,
and the current state of various SELinux configuration.  This new
functionality needs test automation in LTP.

Add test cases which verify that the IMA subsystem correctly
measures the data provided by SELinux.

Signed-off-by: Lakshmi Ramasubramanian <nramas@linux.microsoft.com>
---
This patch is based on commit c7c4cd5e7f3b
("tst_security.sh: Add SELinux helpers")
in https://github.com/pevik/ltp/commits/ima/selinux.v2.draft
in branch ima/selinux.v2.draft.

 runtest/ima                                   |   1 +
 .../kernel/security/integrity/ima/README.md   |  19 ++
 .../security/integrity/ima/datafiles/Makefile |   2 +-
 .../ima/datafiles/ima_selinux/Makefile        |  11 ++
 .../ima/datafiles/ima_selinux/selinux.policy  |   1 +
 .../integrity/ima/tests/ima_selinux.sh        | 180 ++++++++++++++++++
 6 files changed, 213 insertions(+), 1 deletion(-)
 create mode 100644 testcases/kernel/security/integrity/ima/datafiles/ima_selinux/Makefile
 create mode 100644 testcases/kernel/security/integrity/ima/datafiles/ima_selinux/selinux.policy
 create mode 100755 testcases/kernel/security/integrity/ima/tests/ima_selinux.sh

Comments

Petr Vorel Feb. 23, 2021, 6 p.m. UTC | #1
Hi Lakshmi,

> +++ b/testcases/kernel/security/integrity/ima/tests/ima_selinux.sh
...
> +validate_policy_capabilities()
> +{
> +	local measured_cap measured_value expected_value
> +	local result=1
> +	local inx=7
> +
> +	# Policy capabilities flags start from "network_peer_controls"
> +	# in the measured SELinux state at offset 7 for 'awk'
> +	while [ $inx -lt 20 ]; do
> +		measured_cap=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}')
> +		inx=$(( $inx + 1 ))
> +
> +		measured_value=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}')
> +		expected_value=$(cat "$SELINUX_DIR/policy_capabilities/$measured_cap")
> +		if [ "$measured_value" != "$expected_value" ];then
> +			tst_res TWARN "$measured_cap: expected: $expected_value, got: $digest"
We rarely use TWARN in the tests, only when the error is not related to the test result.
Otherwise we use TFAIL.

The rest LGTM.
Reviewed-by: Petr Vorel <pvorel@suse.cz>

I did few formatting and style changes:
https://github.com/pevik/ltp/commits/ima/selinux.v2.fixes
(branch ima/selinux.v2.fixes), see diff below.

As we discuss, I'm going tom merge test when patchset is merged in maintainers tree,
please ping me. And ideally we should mention kernel commit hash as a comment in
the test.

Thanks a lot!

Kind regards,
Petr

diff --git testcases/kernel/security/integrity/ima/tests/ima_selinux.sh testcases/kernel/security/integrity/ima/tests/ima_selinux.sh
index e5060a5e3..ed758631b 100755
--- testcases/kernel/security/integrity/ima/tests/ima_selinux.sh
+++ testcases/kernel/security/integrity/ima/tests/ima_selinux.sh
@@ -13,16 +13,14 @@ TST_SETUP="setup"
 . ima_setup.sh
 
 FUNC_CRITICAL_DATA='func=CRITICAL_DATA'
-REQUIRED_POLICY="^measure.*($FUNC_CRITICAL_DATA)"
+REQUIRED_POLICY="^measure.*$FUNC_CRITICAL_DATA"
 
 setup()
 {
-	SELINUX_DIR=$(tst_get_selinux_dir)
-	if [ -z "$SELINUX_DIR" ]; then
-		tst_brk TCONF "SELinux is not enabled"
-		return
-	fi
+	tst_require_selinux_enabled
 	require_ima_policy_content "$REQUIRED_POLICY" '-E' > $TST_TMPDIR/policy.txt
+
+	SELINUX_DIR=$(tst_get_selinux_dir)
 }
 
 # Format of the measured SELinux state data.
@@ -41,16 +39,16 @@ validate_policy_capabilities()
 	# in the measured SELinux state at offset 7 for 'awk'
 	while [ $inx -lt 20 ]; do
 		measured_cap=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}')
-		inx=$(( $inx + 1 ))
+		inx=$(($inx + 1))
 
 		measured_value=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}')
 		expected_value=$(cat "$SELINUX_DIR/policy_capabilities/$measured_cap")
-		if [ "$measured_value" != "$expected_value" ];then
+		if [ "$measured_value" != "$expected_value" ]; then
 			tst_res TWARN "$measured_cap: expected: $expected_value, got: $digest"
 			result=0
 		fi
 
-		inx=$(( $inx + 1 ))
+		inx=$(($inx + 1))
 	done
 
 	return $result
@@ -109,7 +107,6 @@ test2()
 	local initialized_value
 	local enforced_value expected_enforced_value
 	local checkreqprot_value expected_checkreqprot_value
-	local result
 
 	tst_res TINFO "verifying SELinux state measurement"
 
@@ -149,27 +146,25 @@ test2()
 	measured_data=$(cat $state_file)
 	enforced_value=$(echo $measured_data | awk -F'[=;]' '{print $4}')
 	expected_enforced_value=$(cat $SELINUX_DIR/enforce)
-	if [ "$expected_enforced_value" != "$enforced_value" ];then
+	if [ "$expected_enforced_value" != "$enforced_value" ]; then
 		tst_res TFAIL "enforce: expected: $expected_enforced_value, got: $enforced_value"
 		return
 	fi
 
 	checkreqprot_value=$(echo $measured_data | awk -F'[=;]' '{print $6}')
 	expected_checkreqprot_value=$(cat $SELINUX_DIR/checkreqprot)
-	if [ "$expected_checkreqprot_value" != "$checkreqprot_value" ];then
+	if [ "$expected_checkreqprot_value" != "$checkreqprot_value" ]; then
 		tst_res TFAIL "checkreqprot: expected: $expected_checkreqprot_value, got: $checkreqprot_value"
 		return
 	fi
 
 	initialized_value=$(echo $measured_data | awk -F'[=;]' '{print $2}')
-	if [ "$initialized_value" != "1" ];then
+	if [ "$initialized_value" != "1" ]; then
 		tst_res TFAIL "initialized: expected 1, got: $initialized_value"
 		return
 	fi
 
-	validate_policy_capabilities $measured_data
-	result=$?
-	if [ $result = 0 ]; then
+	if validate_policy_capabilities $measured_data; then
 		tst_res TFAIL "policy capabilities did not match"
 		return
 	fi
Lakshmi Ramasubramanian Feb. 23, 2021, 6:26 p.m. UTC | #2
On 2/23/21 10:00 AM, Petr Vorel wrote:

> 
>> +++ b/testcases/kernel/security/integrity/ima/tests/ima_selinux.sh
> ...
>> +validate_policy_capabilities()
>> +{
>> +	local measured_cap measured_value expected_value
>> +	local result=1
>> +	local inx=7
>> +
>> +	# Policy capabilities flags start from "network_peer_controls"
>> +	# in the measured SELinux state at offset 7 for 'awk'
>> +	while [ $inx -lt 20 ]; do
>> +		measured_cap=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}')
>> +		inx=$(( $inx + 1 ))
>> +
>> +		measured_value=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}')
>> +		expected_value=$(cat "$SELINUX_DIR/policy_capabilities/$measured_cap")
>> +		if [ "$measured_value" != "$expected_value" ];then
>> +			tst_res TWARN "$measured_cap: expected: $expected_value, got: $digest"
> We rarely use TWARN in the tests, only when the error is not related to the test result.
> Otherwise we use TFAIL.
ok - I will change it to TFAIL.

> 
> The rest LGTM.
> Reviewed-by: Petr Vorel <pvorel@suse.cz>
> 
> I did few formatting and style changes:
> https://github.com/pevik/ltp/commits/ima/selinux.v2.fixes
> (branch ima/selinux.v2.fixes), see diff below.
The changes look. Thanks Petr.
I do have one comment - please see below.

> 
> As we discuss, I'm going tom merge test when patchset is merged in maintainers tree,
> please ping me. And ideally we should mention kernel commit hash as a comment in
> the test.
Will do. Thank you.

> 
> Thanks a lot!
> 
> Kind regards,
> Petr
> 
> diff --git testcases/kernel/security/integrity/ima/tests/ima_selinux.sh testcases/kernel/security/integrity/ima/tests/ima_selinux.sh
> index e5060a5e3..ed758631b 100755
> --- testcases/kernel/security/integrity/ima/tests/ima_selinux.sh
> +++ testcases/kernel/security/integrity/ima/tests/ima_selinux.sh
> @@ -13,16 +13,14 @@ TST_SETUP="setup"
>   . ima_setup.sh
>   
>   FUNC_CRITICAL_DATA='func=CRITICAL_DATA'
> -REQUIRED_POLICY="^measure.*($FUNC_CRITICAL_DATA)"
> +REQUIRED_POLICY="^measure.*$FUNC_CRITICAL_DATA"
>   
>   setup()
>   {
> -	SELINUX_DIR=$(tst_get_selinux_dir)
> -	if [ -z "$SELINUX_DIR" ]; then
> -		tst_brk TCONF "SELinux is not enabled"
> -		return
> -	fi
> +	tst_require_selinux_enabled
Please correct me if I have misunderstood this one:

tst_require_selinux_enabled is checking if SELinux is enabled in 
"enforce" mode. Would this check fail if SELinux is enabled in 
"permissive" mode?

For running the test, we just need SELinux to be enabled. I verify that 
by checking for the presence of SELINUX_DIR.

thanks,
  -lakshmi

>   	require_ima_policy_content "$REQUIRED_POLICY" '-E' > $TST_TMPDIR/policy.txt
> +
> +	SELINUX_DIR=$(tst_get_selinux_dir)
>   }
>   
>   # Format of the measured SELinux state data.
> @@ -41,16 +39,16 @@ validate_policy_capabilities()
>   	# in the measured SELinux state at offset 7 for 'awk'
>   	while [ $inx -lt 20 ]; do
>   		measured_cap=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}')
> -		inx=$(( $inx + 1 ))
> +		inx=$(($inx + 1))
>   
>   		measured_value=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}')
>   		expected_value=$(cat "$SELINUX_DIR/policy_capabilities/$measured_cap")
> -		if [ "$measured_value" != "$expected_value" ];then
> +		if [ "$measured_value" != "$expected_value" ]; then
>   			tst_res TWARN "$measured_cap: expected: $expected_value, got: $digest"
>   			result=0
>   		fi
>   
> -		inx=$(( $inx + 1 ))
> +		inx=$(($inx + 1))
>   	done
>   
>   	return $result
> @@ -109,7 +107,6 @@ test2()
>   	local initialized_value
>   	local enforced_value expected_enforced_value
>   	local checkreqprot_value expected_checkreqprot_value
> -	local result
>   
>   	tst_res TINFO "verifying SELinux state measurement"
>   
> @@ -149,27 +146,25 @@ test2()
>   	measured_data=$(cat $state_file)
>   	enforced_value=$(echo $measured_data | awk -F'[=;]' '{print $4}')
>   	expected_enforced_value=$(cat $SELINUX_DIR/enforce)
> -	if [ "$expected_enforced_value" != "$enforced_value" ];then
> +	if [ "$expected_enforced_value" != "$enforced_value" ]; then
>   		tst_res TFAIL "enforce: expected: $expected_enforced_value, got: $enforced_value"
>   		return
>   	fi
>   
>   	checkreqprot_value=$(echo $measured_data | awk -F'[=;]' '{print $6}')
>   	expected_checkreqprot_value=$(cat $SELINUX_DIR/checkreqprot)
> -	if [ "$expected_checkreqprot_value" != "$checkreqprot_value" ];then
> +	if [ "$expected_checkreqprot_value" != "$checkreqprot_value" ]; then
>   		tst_res TFAIL "checkreqprot: expected: $expected_checkreqprot_value, got: $checkreqprot_value"
>   		return
>   	fi
>   
>   	initialized_value=$(echo $measured_data | awk -F'[=;]' '{print $2}')
> -	if [ "$initialized_value" != "1" ];then
> +	if [ "$initialized_value" != "1" ]; then
>   		tst_res TFAIL "initialized: expected 1, got: $initialized_value"
>   		return
>   	fi
>   
> -	validate_policy_capabilities $measured_data
> -	result=$?
> -	if [ $result = 0 ]; then
> +	if validate_policy_capabilities $measured_data; then
>   		tst_res TFAIL "policy capabilities did not match"
>   		return
>   	fi
>
Petr Vorel Feb. 23, 2021, 10:14 p.m. UTC | #3
> On 2/23/21 10:00 AM, Petr Vorel wrote:


> > > +++ b/testcases/kernel/security/integrity/ima/tests/ima_selinux.sh
> > ...
> > > +validate_policy_capabilities()
> > > +{
> > > +	local measured_cap measured_value expected_value
> > > +	local result=1
> > > +	local inx=7
> > > +
> > > +	# Policy capabilities flags start from "network_peer_controls"
> > > +	# in the measured SELinux state at offset 7 for 'awk'
> > > +	while [ $inx -lt 20 ]; do
> > > +		measured_cap=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}')
> > > +		inx=$(( $inx + 1 ))
> > > +
> > > +		measured_value=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}')
> > > +		expected_value=$(cat "$SELINUX_DIR/policy_capabilities/$measured_cap")
> > > +		if [ "$measured_value" != "$expected_value" ];then
> > > +			tst_res TWARN "$measured_cap: expected: $expected_value, got: $digest"
> > We rarely use TWARN in the tests, only when the error is not related to the test result.
> > Otherwise we use TFAIL.
> ok - I will change it to TFAIL.
Thanks!
But I've noticed that this error is handled twice, first in validate_policy_capabilities()
as TWARN (or TFAIL) and then in test2(). Let's use TPASS/TFAIL in
validate_policy_capabilities():

validate_policy_capabilities()
{
	local measured_cap measured_value expected_value
	local inx=7

	# Policy capabilities flags start from "network_peer_controls"
	# in the measured SELinux state at offset 7 for 'awk'
	while [ $inx -lt 20 ]; do
		measured_cap=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}')
		inx=$(($inx + 1))

		measured_value=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}')
		expected_value=$(cat "$SELINUX_DIR/policy_capabilities/$measured_cap")
		if [ "$measured_value" != "$expected_value" ]; then
			tst_res TFAIL "$measured_cap: expected: $expected_value, got: $digest"
			return
		fi

		inx=$(($inx + 1))
	done

	tst_res TPASS "SELinux state measured correctly"
}

test2()
{
	...
	validate_policy_capabilities $measured_data
}

...
> > As we discuss, I'm going tom merge test when patchset is merged in maintainers tree,
> > please ping me. And ideally we should mention kernel commit hash as a comment in
> > the test.
> Will do. Thank you.
Thanks!

...
> > +++ testcases/kernel/security/integrity/ima/tests/ima_selinux.sh
> > @@ -13,16 +13,14 @@ TST_SETUP="setup"
> >   . ima_setup.sh
> >   FUNC_CRITICAL_DATA='func=CRITICAL_DATA'
> > -REQUIRED_POLICY="^measure.*($FUNC_CRITICAL_DATA)"
> > +REQUIRED_POLICY="^measure.*$FUNC_CRITICAL_DATA"
> >   setup()
> >   {
> > -	SELINUX_DIR=$(tst_get_selinux_dir)
> > -	if [ -z "$SELINUX_DIR" ]; then
> > -		tst_brk TCONF "SELinux is not enabled"
> > -		return
> > -	fi
> > +	tst_require_selinux_enabled
> Please correct me if I have misunderstood this one:

> tst_require_selinux_enabled is checking if SELinux is enabled in "enforce"
> mode. Would this check fail if SELinux is enabled in "permissive" mode?

> For running the test, we just need SELinux to be enabled. I verify that by
> checking for the presence of SELINUX_DIR.

Good catch. Your original version is correct (put it back into ima/selinux.v2.fixes).
I didn't put a helper for it, because you need $SELINUX_DIR anyway.
Thus removed tst_require_selinux_enabled() as not needed.

I renamed tst_selinux_enabled() to tst_selinux_enforced() to make the purpose clearer
(commit 82b598ea1 IMA: Add test for selinux measurement).

I've updated branch ima/selinux.v2.fixes with all mentioned changes
https://github.com/pevik/ltp/commits/ima/selinux.v2.fixes

Kind regards,
Petr

> thanks,
>  -lakshmi
Lakshmi Ramasubramanian Feb. 23, 2021, 11:01 p.m. UTC | #4
On 2/23/21 2:14 PM, Petr Vorel wrote:
>> On 2/23/21 10:00 AM, Petr Vorel wrote:
> 
> 
>>>> +++ b/testcases/kernel/security/integrity/ima/tests/ima_selinux.sh
>>> ...
>>>> +validate_policy_capabilities()
>>>> +{
>>>> +	local measured_cap measured_value expected_value
>>>> +	local result=1
>>>> +	local inx=7
>>>> +
>>>> +	# Policy capabilities flags start from "network_peer_controls"
>>>> +	# in the measured SELinux state at offset 7 for 'awk'
>>>> +	while [ $inx -lt 20 ]; do
>>>> +		measured_cap=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}')
>>>> +		inx=$(( $inx + 1 ))
>>>> +
>>>> +		measured_value=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}')
>>>> +		expected_value=$(cat "$SELINUX_DIR/policy_capabilities/$measured_cap")
>>>> +		if [ "$measured_value" != "$expected_value" ];then
>>>> +			tst_res TWARN "$measured_cap: expected: $expected_value, got: $digest"
>>> We rarely use TWARN in the tests, only when the error is not related to the test result.
>>> Otherwise we use TFAIL.
>> ok - I will change it to TFAIL.
> Thanks!
> But I've noticed that this error is handled twice, first in validate_policy_capabilities()
> as TWARN (or TFAIL) and then in test2(). Let's use TPASS/TFAIL in
> validate_policy_capabilities():
Sure - will make that change.

> 
> validate_policy_capabilities()
> {
> 	local measured_cap measured_value expected_value
> 	local inx=7
> 
> 	# Policy capabilities flags start from "network_peer_controls"
> 	# in the measured SELinux state at offset 7 for 'awk'
> 	while [ $inx -lt 20 ]; do
> 		measured_cap=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}')
> 		inx=$(($inx + 1))
> 
> 		measured_value=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}')
> 		expected_value=$(cat "$SELINUX_DIR/policy_capabilities/$measured_cap")
> 		if [ "$measured_value" != "$expected_value" ]; then
> 			tst_res TFAIL "$measured_cap: expected: $expected_value, got: $digest"
> 			return
> 		fi
> 
> 		inx=$(($inx + 1))
> 	done
> 
> 	tst_res TPASS "SELinux state measured correctly"
> }
> 
> test2()
> {
> 	...
> 	validate_policy_capabilities $measured_data
> }
> 
> ...
>>> As we discuss, I'm going tom merge test when patchset is merged in maintainers tree,
>>> please ping me. And ideally we should mention kernel commit hash as a comment in
>>> the test.
>> Will do. Thank you.
> Thanks!
> 
> ...
>>> +++ testcases/kernel/security/integrity/ima/tests/ima_selinux.sh
>>> @@ -13,16 +13,14 @@ TST_SETUP="setup"
>>>    . ima_setup.sh
>>>    FUNC_CRITICAL_DATA='func=CRITICAL_DATA'
>>> -REQUIRED_POLICY="^measure.*($FUNC_CRITICAL_DATA)"
>>> +REQUIRED_POLICY="^measure.*$FUNC_CRITICAL_DATA"
>>>    setup()
>>>    {
>>> -	SELINUX_DIR=$(tst_get_selinux_dir)
>>> -	if [ -z "$SELINUX_DIR" ]; then
>>> -		tst_brk TCONF "SELinux is not enabled"
>>> -		return
>>> -	fi
>>> +	tst_require_selinux_enabled
>> Please correct me if I have misunderstood this one:
> 
>> tst_require_selinux_enabled is checking if SELinux is enabled in "enforce"
>> mode. Would this check fail if SELinux is enabled in "permissive" mode?
> 
>> For running the test, we just need SELinux to be enabled. I verify that by
>> checking for the presence of SELINUX_DIR.
> 
> Good catch. Your original version is correct (put it back into ima/selinux.v2.fixes).
> I didn't put a helper for it, because you need $SELINUX_DIR anyway.
> Thus removed tst_require_selinux_enabled() as not needed.
Thanks

> 
> I renamed tst_selinux_enabled() to tst_selinux_enforced() to make the purpose clearer
> (commit 82b598ea1 IMA: Add test for selinux measurement).
> 
> I've updated branch ima/selinux.v2.fixes with all mentioned changes
> https://github.com/pevik/ltp/commits/ima/selinux.v2.fixes
> 
Thanks again. I'll make my changes in this branch and post the patches 
later this week.

  -lakshmi
Petr Vorel March 16, 2021, 11:54 a.m. UTC | #5
Hi Lakshmi, Mimi,

> New functionality has been added in IMA to measure data that is
> critical to the integrity of the system.  SELinux uses this feature
> to measure the hash of the SELinux policy loaded in kernel memory,
> and the current state of various SELinux configuration.  This new
> functionality needs test automation in LTP.

> Add test cases which verify that the IMA subsystem correctly
> measures the data provided by SELinux.

I finally merged this patchset, noted both kernel commits.
Thanks for this test!

Kind regards,
Petr

* fdd1ffe8a812 ("selinux: include a consumer of the new IMA critical data hook")
(v5.12-rc1-dontuse)
* 2554a48f4437 ("selinux: measure state and policy capabilities")
(to be in v5.13
diff mbox series

Patch

diff --git a/runtest/ima b/runtest/ima
index 5f4b4a7a1..29caa034a 100644
--- a/runtest/ima
+++ b/runtest/ima
@@ -5,4 +5,5 @@  ima_tpm ima_tpm.sh
 ima_violations ima_violations.sh
 ima_keys ima_keys.sh
 ima_kexec ima_kexec.sh
+ima_selinux ima_selinux.sh
 evm_overlay evm_overlay.sh
diff --git a/testcases/kernel/security/integrity/ima/README.md b/testcases/kernel/security/integrity/ima/README.md
index 68d046678..8f2249767 100644
--- a/testcases/kernel/security/integrity/ima/README.md
+++ b/testcases/kernel/security/integrity/ima/README.md
@@ -37,6 +37,25 @@  see example in `kexec.policy`.
 The test attempts to kexec the existing running kernel image.
 To kexec a different kernel image export `IMA_KEXEC_IMAGE=<pathname>`.
 
+### IMA SELinux test
+
+To enable IMA to measure SELinux state and policy, `ima_selinux.sh`
+requires a readable IMA policy, as well as a loaded measure policy with
+`measure func=CRITICAL_DATA label=selinux`,
+see example in `selinux.policy`.
+
+As well as what's required for the IMA tests, SELinux tests require reading
+the IMA policy allowed in the kernel configuration:
+```
+CONFIG_IMA_READ_POLICY=y
+```
+
+The following kernel configuration is also required. It enables compiling
+the Linux Security Module (LSM) namely SELinux.
+```
+CONFIG_SECURITY_SELINUX=y
+```
+
 ## EVM tests
 
 `evm_overlay.sh` requires a builtin IMA appraise tcb policy (e.g. `ima_policy=appraise_tcb`
diff --git a/testcases/kernel/security/integrity/ima/datafiles/Makefile b/testcases/kernel/security/integrity/ima/datafiles/Makefile
index 47a470416..280175b17 100644
--- a/testcases/kernel/security/integrity/ima/datafiles/Makefile
+++ b/testcases/kernel/security/integrity/ima/datafiles/Makefile
@@ -26,6 +26,6 @@  top_srcdir	?= ../../../../../..
 
 include	$(top_srcdir)/include/mk/env_pre.mk
 
-SUBDIRS	:= ima_kexec ima_keys ima_policy
+SUBDIRS	:= ima_kexec ima_keys ima_policy ima_selinux
 
 include $(top_srcdir)/include/mk/generic_trunk_target.mk
diff --git a/testcases/kernel/security/integrity/ima/datafiles/ima_selinux/Makefile b/testcases/kernel/security/integrity/ima/datafiles/ima_selinux/Makefile
new file mode 100644
index 000000000..35088fdbc
--- /dev/null
+++ b/testcases/kernel/security/integrity/ima/datafiles/ima_selinux/Makefile
@@ -0,0 +1,11 @@ 
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) Linux Test Project, 2021
+
+top_srcdir	?= ../../../../../../..
+
+include	$(top_srcdir)/include/mk/env_pre.mk
+
+INSTALL_DIR		:= testcases/data/ima_selinux
+INSTALL_TARGETS	:= *.policy
+
+include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/security/integrity/ima/datafiles/ima_selinux/selinux.policy b/testcases/kernel/security/integrity/ima/datafiles/ima_selinux/selinux.policy
new file mode 100644
index 000000000..7cbe9352d
--- /dev/null
+++ b/testcases/kernel/security/integrity/ima/datafiles/ima_selinux/selinux.policy
@@ -0,0 +1 @@ 
+measure func=CRITICAL_DATA label=selinux
diff --git a/testcases/kernel/security/integrity/ima/tests/ima_selinux.sh b/testcases/kernel/security/integrity/ima/tests/ima_selinux.sh
new file mode 100755
index 000000000..e5060a5e3
--- /dev/null
+++ b/testcases/kernel/security/integrity/ima/tests/ima_selinux.sh
@@ -0,0 +1,180 @@ 
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2021 Microsoft Corporation
+# Author: Lakshmi Ramasubramanian <nramas@linux.microsoft.com>
+#
+# Verify measurement of SELinux policy hash and state.
+
+TST_NEEDS_CMDS="awk cut grep tail"
+TST_CNT=2
+TST_NEEDS_DEVICE=1
+TST_SETUP="setup"
+
+. ima_setup.sh
+
+FUNC_CRITICAL_DATA='func=CRITICAL_DATA'
+REQUIRED_POLICY="^measure.*($FUNC_CRITICAL_DATA)"
+
+setup()
+{
+	SELINUX_DIR=$(tst_get_selinux_dir)
+	if [ -z "$SELINUX_DIR" ]; then
+		tst_brk TCONF "SELinux is not enabled"
+		return
+	fi
+	require_ima_policy_content "$REQUIRED_POLICY" '-E' > $TST_TMPDIR/policy.txt
+}
+
+# Format of the measured SELinux state data.
+#
+# initialized=1;enforcing=0;checkreqprot=1;
+# network_peer_controls=1;open_perms=1;extended_socket_class=1;
+# always_check_network=0;cgroup_seclabel=1;nnp_nosuid_transition=1;
+# genfs_seclabel_symlinks=0;
+validate_policy_capabilities()
+{
+	local measured_cap measured_value expected_value
+	local result=1
+	local inx=7
+
+	# Policy capabilities flags start from "network_peer_controls"
+	# in the measured SELinux state at offset 7 for 'awk'
+	while [ $inx -lt 20 ]; do
+		measured_cap=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}')
+		inx=$(( $inx + 1 ))
+
+		measured_value=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}')
+		expected_value=$(cat "$SELINUX_DIR/policy_capabilities/$measured_cap")
+		if [ "$measured_value" != "$expected_value" ];then
+			tst_res TWARN "$measured_cap: expected: $expected_value, got: $digest"
+			result=0
+		fi
+
+		inx=$(( $inx + 1 ))
+	done
+
+	return $result
+}
+
+# Trigger measurement of SELinux constructs and verify that
+# the measured SELinux policy hash matches the hash of the policy
+# loaded in kernel memory for SELinux.
+test1()
+{
+	local policy_digest expected_policy_digest algorithm
+	local data_source_name="selinux"
+	local pattern="data_sources=[^[:space:]]*$data_source_name"
+	local tmp_file="$TST_TMPDIR/selinux_policy_tmp_file.txt"
+
+	tst_res TINFO "verifying SELinux policy hash measurement"
+
+	# Trigger a measurement by changing SELinux state
+	tst_update_selinux_state
+
+	# Verify SELinux policy hash is measured and then validate that
+	# the measured policy hash matches the hash of the policy currently
+	# in kernel memory for SELinux
+	line=$(grep -E "selinux-policy-hash" $ASCII_MEASUREMENTS | tail -1)
+	if [ -z "$line" ]; then
+		tst_res TFAIL "SELinux policy hash not measured"
+		return
+	fi
+
+	algorithm=$(echo "$line" | cut -d' ' -f4 | cut -d':' -f1)
+	policy_digest=$(echo "$line" | cut -d' ' -f6)
+
+	expected_policy_digest="$(compute_digest $algorithm $SELINUX_DIR/policy)" || \
+		tst_brk TCONF "cannot compute digest for $algorithm"
+
+	if [ "$policy_digest" != "$expected_policy_digest" ]; then
+		tst_res TFAIL "Digest mismatch: expected: $expected_policy_digest, got: $policy_digest"
+		return
+	fi
+
+	tst_res TPASS "SELinux policy hash measured correctly"
+}
+
+# Trigger measurement of SELinux constructs and verify that
+# the measured SELinux state matches the current SELinux
+# configuration.
+test2()
+{
+	tst_check_cmds xxd || return
+
+	local measured_data state_file="$TST_TMPDIR/selinux_state.txt"
+	local data_source_name="selinux"
+	local pattern="data_sources=[^[:space:]]*$data_source_name"
+	local tmp_file="$TST_TMPDIR/selinux_state_tmp_file.txt"
+	local digest expected_digest algorithm
+	local initialized_value
+	local enforced_value expected_enforced_value
+	local checkreqprot_value expected_checkreqprot_value
+	local result
+
+	tst_res TINFO "verifying SELinux state measurement"
+
+	# Trigger a measurement by changing SELinux state
+	tst_update_selinux_state
+
+	# Verify SELinux state is measured and then validate the measured
+	# state matches that currently set for SELinux
+	line=$(grep -E "selinux-state" $ASCII_MEASUREMENTS | tail -1)
+	if [ -z "$line" ]; then
+		tst_res TFAIL "SELinux state not measured"
+		return
+	fi
+
+	digest=$(echo "$line" | cut -d' ' -f4 | cut -d':' -f2)
+	algorithm=$(echo "$line" | cut -d' ' -f4 | cut -d':' -f1)
+
+	echo "$line" | cut -d' ' -f6 | xxd -r -p > $state_file
+
+	expected_digest="$(compute_digest $algorithm $state_file)" || \
+	tst_brk TCONF "cannot compute digest for $algorithm"
+
+	if [ "$digest" != "$expected_digest" ]; then
+		tst_res TFAIL "digest mismatch: expected: $expected_digest, got: $digest"
+		return
+	fi
+
+	# SELinux state is measured as the following string
+	#   initialized=1;enforcing=0;checkreqprot=1;
+	# Value of 0 indicates the state is ON, and 1 indicates OFF
+	#
+	# enforce and checkreqprot measurement can be verified by
+	# comparing the value of the file "enforce" and "checkreqprot"
+	# respectively in the SELinux directory.
+	# "initialized" is an internal state and should be set to 1
+	# if enforce and checkreqprot are measured correctly.
+	measured_data=$(cat $state_file)
+	enforced_value=$(echo $measured_data | awk -F'[=;]' '{print $4}')
+	expected_enforced_value=$(cat $SELINUX_DIR/enforce)
+	if [ "$expected_enforced_value" != "$enforced_value" ];then
+		tst_res TFAIL "enforce: expected: $expected_enforced_value, got: $enforced_value"
+		return
+	fi
+
+	checkreqprot_value=$(echo $measured_data | awk -F'[=;]' '{print $6}')
+	expected_checkreqprot_value=$(cat $SELINUX_DIR/checkreqprot)
+	if [ "$expected_checkreqprot_value" != "$checkreqprot_value" ];then
+		tst_res TFAIL "checkreqprot: expected: $expected_checkreqprot_value, got: $checkreqprot_value"
+		return
+	fi
+
+	initialized_value=$(echo $measured_data | awk -F'[=;]' '{print $2}')
+	if [ "$initialized_value" != "1" ];then
+		tst_res TFAIL "initialized: expected 1, got: $initialized_value"
+		return
+	fi
+
+	validate_policy_capabilities $measured_data
+	result=$?
+	if [ $result = 0 ]; then
+		tst_res TFAIL "policy capabilities did not match"
+		return
+	fi
+
+	tst_res TPASS "SELinux state measured correctly"
+}
+
+tst_run