Message ID | 20200327042515.22315-2-vt@altlinux.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | ima-evm-utils: Add some tests for evmctl | expand |
On 3/30/20 9:16 AM, Lakshmi Ramasubramanian wrote: > On 3/26/20 9:25 PM, Vitaly Chikunov wrote: > >> Run `make check' to execute the tests. > > autogen.sh followed by configure need to be run. Could you please add > that in the patch description? > >> diff --git a/tests/functions.sh b/tests/functions.sh >> new file mode 100755 >> index 0000000..16c8d89 >> --- /dev/null >> +++ b/tests/functions.sh >> @@ -0,0 +1,272 @@ >> +#!/bin/bash >> +# SPDX-License-Identifier: GPL-2.0 > > In some source files in the kernel repo I have seen the following: > > // SPDX-License-Identifier: GPL-2.0+ > > Not sure if it is applicable here. > >> +check() { >> + local alg=$1 pref=$2 chash=$3 hash >> + local file=$alg-hash.txt >> + >> + rm -f $file >> + touch $file >> + # Generate hash with openssl, if it's failed skip test, > > "if it failed skip test" > >> + # unless it's negative test, then pass to evmctl >> + cmd="openssl dgst $OPENSSL_ENGINE -$alg $file" >> +} >> + > >> +# check args: algo hdr-prefix canonic-hash >> +expect_pass check md4 0x01 31d6cfe0d16ae931b73c59d7e0c089c0 >> +expect_pass check md5 0x01 d41d8cd98f00b204e9800998ecf8427e > Is MD4 and MD5 tests still needed? > > > -lakshmi + Mimi and linux-integrity
Lakshmi, On Mon, Mar 30, 2020 at 09:29:29AM -0700, Lakshmi Ramasubramanian wrote: > On 3/30/20 9:16 AM, Lakshmi Ramasubramanian wrote: > > > On 3/26/20 9:25 PM, Vitaly Chikunov wrote: > > > > > Run `make check' to execute the tests. > > > > autogen.sh followed by configure need to be run. Could you please add > > that in the patch description? Why this patch should have this and not any other patch/commit? > > > diff --git a/tests/functions.sh b/tests/functions.sh > > > new file mode 100755 > > > index 0000000..16c8d89 > > > --- /dev/null > > > +++ b/tests/functions.sh > > > @@ -0,0 +1,272 @@ > > > +#!/bin/bash > > > +# SPDX-License-Identifier: GPL-2.0 > > > > In some source files in the kernel repo I have seen the following: > > > > // SPDX-License-Identifier: GPL-2.0+ > > > > Not sure if it is applicable here. I'm sure it's applicable. > > > > > +check() { > > > + local alg=$1 pref=$2 chash=$3 hash > > > + local file=$alg-hash.txt > > > + > > > + rm -f $file > > > + touch $file > > > + # Generate hash with openssl, if it's failed skip test, > > > > "if it failed skip test" > > > > > + # unless it's negative test, then pass to evmctl > > > + cmd="openssl dgst $OPENSSL_ENGINE -$alg $file" > > > +} > > > + > > > > > +# check args: algo hdr-prefix canonic-hash > > > +expect_pass check md4 0x01 31d6cfe0d16ae931b73c59d7e0c089c0 > > > +expect_pass check md5 0x01 d41d8cd98f00b204e9800998ecf8427e > > Is MD4 and MD5 tests still needed? If it's supported why it shouldn't be tested. > > > > > > -lakshmi > > + Mimi and linux-integrity
Hi Vitaly, On Fri, 2020-03-27 at 07:25 +0300, Vitaly Chikunov wrote: > Run `make check' to execute the tests. > This commit only adds ima_hash test. > Signed-off-by: Vitaly Chikunov <vt@altlinux.org> Thank you for breaking up the patch based on tests. "ima_hash.test" may be executed by running "make check", but obviously also by invoking it directly. A couple of comments/questions inline below. > diff --git a/tests/functions.sh b/tests/functions.sh > new file mode 100755 > index 0000000..16c8d89 > --- /dev/null > +++ b/tests/functions.sh > @@ -0,0 +1,272 @@ > +#!/bin/bash > +# SPDX-License-Identifier: GPL-2.0 > +# > +# ima-evm-utils tests bash functions > +# > +# Copyright (C) 2019 Vitaly Chikunov <vt@altlinux.org> > +# > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; either version 2, or (at your option) > +# any later version. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > + > +# Tests accounting > +declare -i testspass=0 testsfail=0 testsskip=0 > + > +# Exit codes (compatible with automake) > +declare -r OK=0 > +declare -r FAIL=1 > +declare -r HARDFAIL=99 # hard failure no matter testing mode > +declare -r SKIP=77 > + > +# You can set env VERBOSE=1 to see more output from evmctl > +V=vvvv > +V=${V:0:$VERBOSE} > +V=${V:+-$V} > + > +# Exit if env FAILEARLY is defined. > +# Used in expect_{pass,fail}. > +exit_early() { > + if [ $FAILEARLY ]; then Throughout variables are not double quoted, though the tests themselves turn off globbing (set -f). shellcheck doesn't detect that globbing has been turned off. It complains about each variable usage that is not double quoted. > + exit $1 > + fi > +} > + > +# Require particular executables to be present > +_require() { > + ret= > + for i; do > + if ! type $i; then > + echo "$i is required for test" > + ret=1 > + fi > + done > + [ $ret ] && exit $HARDFAIL > +} > + > +# Only allow color output on tty > +if [ -t 1 ]; then > + RED=$'\e[1;31m' > + GREEN=$'\e[1;32m' > + YELLOW=$'\e[1;33m' > + BLUE=$'\e[1;34m' > + CYAN=$'\e[1;36m' > + NORM=$'\e[m' > +fi > + > +# Test mode determined by TFAIL variable: > +# undefined: to success testing > +# defined: failure testing > +TFAIL= > +TMODE=+ # mode character to prepend running command in log > +declare -i TNESTED=0 # just for sanity checking > + > +# Run positive test (one that should pass) and account its result > +expect_pass() { > + local ret > + > + if [ $TNESTED -gt 0 ]; then > + echo $RED"expect_pass should not be run nested"$NORM > + testsfail+=1 > + exit $HARDFAIL > + fi > + TFAIL= > + TMODE=+ > + TNESTED+=1 > + [[ "$VERBOSE" -gt 1 ]] && echo "____ START positive test: $@" Unless VERBOSE is set as an environment variable, it isn't defined. This isn't an issue when using [[ ... ]], but should it be initialized: VERBOSE="${VERBOSE:-0}"? > + "$@" > + ret=$? > + [[ "$VERBOSE" -gt 1 ]] && echo "^^^^ STOP ($ret) positive test: $@" > + TNESTED+=-1 > + case $ret in > + 0) testspass+=1 ;; > + 77) testsskip+=1 ;; > + 99) testsfail+=1; exit_early 1 ;; > + *) testsfail+=1; exit_early 2 ;; > + esac > + return $ret > +} > + > +# Eval negative test (one that should fail) and account its result > +expect_fail() { > + local ret > + > + if [ $TNESTED -gt 0 ]; then > + echo $RED"expect_fail should not be run nested"$NORM > + testsfail+=1 > + exit $HARDFAIL > + fi > + > + TFAIL=yes > + TMODE=- > + TNESTED+=1 > + [[ "$VERBOSE" -gt 1 ]] && echo "____ START negative test: $@" > + "$@" > + ret=$? > + [[ "$VERBOSE" -gt 1 ]] && echo "^^^^ STOP ($ret) negative test: $@" > + TNESTED+=-1 > + case $ret in > + 0) testsfail+=1; exit_early 3 ;; > + 77) testsskip+=1 ;; > + 99) testsfail+=1; exit_early 4 ;; > + *) testspass+=1 ;; > + esac > + # Restore defaults (as in positive tests) > + # for tests to run without wrappers > + TFAIL= > + TMODE=+ > + return $ret > +} > + > +# return true if current test is positive > +_is_expect_pass() { > + [ ! $TFAIL ] > +} > + > +# return true if current test is negative > +_is_expect_fail() { > + [ $TFAIL ] > +} > + > +# Show blank line and color following text to red > +# if it's real error (ie we are in expect_pass mode). > +red_if_failure() { > + if _is_expect_pass; then > + echo $@ $RED > + COLOR_RESTORE=1 > + fi > +} > + > +# For hard errors > +red_always() { > + echo $@ $RED A few functions - "red_always", "red_if_failure", "color_restore" - use "$@", but none of the function callers pass any parameters. Is there a reason for the "$@" or just something left over from debugging? > + COLOR_RESTORE=1 > +} > + > +color_restore() { > + [ $COLOR_RESTORE ] && echo $@ $NORM > + COLOR_RESTORE= > +} > + > +ADD_DEL= > +ADD_TEXT_FOR= > +# _evmctl_run should be run as `_evmctl_run ... || return' > +_evmctl_run() { > + local op=$1 out=$1-$$.out > + local text_for=${FOR:+for $ADD_TEXT_FOR} > + # Additional parameters: > + # ADD_DEL: additional files to rm on failure > + # ADD_TEXT_FOR: append to text as 'for $ADD_TEXT_FOR' > + > + cmd="evmctl $V $EVMCTL_ENGINE $@" > + echo $YELLOW$TMODE $cmd$NORM > + $cmd >$out 2>&1 > + ret=$? > + > + # Shell special and signal exit codes (except 255) > + if [ $ret -ge 126 -a $ret -lt 255 ]; then > + red_always > + echo "evmctl $op failed hard with ($ret) $text_for" > + sed 's/^/ /' $out > + color_restore > + rm $out $ADD_DEL > + ADD_DEL= > + ADD_TEXT_FOR= > + return $HARDFAIL > + elif [ $ret -gt 0 ]; then > + red_if_failure > + echo "evmctl $op failed" ${TFAIL:+properly} "with ($ret) $text_for" > + # Show evmctl output only in verbose mode or if real failure. > + if _is_expect_pass || [ "$VERBOSE" ]; then > + sed 's/^/ /' $out > + fi > + color_restore > + rm $out $ADD_DEL > + ADD_DEL= > + ADD_TEXT_FOR= > + return $FAIL > + elif _is_expect_fail; then > + red_always > + echo "evmctl $op wrongly succeeded $text_for" > + sed 's/^/ /' $out > + color_restore > + else > + [ "$VERBOSE" ] && sed 's/^/ /' $out > + fi > + rm $out > + ADD_DEL= > + ADD_TEXT_FOR= > + return $OK > +} > + > +# Extract xattr $attr from $file into $out file skipping $pref'ix > +_extract_xattr() { > + local file=$1 attr=$2 out=$3 pref=$4 > + > + getfattr -n $attr -e hex $file \ > + | grep "^$attr=" \ > + | sed "s/^$attr=$pref//" \ > + | xxd -r -p > $out > +} > + > +# Test if xattr $attr in $file matches $pref'ix > +# Show error and fail otherwise. > +_test_xattr() { > + local file=$1 attr=$2 pref=$3 > + local text_for=${ADD_TEXT_FOR:+ for $ADD_TEXT_FOR} > + > + if ! getfattr -n $attr -e hex $file | egrep -qx "$attr=$pref"; then > + red_if_failure > + echo "Did not find expected hash$text_for:" > + echo " $attr=$pref" > + echo "" > + echo "Actual output below:" > + getfattr -n $attr -e hex $file | sed 's/^/ /' > + color_restore > + rm $file > + ADD_TEXT_FOR= > + return $FAIL > + fi > + ADD_TEXT_FOR= > +} > + > +# Try to enable gost-engine if needed. > +_enable_gost_engine() { > + # Do not enable if it's already working (enabled by user) > + if ! openssl md_gost12_256 /dev/null >/dev/null 2>&1 \ Interesting /dev/null usage here as a filename. > + && openssl engine gost >/dev/null 2>&1; then > + EVMCTL_ENGINE="--engine gost" > + OPENSSL_ENGINE="-engine gost" > + fi > +} > + > +# Show test stats and exit into automake test system > +# with proper exit code (same as ours). > +_report_exit() { > + if [ $testsfail -gt 0 ]; then > + echo "=================================" > + echo " Run with FAILEARLY=1 $0 $@" > + echo " To stop after first failure" > + echo "=================================" > + fi > + [ $testspass -gt 0 ] && echo -n $GREEN || echo -n $NORM > + echo -n "PASS: $testspass" > + [ $testsskip -gt 0 ] && echo -n $YELLOW || echo -n $NORM > + echo -n " SKIP: $testsskip" > + [ $testsfail -gt 0 ] && echo -n $RED || echo -n $NORM > + echo " FAIL: $testsfail" > + echo $NORM > + if [ $testsfail -gt 0 ]; then > + exit $FAIL > + elif [ $testspass -gt 0 ]; then > + exit $OK > + else > + exit $SKIP > + fi > +} > + > diff --git a/tests/ima_hash.test b/tests/ima_hash.test > new file mode 100755 > index 0000000..30aec19 > --- /dev/null > +++ b/tests/ima_hash.test > @@ -0,0 +1,80 @@ > +#!/bin/bash > +# SPDX-License-Identifier: GPL-2.0 > +# > +# evmctl ima_hash tests > +# > +# Copyright (C) 2019 Vitaly Chikunov <vt@altlinux.org> > +# > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; either version 2, or (at your option) > +# any later version. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > + > +cd $(dirname $0) > +PATH=../src:$PATH > +source ./functions.sh > +_require evmctl openssl getfattr > + > +trap _report_exit EXIT > +set -f # disable globbing > + > +check() { > + local alg=$1 pref=$2 chash=$3 hash > + local file=$alg-hash.txt > + > + rm -f $file > + touch $file > + # Generate hash with openssl, if it's failed skip test, > + # unless it's negative test, then pass to evmctl > + cmd="openssl dgst $OPENSSL_ENGINE -$alg $file" > + echo - $cmd > + hash=$(set -o pipefail; $cmd 2>/dev/null | cut -d' ' -f2) > + if [ $? -ne 0 ] && _is_expect_pass; then > + echo $CYAN"$alg test is skipped"$NORM > + rm $file > + return $SKIP > + fi > + if [ "$chash" ] && [ "$chash" != "$hash" ]; then > + red_always Only when "ima_hash.test" is invoked directly, the output is colored red. Really confusing. > + echo "Invalid hash for $alg from openssl" > + echo "Expected: $chash" > + echo "Returned: $hash" > + color_restore > + rm $file > + return $HARDFAIL > + fi > + > + ADD_TEXT_FOR=$alg ADD_DEL=$file \ > + _evmctl_run ima_hash --hashalgo $alg --xattr-user $file || return > + ADD_TEXT_FOR=$alg \ > + _test_xattr $file user.ima $pref$hash || return > + rm $file > + return $OK > +} > + > +# check args: algo hdr-prefix canonic-hash > +expect_pass check md4 0x01 31d6cfe0d16ae931b73c59d7e0c089c0 > +expect_pass check md5 0x01 d41d8cd98f00b204e9800998ecf8427e > +expect_pass check sha1 0x01 da39a3ee5e6b4b0d3255bfef95601890afd80709 > +expect_fail check SHA1 0x01 # uppercase > +expect_fail check sha512-224 0x01 # valid for pkcs1 > +expect_fail check sha512-256 0x01 # valid for pkcs1 > +expect_fail check unknown 0x01 # nonexistent > +expect_pass check sha224 0x0407 d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f > +expect_pass check sha256 0x0404 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 > +expect_pass check sha384 0x0405 38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b > +expect_pass check sha512 0x0406 cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e > +expect_pass check rmd160 0x0403 9c1185a5c5e9fc54612808977ee8f548b2258d31 > +expect_fail check sm3 0x01 > +expect_fail check sm3-256 0x01 > +_enable_gost_engine > +expect_pass check md_gost12_256 0x0412 3f539a213e97c802cc229d474c6aa32a825a360b2a933a949fd925208d9ce1bb > +expect_pass check streebog256 0x0412 3f539a213e97c802cc229d474c6aa32a825a360b2a933a949fd925208d9ce1bb > +expect_pass check md_gost12_512 0x0413 8e945da209aa869f0455928529bcae4679e9873ab707b55315f56ceb98bef0a7362f715528356ee83cda5f2aac4c6ad2ba3a715c1bcd81cb8e9f90bf4c1c1a8a > +expect_pass check streebog512 0x0413 8e945da209aa869f0455928529bcae4679e9873ab707b55315f56ceb98bef0a7362f715528356ee83cda5f2aac4c6ad2ba3a715c1bcd81cb8e9f90bf4c1c1a8a > + Nice! The code is very concisely written. Reviewing this patch would be a lot easier, if it was broken up into smaller pieces. For example, and this is only an example, the initial patch could define the base ima_hash.test, a subsequent patch could add coloring for the base ima_hash.test, another patch could introduce "make check" and add its coloring. There's all sorts of ways to break up this patch to simplify review. Mimi
Mimi, On Tue, Mar 31, 2020 at 10:25:24AM -0400, Mimi Zohar wrote: > > +# For hard errors > > +red_always() { > > + echo $@ $RED > > A few functions - "red_always", "red_if_failure", "color_restore" - > use "$@", but none of the function callers pass any parameters. Is > there a reason for the "$@" or just something left over from > debugging? It was to pass `-n` I think, but it is never needed in the end. > > + if [ "$chash" ] && [ "$chash" != "$hash" ]; then > > + red_always > > Only when "ima_hash.test" is invoked directly, the output is colored > red. Really confusing. Non-TTY output is never colored to not clutter log files. But logic is to color the errors in red. So it thought as 'always red', _when_ there is colored output (TTY). And it's "always" in contract to "red if failure" - which make text red only when the test is expected to pass (thus, this is real error condition), when the test is expected to fail there is no point to color it red, because it's not real error (to not confuse user). Because it is unconditional (in that sense) is it named "red always". I can rename it to something like `color_red'. And rename `red_if_failure' to `color_red_on_failure'. > Nice! The code is very concisely written. > > Reviewing this patch would be a lot easier, if it was broken up into > smaller pieces. For example, and this is only an example, the initial > patch could define the base ima_hash.test, a subsequent patch could > add coloring for the base ima_hash.test, another patch could introduce > "make check" and add its coloring. There's all sorts of ways to break > up this patch to simplify review. This would make following commits to change code which is already committed in previous commits. This would make editing code extra hard. Especially, when tests was reworked a lot. Also, I don't think splitting coloring into separate patch improves review. Instead, we can just remember the rule that (real) errors are going to be printed in red. For example, if we prefix every error message with word "ERROR:" - why it would be easier to review if we split adding this prefix to every message in a separate commit? Red color, similarly to uppercase "ERROR", just improves visibility of errors. (Which is useful, because there is really a lot of tests). Thanks, > > Mimi
On Tue, 2020-03-31 at 18:14 +0300, Vitaly Chikunov wrote: > Mimi, > > On Tue, Mar 31, 2020 at 10:25:24AM -0400, Mimi Zohar wrote: > > > +# For hard errors > > > +red_always() { > > > + echo $@ $RED > > > > A few functions - "red_always", "red_if_failure", "color_restore" - > > use "$@", but none of the function callers pass any parameters. Is > > there a reason for the "$@" or just something left over from > > debugging? > > It was to pass `-n` I think, but it is never needed in the end. > > > > + if [ "$chash" ] && [ "$chash" != "$hash" ]; then > > > + red_always > > > > Only when "ima_hash.test" is invoked directly, the output is colored > > red. Really confusing. > > Non-TTY output is never colored to not clutter log files. > But logic is to color the errors in red. > > So it thought as 'always red', _when_ there is colored output (TTY). > > And it's "always" in contract to "red if failure" - which make text > red only when the test is expected to pass (thus, this is real error > condition), when the test is expected to fail there is no point to > color it red, because it's not real error (to not confuse user). > > Because it is unconditional (in that sense) is it named "red always". > > I can rename it to something like `color_red'. And rename > `red_if_failure' to `color_red_on_failure'. Sure, and add a short comment - "Non-TTY output is never colored". > > > Nice! The code is very concisely written. > > > > Reviewing this patch would be a lot easier, if it was broken up into > > smaller pieces. For example, and this is only an example, the initial > > patch could define the base ima_hash.test, a subsequent patch could > > add coloring for the base ima_hash.test, another patch could introduce > > "make check" and add its coloring. There's all sorts of ways to break > > up this patch to simplify review. > > This would make following commits to change code which is already > committed in previous commits. Nothing is committed yet. I pushed it out in order for others to review and comment, not only your patches, but mine as well. > This would make editing code extra hard. > Especially, when tests was reworked a lot. > > Also, I don't think splitting coloring into separate patch improves > review. Instead, we can just remember the rule that (real) errors are > going to be printed in red. > > For example, if we prefix every error message with word "ERROR:" - why > it would be easier to review if we split adding this prefix to every > message in a separate commit? > > Red color, similarly to uppercase "ERROR", just improves visibility of > errors. (Which is useful, because there is really a lot of tests). Trust me, I understand breaking up patches to simplify review is a lot of work. It's one of the hardest things to teach newbies and explain to them why it is necessary and why a clean history is worthwhile, but you know how to do it. These were just suggestions, just as separating the two tests was just a suggestion. BTW, in case it got lost along the way, I really do appreciate your help. Thanks! Mimi
diff --git a/.gitignore b/.gitignore index cb82166..c579199 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,7 @@ missing compile libtool ltmain.sh - +test-driver # Compiled executables *.o diff --git a/Makefile.am b/Makefile.am index dba408d..45c6f82 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = src +SUBDIRS = src tests dist_man_MANS = evmctl.1 doc_DATA = examples/ima-genkey-self.sh examples/ima-genkey.sh examples/ima-gen-local-ca.sh diff --git a/configure.ac b/configure.ac index 099c3b1..f246182 100644 --- a/configure.ac +++ b/configure.ac @@ -72,6 +72,7 @@ EVMCTL_MANPAGE_DOCBOOK_XSL AC_CONFIG_FILES([Makefile src/Makefile + tests/Makefile packaging/ima-evm-utils.spec ]) AC_OUTPUT diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..9ecc984 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,16 @@ +# Generated by test driver +*.log +*.trs + +# Generated by tests +*.txt +*.out +*.sig +*.sig2 + +# Generated certs and keys (by gen-keys.sh) +*.cer +*.pub +*.key +*.conf + diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..e37b958 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,7 @@ +check_SCRIPTS = +TESTS = $(check_SCRIPTS) + +check_SCRIPTS += ima_hash.test + +clean-local: + -rm -f *.txt *.out *.sig *.sig2 diff --git a/tests/functions.sh b/tests/functions.sh new file mode 100755 index 0000000..16c8d89 --- /dev/null +++ b/tests/functions.sh @@ -0,0 +1,272 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# ima-evm-utils tests bash functions +# +# Copyright (C) 2019 Vitaly Chikunov <vt@altlinux.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# Tests accounting +declare -i testspass=0 testsfail=0 testsskip=0 + +# Exit codes (compatible with automake) +declare -r OK=0 +declare -r FAIL=1 +declare -r HARDFAIL=99 # hard failure no matter testing mode +declare -r SKIP=77 + +# You can set env VERBOSE=1 to see more output from evmctl +V=vvvv +V=${V:0:$VERBOSE} +V=${V:+-$V} + +# Exit if env FAILEARLY is defined. +# Used in expect_{pass,fail}. +exit_early() { + if [ $FAILEARLY ]; then + exit $1 + fi +} + +# Require particular executables to be present +_require() { + ret= + for i; do + if ! type $i; then + echo "$i is required for test" + ret=1 + fi + done + [ $ret ] && exit $HARDFAIL +} + +# Only allow color output on tty +if [ -t 1 ]; then + RED=$'\e[1;31m' + GREEN=$'\e[1;32m' + YELLOW=$'\e[1;33m' + BLUE=$'\e[1;34m' + CYAN=$'\e[1;36m' + NORM=$'\e[m' +fi + +# Test mode determined by TFAIL variable: +# undefined: to success testing +# defined: failure testing +TFAIL= +TMODE=+ # mode character to prepend running command in log +declare -i TNESTED=0 # just for sanity checking + +# Run positive test (one that should pass) and account its result +expect_pass() { + local ret + + if [ $TNESTED -gt 0 ]; then + echo $RED"expect_pass should not be run nested"$NORM + testsfail+=1 + exit $HARDFAIL + fi + TFAIL= + TMODE=+ + TNESTED+=1 + [[ "$VERBOSE" -gt 1 ]] && echo "____ START positive test: $@" + "$@" + ret=$? + [[ "$VERBOSE" -gt 1 ]] && echo "^^^^ STOP ($ret) positive test: $@" + TNESTED+=-1 + case $ret in + 0) testspass+=1 ;; + 77) testsskip+=1 ;; + 99) testsfail+=1; exit_early 1 ;; + *) testsfail+=1; exit_early 2 ;; + esac + return $ret +} + +# Eval negative test (one that should fail) and account its result +expect_fail() { + local ret + + if [ $TNESTED -gt 0 ]; then + echo $RED"expect_fail should not be run nested"$NORM + testsfail+=1 + exit $HARDFAIL + fi + + TFAIL=yes + TMODE=- + TNESTED+=1 + [[ "$VERBOSE" -gt 1 ]] && echo "____ START negative test: $@" + "$@" + ret=$? + [[ "$VERBOSE" -gt 1 ]] && echo "^^^^ STOP ($ret) negative test: $@" + TNESTED+=-1 + case $ret in + 0) testsfail+=1; exit_early 3 ;; + 77) testsskip+=1 ;; + 99) testsfail+=1; exit_early 4 ;; + *) testspass+=1 ;; + esac + # Restore defaults (as in positive tests) + # for tests to run without wrappers + TFAIL= + TMODE=+ + return $ret +} + +# return true if current test is positive +_is_expect_pass() { + [ ! $TFAIL ] +} + +# return true if current test is negative +_is_expect_fail() { + [ $TFAIL ] +} + +# Show blank line and color following text to red +# if it's real error (ie we are in expect_pass mode). +red_if_failure() { + if _is_expect_pass; then + echo $@ $RED + COLOR_RESTORE=1 + fi +} + +# For hard errors +red_always() { + echo $@ $RED + COLOR_RESTORE=1 +} + +color_restore() { + [ $COLOR_RESTORE ] && echo $@ $NORM + COLOR_RESTORE= +} + +ADD_DEL= +ADD_TEXT_FOR= +# _evmctl_run should be run as `_evmctl_run ... || return' +_evmctl_run() { + local op=$1 out=$1-$$.out + local text_for=${FOR:+for $ADD_TEXT_FOR} + # Additional parameters: + # ADD_DEL: additional files to rm on failure + # ADD_TEXT_FOR: append to text as 'for $ADD_TEXT_FOR' + + cmd="evmctl $V $EVMCTL_ENGINE $@" + echo $YELLOW$TMODE $cmd$NORM + $cmd >$out 2>&1 + ret=$? + + # Shell special and signal exit codes (except 255) + if [ $ret -ge 126 -a $ret -lt 255 ]; then + red_always + echo "evmctl $op failed hard with ($ret) $text_for" + sed 's/^/ /' $out + color_restore + rm $out $ADD_DEL + ADD_DEL= + ADD_TEXT_FOR= + return $HARDFAIL + elif [ $ret -gt 0 ]; then + red_if_failure + echo "evmctl $op failed" ${TFAIL:+properly} "with ($ret) $text_for" + # Show evmctl output only in verbose mode or if real failure. + if _is_expect_pass || [ "$VERBOSE" ]; then + sed 's/^/ /' $out + fi + color_restore + rm $out $ADD_DEL + ADD_DEL= + ADD_TEXT_FOR= + return $FAIL + elif _is_expect_fail; then + red_always + echo "evmctl $op wrongly succeeded $text_for" + sed 's/^/ /' $out + color_restore + else + [ "$VERBOSE" ] && sed 's/^/ /' $out + fi + rm $out + ADD_DEL= + ADD_TEXT_FOR= + return $OK +} + +# Extract xattr $attr from $file into $out file skipping $pref'ix +_extract_xattr() { + local file=$1 attr=$2 out=$3 pref=$4 + + getfattr -n $attr -e hex $file \ + | grep "^$attr=" \ + | sed "s/^$attr=$pref//" \ + | xxd -r -p > $out +} + +# Test if xattr $attr in $file matches $pref'ix +# Show error and fail otherwise. +_test_xattr() { + local file=$1 attr=$2 pref=$3 + local text_for=${ADD_TEXT_FOR:+ for $ADD_TEXT_FOR} + + if ! getfattr -n $attr -e hex $file | egrep -qx "$attr=$pref"; then + red_if_failure + echo "Did not find expected hash$text_for:" + echo " $attr=$pref" + echo "" + echo "Actual output below:" + getfattr -n $attr -e hex $file | sed 's/^/ /' + color_restore + rm $file + ADD_TEXT_FOR= + return $FAIL + fi + ADD_TEXT_FOR= +} + +# Try to enable gost-engine if needed. +_enable_gost_engine() { + # Do not enable if it's already working (enabled by user) + if ! openssl md_gost12_256 /dev/null >/dev/null 2>&1 \ + && openssl engine gost >/dev/null 2>&1; then + EVMCTL_ENGINE="--engine gost" + OPENSSL_ENGINE="-engine gost" + fi +} + +# Show test stats and exit into automake test system +# with proper exit code (same as ours). +_report_exit() { + if [ $testsfail -gt 0 ]; then + echo "=================================" + echo " Run with FAILEARLY=1 $0 $@" + echo " To stop after first failure" + echo "=================================" + fi + [ $testspass -gt 0 ] && echo -n $GREEN || echo -n $NORM + echo -n "PASS: $testspass" + [ $testsskip -gt 0 ] && echo -n $YELLOW || echo -n $NORM + echo -n " SKIP: $testsskip" + [ $testsfail -gt 0 ] && echo -n $RED || echo -n $NORM + echo " FAIL: $testsfail" + echo $NORM + if [ $testsfail -gt 0 ]; then + exit $FAIL + elif [ $testspass -gt 0 ]; then + exit $OK + else + exit $SKIP + fi +} + diff --git a/tests/ima_hash.test b/tests/ima_hash.test new file mode 100755 index 0000000..30aec19 --- /dev/null +++ b/tests/ima_hash.test @@ -0,0 +1,80 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# evmctl ima_hash tests +# +# Copyright (C) 2019 Vitaly Chikunov <vt@altlinux.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +cd $(dirname $0) +PATH=../src:$PATH +source ./functions.sh +_require evmctl openssl getfattr + +trap _report_exit EXIT +set -f # disable globbing + +check() { + local alg=$1 pref=$2 chash=$3 hash + local file=$alg-hash.txt + + rm -f $file + touch $file + # Generate hash with openssl, if it's failed skip test, + # unless it's negative test, then pass to evmctl + cmd="openssl dgst $OPENSSL_ENGINE -$alg $file" + echo - $cmd + hash=$(set -o pipefail; $cmd 2>/dev/null | cut -d' ' -f2) + if [ $? -ne 0 ] && _is_expect_pass; then + echo $CYAN"$alg test is skipped"$NORM + rm $file + return $SKIP + fi + if [ "$chash" ] && [ "$chash" != "$hash" ]; then + red_always + echo "Invalid hash for $alg from openssl" + echo "Expected: $chash" + echo "Returned: $hash" + color_restore + rm $file + return $HARDFAIL + fi + + ADD_TEXT_FOR=$alg ADD_DEL=$file \ + _evmctl_run ima_hash --hashalgo $alg --xattr-user $file || return + ADD_TEXT_FOR=$alg \ + _test_xattr $file user.ima $pref$hash || return + rm $file + return $OK +} + +# check args: algo hdr-prefix canonic-hash +expect_pass check md4 0x01 31d6cfe0d16ae931b73c59d7e0c089c0 +expect_pass check md5 0x01 d41d8cd98f00b204e9800998ecf8427e +expect_pass check sha1 0x01 da39a3ee5e6b4b0d3255bfef95601890afd80709 +expect_fail check SHA1 0x01 # uppercase +expect_fail check sha512-224 0x01 # valid for pkcs1 +expect_fail check sha512-256 0x01 # valid for pkcs1 +expect_fail check unknown 0x01 # nonexistent +expect_pass check sha224 0x0407 d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f +expect_pass check sha256 0x0404 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +expect_pass check sha384 0x0405 38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b +expect_pass check sha512 0x0406 cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e +expect_pass check rmd160 0x0403 9c1185a5c5e9fc54612808977ee8f548b2258d31 +expect_fail check sm3 0x01 +expect_fail check sm3-256 0x01 +_enable_gost_engine +expect_pass check md_gost12_256 0x0412 3f539a213e97c802cc229d474c6aa32a825a360b2a933a949fd925208d9ce1bb +expect_pass check streebog256 0x0412 3f539a213e97c802cc229d474c6aa32a825a360b2a933a949fd925208d9ce1bb +expect_pass check md_gost12_512 0x0413 8e945da209aa869f0455928529bcae4679e9873ab707b55315f56ceb98bef0a7362f715528356ee83cda5f2aac4c6ad2ba3a715c1bcd81cb8e9f90bf4c1c1a8a +expect_pass check streebog512 0x0413 8e945da209aa869f0455928529bcae4679e9873ab707b55315f56ceb98bef0a7362f715528356ee83cda5f2aac4c6ad2ba3a715c1bcd81cb8e9f90bf4c1c1a8a +
Run `make check' to execute the tests. This commit only adds ima_hash test. Signed-off-by: Vitaly Chikunov <vt@altlinux.org> --- .gitignore | 2 +- Makefile.am | 2 +- configure.ac | 1 + tests/.gitignore | 16 ++++ tests/Makefile.am | 7 ++ tests/functions.sh | 272 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/ima_hash.test | 80 ++++++++++++++++ 7 files changed, 378 insertions(+), 2 deletions(-) create mode 100644 tests/.gitignore create mode 100644 tests/Makefile.am create mode 100755 tests/functions.sh create mode 100755 tests/ima_hash.test