diff mbox series

[1/2] IMA: Verify that the kernel cmdline is passed and measured correctly through the kexec barrier.

Message ID 20200702153545.3126-2-t-josne@linux.microsoft.com (mailing list archive)
State New, archived
Headers show
Series Test cmdline measurement and IMA buffer passing through kexec | expand

Commit Message

Lachlan Sneff July 2, 2020, 3:35 p.m. UTC
Add a testcase that verifies that kexec correctly logs the
kernel command line to the IMA buffer and that the command
line is then correctly measured.

This test must be run standalone, since it runs kexec
multiple times (and therefore reboots several times).

Signed-off-by: Lachlan Sneff <t-josne@linux.microsoft.com>
---
 testcases/kexec/README.md  | 26 +++++++++++++
 testcases/kexec/cmdline.sh | 78 ++++++++++++++++++++++++++++++++++++++
 testcases/kexec/utils.sh   | 47 +++++++++++++++++++++++
 3 files changed, 151 insertions(+)
 create mode 100644 testcases/kexec/README.md
 create mode 100755 testcases/kexec/cmdline.sh
 create mode 100755 testcases/kexec/utils.sh

Comments

Mimi Zohar July 15, 2020, 12:58 a.m. UTC | #1
On Thu, 2020-07-02 at 11:35 -0400, Lachlan Sneff wrote:
> Add a testcase that verifies that kexec correctly logs the
> kernel command line to the IMA buffer and that the command
> line is then correctly measured.
> 
> This test must be run standalone, since it runs kexec
> multiple times (and therefore reboots several times).

Verifying the kexec boot command line doesn't require rebooting.  Just
loading the kexec kernel image should be enough (kexec -s -l).
 Verifying that the measurement list, including the kexec boot command
line, is carried across kexec could be a separate test.

Mimi
Petr Vorel July 15, 2020, 8:03 a.m. UTC | #2
> On Thu, 2020-07-02 at 11:35 -0400, Lachlan Sneff wrote:
> > Add a testcase that verifies that kexec correctly logs the
> > kernel command line to the IMA buffer and that the command
> > line is then correctly measured.

> > This test must be run standalone, since it runs kexec
> > multiple times (and therefore reboots several times).

> Verifying the kexec boot command line doesn't require rebooting.  Just
> loading the kexec kernel image should be enough (kexec -s -l).
>  Verifying that the measurement list, including the kexec boot command
> line, is carried across kexec could be a separate test.
Mimi, thanks for a confirmation.
Lachlan, so is it possible to turn it as a normal LTP test? And the test looks
like IMA specific now, thus I'd move it into IMA tests.

I'll post some comments to the test itself.

Kind regards,
Petr
Petr Vorel July 15, 2020, 8:18 a.m. UTC | #3
> Add a testcase that verifies that kexec correctly logs the
> kernel command line to the IMA buffer and that the command
> line is then correctly measured.

> This test must be run standalone, since it runs kexec
> multiple times (and therefore reboots several times).

> Signed-off-by: Lachlan Sneff <t-josne@linux.microsoft.com>
...
> +++ b/testcases/kexec/utils.sh
> @@ -0,0 +1,47 @@
> +#!/bin/sh
> +
> +install() {
> +    local arg="$1"
> +
> +    if [ ! -d "/etc/init.d" ]; then
> +        mkdir /etc/init.d
> +    fi
I'm not sure if tests like this are suitable for LTP.
Ideal LTP test is a normal test, which is able to run with runltp, cleanup after
itself and use LTP C or/and shell API. LTP is full of tests which needs special
handling and thus not being run, not sure if it's a good idea to introduce yet
another one.

Also test shouldn't not significantly modify SUT to make it unbootable, which
I'm not sure in this case. This is a big difference to kselftests which are
meant to help during kernel development which somehow expects some system
modifications (as you install your custom build kernel).

I wonder if using QEMU would help to implement this test while not touching SUT
(thus be able to run this test with runltp). If you miss something in LTP API
just let us know.

Kind regards,
Petr
Lachlan Sneff July 15, 2020, 7:38 p.m. UTC | #4
On 7/14/20 8:58 PM, Mimi Zohar wrote:
> On Thu, 2020-07-02 at 11:35 -0400, Lachlan Sneff wrote:
>> Add a testcase that verifies that kexec correctly logs the
>> kernel command line to the IMA buffer and that the command
>> line is then correctly measured.
>>
>> This test must be run standalone, since it runs kexec
>> multiple times (and therefore reboots several times).
> Verifying the kexec boot command line doesn't require rebooting.  Just
> loading the kexec kernel image should be enough (kexec -s -l).
>   Verifying that the measurement list, including the kexec boot command
> line, is carried across kexec could be a separate test.

This is true. However, it only appends to the IMA log once, even if you 
unload (`kexec -u`) the kexec kernel after `kexec -s -l ...`.

Therefore, the test would only be able to check kexec with the cmdline 
supplied in one way.

I will have to check internally if that's the right way to go. If it 
didn't need to reboot, then the test could be integrated into the normal 
IMA tests,
which would definitely be a good thing.

Regards,

Lachlan
Mimi Zohar July 15, 2020, 7:40 p.m. UTC | #5
On Wed, 2020-07-15 at 15:38 -0400, Lachlan Sneff wrote:
> On 7/14/20 8:58 PM, Mimi Zohar wrote:
> > On Thu, 2020-07-02 at 11:35 -0400, Lachlan Sneff wrote:
> >> Add a testcase that verifies that kexec correctly logs the
> >> kernel command line to the IMA buffer and that the command
> >> line is then correctly measured.
> >>
> >> This test must be run standalone, since it runs kexec
> >> multiple times (and therefore reboots several times).
> > Verifying the kexec boot command line doesn't require rebooting.  Just
> > loading the kexec kernel image should be enough (kexec -s -l).
> >   Verifying that the measurement list, including the kexec boot command
> > line, is carried across kexec could be a separate test.
> 
> This is true. However, it only appends to the IMA log once, even if you 
> unload (`kexec -u`) the kexec kernel after `kexec -s -l ...`.
> 
> Therefore, the test would only be able to check kexec with the cmdline 
> supplied in one way.
> 
> I will have to check internally if that's the right way to go. If it 
> didn't need to reboot, then the test could be integrated into the normal 
> IMA tests,
> which would definitely be a good thing.

For files, there is a single measurement unless the file changes.  I
would assume that would be the same for the kexec command line as
well.  You could modify the command line a bit to force it to be re-
measured.

Mimi
Lachlan Sneff July 15, 2020, 7:46 p.m. UTC | #6
On 7/15/20 4:18 AM, Petr Vorel wrote:
>> +++ b/testcases/kexec/utils.sh
>> @@ -0,0 +1,47 @@
>> +#!/bin/sh
>> +
>> +install() {
>> +    local arg="$1"
>> +
>> +    if [ ! -d "/etc/init.d" ]; then
>> +        mkdir /etc/init.d
>> +    fi
> I'm not sure if tests like this are suitable for LTP.
> Ideal LTP test is a normal test, which is able to run with runltp, cleanup after
> itself and use LTP C or/and shell API. LTP is full of tests which needs special
> handling and thus not being run, not sure if it's a good idea to introduce yet
> another one.
>
> Also test shouldn't not significantly modify SUT to make it unbootable, which
> I'm not sure in this case. This is a big difference to kselftests which are
> meant to help during kernel development which somehow expects some system
> modifications (as you install your custom build kernel).
>
> I wonder if using QEMU would help to implement this test while not touching SUT
> (thus be able to run this test with runltp). If you miss something in LTP API
> just let us know.
Using qemu is an interesting idea, but would be difficult to generalize.

I actually do agree with you that a test like this may not be 
appropriate for
LTP since it's so separate and alien to the rest of the LTP suite.

I need to confirm internally before I make a decision here, but is there
a better place to put a test like this?


Thanks for your review,

Lachlan :)
diff mbox series

Patch

diff --git a/testcases/kexec/README.md b/testcases/kexec/README.md
new file mode 100644
index 000000000..42988cd7b
--- /dev/null
+++ b/testcases/kexec/README.md
@@ -0,0 +1,26 @@ 
+# IMA + Kexec Tests
+
+Since these tests cannot be run by the usual LTP machinery (runltp, etc)
+because the machine you run these on will reboot several times, these
+tests must be run standalone.
+
+To run these tests, you must first copy over either the entire LTP repo, or
+just the `testcases/kexec` directory.
+
+You must supply a kernel image that will be passed to kexec, either
+though the `IMAGE` environment variable, or placed at `testcases/kexec/Image`.
+
+If the kernel is built to require signed kernel images, you must supply
+a signed image.
+
+Currently, the only arch that has support in the upstream kernel to pass
+the IMA buffer through kexec is powerpc. A patchset that adds support for
+IMA buffer passing through kexec on aarch64 is in the process of being
+upstreamed. Therefore, these tests will not let you run them on
+architectures other than powerpc or aarch64.
+
+Running
+-------
+- kexec cmdline measurement
+    1. `IMAGE=<path to kernel image> testcases/kexec/cmdline.sh start`
+    2. Runtime logs will be emitted in `testcases/kexec/kexec_cmdline.log`.
diff --git a/testcases/kexec/cmdline.sh b/testcases/kexec/cmdline.sh
new file mode 100755
index 000000000..e3d050ce0
--- /dev/null
+++ b/testcases/kexec/cmdline.sh
@@ -0,0 +1,78 @@ 
+#!/bin/sh
+
+ASCII_MEASUREMENTS="/sys/kernel/security/integrity/ima/ascii_runtime_measurements"
+SCRIPT_DIR="$(dirname $(realpath $0))"
+IMAGE=$(realpath "${IMAGE:-$SCRIPT_DIR/Image}")
+LOG_FILE="$SCRIPT_DIR/kexec_cmdline.log"
+
+. $SCRIPT_DIR/utils.sh
+
+must_be_root
+on_correct_machine
+
+case $1 in
+    start)
+        # Start the state machine
+        echo "$(cat /proc/cmdline | tr -d '\n' | text2hex)" > /etc/saved-kexec-cmdline
+
+        install 1
+        if ! kexec -s $IMAGE --reuse-cmdline; then
+            echo "kexec failed: $?" >> $LOG_FILE
+        fi
+        ;;
+    1)
+        cmdline="$(cat /proc/cmdline | tr -d '\n')"
+        printf "$cmdline" | text2hex | xargs echo >> /etc/saved-kexec-cmdline
+
+        install 2
+        if ! kexec -s $IMAGE --append="$cmdline"; then
+            echo "kexec failed: $?" >> $LOG_FILE
+        fi
+        ;;
+    2)
+        cmdline="$(cat /proc/cmdline | tr -d '\n')"
+        printf "$cmdline" | text2hex | xargs echo >> /etc/saved-kexec-cmdline
+
+        install 3
+        if ! kexec -s $IMAGE --command-line="$cmdline"; then
+            echo "kexec failed: $?" >> $LOG_FILE
+        fi
+        ;;
+    3)
+        update-rc.d resume-after-kexec remove
+        rm /etc/init.d/resume-after-kexec
+
+        success=true
+
+        grep "kexec-cmdline" $ASCII_MEASUREMENTS \
+        | tail -n "$(wc -l /etc/saved-kexec-cmdline | cut -d' ' -f1)" \
+        | paste -d'|' /etc/saved-kexec-cmdline - \
+        | while IFS="|" read -r saved_cmdline logged_line
+        do
+            # saved_cmdline is encoded in hex
+            algorithm=$(echo "$logged_line" | cut -d' ' -f4 | cut -d':' -f1)
+            digest=$(echo "$logged_line" | cut -d' ' -f4 | cut -d':' -f2)
+            logged_cmdline=$(echo "$logged_line" | cut -d' ' -f6)
+            saved_digest=$(echo "$saved_cmdline" | hex2text | ${algorithm}sum | cut -d' ' -f1)
+
+            if [ "$saved_cmdline" != "$logged_cmdline" ]; then
+                echo "saved cmdline != logged cmdline" >> $LOG_FILE
+                success=false
+            fi
+
+            if [ "$saved_digest" != "$digest" ]; then
+                echo "computed digest != logged digest" >> $LOG_FILE
+                success=false
+            fi
+        done
+
+        if $success; then
+            echo "test succeeded" >> $LOG_FILE
+        else
+            echo "test failed" >> $LOG_FILE
+        fi
+        ;;
+    *)
+        echo "You must run '$0 start' to begin the test"
+        ;;
+esac
diff --git a/testcases/kexec/utils.sh b/testcases/kexec/utils.sh
new file mode 100755
index 000000000..2399294d4
--- /dev/null
+++ b/testcases/kexec/utils.sh
@@ -0,0 +1,47 @@ 
+#!/bin/sh
+
+install() {
+    local arg="$1"
+
+    if [ ! -d "/etc/init.d" ]; then
+        mkdir /etc/init.d
+    fi
+
+    cat << EOF > /etc/init.d/resume-after-kexec
+#!/bin/sh
+# Run the test
+IMAGE=$IMAGE $(realpath $0) $arg
+EOF
+
+    chmod +x /etc/init.d/resume-after-kexec
+    update-rc.d resume-after-kexec defaults
+}
+
+must_be_root() {
+    if [ "$EUID" != 0 ]; then
+        echo "run this script as root"
+        exit
+    fi
+}
+
+# Since the IMA buffer passing through kexec is only
+# implemented on powerpc right now with support for aarch64 in review,
+# only support running this test on powerpc or aarch64 systems.
+on_correct_machine() {
+    case "$(uname -m)" in
+        ppc|ppc64|ppcle|ppc64le|aarch64)
+            ;;
+        *)
+            echo "must be on powerpc or aarch64 architecture"
+            exit
+            ;;
+    esac
+}
+
+hex2text() {
+    sed 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf
+}
+
+text2hex() {
+    hexdump -v -e '1/1 "%02x"'
+}