From patchwork Wed Sep 30 16:02:11 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Petr Vorel X-Patchwork-Id: 11809779 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 1512413B2 for ; Wed, 30 Sep 2020 16:02:22 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id F382220789 for ; Wed, 30 Sep 2020 16:02:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728819AbgI3QCV (ORCPT ); Wed, 30 Sep 2020 12:02:21 -0400 Received: from mx2.suse.de ([195.135.220.15]:50422 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725355AbgI3QCV (ORCPT ); Wed, 30 Sep 2020 12:02:21 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.221.27]) by mx2.suse.de (Postfix) with ESMTP id 0624EAE09; Wed, 30 Sep 2020 16:02:20 +0000 (UTC) From: Petr Vorel To: ltp@lists.linux.it Cc: Petr Vorel , Mimi Zohar , Lakshmi Ramasubramanian , linux-integrity@vger.kernel.org Subject: [PATCH v4 1/4] IMA: Move get_algorithm_digest(), set_digest_index() to ima_setup.sh Date: Wed, 30 Sep 2020 18:02:11 +0200 Message-Id: <20200930160214.29358-2-pvorel@suse.cz> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20200930160214.29358-1-pvorel@suse.cz> References: <20200930160214.29358-1-pvorel@suse.cz> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org To be reusable by more tests (preparation for next commit). Call set_digest_index() inside get_algorithm_digest() if needed instead of expecting get_algorithm_digest() caller to call set_digest_index() before. Signed-off-by: Petr Vorel --- .../integrity/ima/tests/ima_measurements.sh | 62 ++-------------- .../security/integrity/ima/tests/ima_setup.sh | 70 +++++++++++++++++++ 2 files changed, 76 insertions(+), 56 deletions(-) diff --git a/testcases/kernel/security/integrity/ima/tests/ima_measurements.sh b/testcases/kernel/security/integrity/ima/tests/ima_measurements.sh index 9a7500c76..1927e937c 100755 --- a/testcases/kernel/security/integrity/ima/tests/ima_measurements.sh +++ b/testcases/kernel/security/integrity/ima/tests/ima_measurements.sh @@ -6,7 +6,7 @@ # # Verify that measurements are added to the measurement list based on policy. -TST_NEEDS_CMDS="awk cut" +TST_NEEDS_CMDS="awk cut sed" TST_SETUP="setup" TST_CNT=3 TST_NEEDS_DEVICE=1 @@ -20,72 +20,22 @@ setup() TEST_FILE="$PWD/test.txt" POLICY="$IMA_DIR/policy" [ -f "$POLICY" ] || tst_res TINFO "not using default policy" - DIGEST_INDEX= - - local template="$(tail -1 $ASCII_MEASUREMENTS | cut -d' ' -f 3)" - local i - - # parse digest index - # https://www.kernel.org/doc/html/latest/security/IMA-templates.html#use - case "$template" in - ima|ima-ng|ima-sig|ima-buf) DIGEST_INDEX=4 ;; - *) - # using ima_template_fmt kernel parameter - local IFS="|" - i=4 - for word in $template; do - if [ "$word" = 'd' -o "$word" = 'd-ng' ]; then - DIGEST_INDEX=$i - break - fi - i=$((i+1)) - done - esac - - [ -z "$DIGEST_INDEX" ] && tst_brk TCONF \ - "Cannot find digest index (template: '$template')" } ima_check() { - local delimiter=':' - local algorithm digest expected_digest line + local algorithm digest expected_digest line tmp # need to read file to get updated $ASCII_MEASUREMENTS cat $TEST_FILE > /dev/null line="$(grep $TEST_FILE $ASCII_MEASUREMENTS | tail -1)" - if [ -z "$line" ]; then - tst_res TFAIL "cannot find measurement record for '$TEST_FILE'" - return - fi - tst_res TINFO "measurement record: '$line'" - digest=$(echo "$line" | cut -d' ' -f $DIGEST_INDEX) - if [ -z "$digest" ]; then - tst_res TFAIL "cannot find digest (index: $DIGEST_INDEX)" - return - fi - - if [ "${digest#*$delimiter}" != "$digest" ]; then - algorithm=$(echo "$digest" | cut -d $delimiter -f 1) - digest=$(echo "$digest" | cut -d $delimiter -f 2) + if tmp=$(get_algorithm_digest "$line"); then + algorithm=$(echo "$tmp" | cut -d'|' -f1) + digest=$(echo "$tmp" | cut -d'|' -f2) else - case "${#digest}" in - 32) algorithm="md5" ;; - 40) algorithm="sha1" ;; - *) - tst_res TFAIL "algorithm must be either md5 or sha1 (digest: '$digest')" - return ;; - esac - fi - if [ -z "$algorithm" ]; then - tst_res TFAIL "cannot find algorithm" - return - fi - if [ -z "$digest" ]; then - tst_res TFAIL "cannot find digest" - return + tst_res TBROK "failed to get algorithm/digest for '$TEST_FILE': $tmp" fi tst_res TINFO "computing digest for $algorithm algorithm" diff --git a/testcases/kernel/security/integrity/ima/tests/ima_setup.sh b/testcases/kernel/security/integrity/ima/tests/ima_setup.sh index 1f17aa707..83ea62d4f 100644 --- a/testcases/kernel/security/integrity/ima/tests/ima_setup.sh +++ b/testcases/kernel/security/integrity/ima/tests/ima_setup.sh @@ -191,6 +191,76 @@ ima_cleanup() fi } +set_digest_index() +{ + DIGEST_INDEX= + + local template="$(tail -1 $ASCII_MEASUREMENTS | cut -d' ' -f 3)" + local i word + + # parse digest index + # https://www.kernel.org/doc/html/latest/security/IMA-templates.html#use + case "$template" in + ima|ima-ng|ima-sig) DIGEST_INDEX=4 ;; + *) + # using ima_template_fmt kernel parameter + local IFS="|" + i=4 + for word in $template; do + if [ "$word" = 'd' -o "$word" = 'd-ng' ]; then + DIGEST_INDEX=$i + break + fi + i=$((i+1)) + done + esac + + [ -z "$DIGEST_INDEX" ] && tst_brk TCONF \ + "Cannot find digest index (template: '$template')" +} + +get_algorithm_digest() +{ + local line="$1" + local delimiter=':' + local algorithm digest + + if [ -z "$line" ]; then + echo "measurement record not found" + return 1 + fi + + [ -z "$DIGEST_INDEX" ] && set_digest_index + digest=$(echo "$line" | cut -d' ' -f $DIGEST_INDEX) + if [ -z "$digest" ]; then + echo "digest not found (index: $DIGEST_INDEX, line: '$line')" + return 1 + fi + + if [ "${digest#*$delimiter}" != "$digest" ]; then + algorithm=$(echo "$digest" | cut -d $delimiter -f 1) + digest=$(echo "$digest" | cut -d $delimiter -f 2) + else + case "${#digest}" in + 32) algorithm="md5" ;; + 40) algorithm="sha1" ;; + *) + echo "algorithm must be either md5 or sha1 (digest: '$digest')" + return 1 ;; + esac + fi + if [ -z "$algorithm" ]; then + echo "algorithm not found" + return 1 + fi + if [ -z "$digest" ]; then + echo "digest not found" + return 1 + fi + + echo "$algorithm|$digest" +} + # loop device is needed to use only for tmpfs TMPDIR="${TMPDIR:-/tmp}" if [ "$(df -T $TMPDIR | tail -1 | awk '{print $2}')" != "tmpfs" -a -n "$TST_NEEDS_DEVICE" ]; then From patchwork Wed Sep 30 16:02:12 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Petr Vorel X-Patchwork-Id: 11809781 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5AA20618 for ; Wed, 30 Sep 2020 16:02:22 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 432E52087D for ; Wed, 30 Sep 2020 16:02:22 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1725355AbgI3QCV (ORCPT ); Wed, 30 Sep 2020 12:02:21 -0400 Received: from mx2.suse.de ([195.135.220.15]:50438 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725860AbgI3QCV (ORCPT ); Wed, 30 Sep 2020 12:02:21 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.221.27]) by mx2.suse.de (Postfix) with ESMTP id 411F3AE13; Wed, 30 Sep 2020 16:02:20 +0000 (UTC) From: Petr Vorel To: ltp@lists.linux.it Cc: Petr Vorel , Mimi Zohar , Lakshmi Ramasubramanian , linux-integrity@vger.kernel.org Subject: [PATCH v4 2/4] IMA: Rewrite ima_boot_aggregate.c to new API Date: Wed, 30 Sep 2020 18:02:12 +0200 Message-Id: <20200930160214.29358-3-pvorel@suse.cz> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20200930160214.29358-1-pvorel@suse.cz> References: <20200930160214.29358-1-pvorel@suse.cz> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org The main reason was to see TCONF messages, which are printed into stderr in new API (but to stdout in legacy API) and thus visible as the output is redirected into the variable. Changing boot_aggregate: to sha1: to be compatible with evmctl ima_boot_aggregate. Signed-off-by: Petr Vorel --- .../integrity/ima/src/ima_boot_aggregate.c | 113 +++++++++--------- .../security/integrity/ima/tests/ima_tpm.sh | 2 +- 2 files changed, 57 insertions(+), 58 deletions(-) diff --git a/testcases/kernel/security/integrity/ima/src/ima_boot_aggregate.c b/testcases/kernel/security/integrity/ima/src/ima_boot_aggregate.c index 98893b99a..67f97ea04 100644 --- a/testcases/kernel/security/integrity/ima/src/ima_boot_aggregate.c +++ b/testcases/kernel/security/integrity/ima/src/ima_boot_aggregate.c @@ -1,19 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* -* Copyright (c) International Business Machines Corp., 2009 -* -* Authors: -* Mimi Zohar -* -* 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, version 2 of the -* License. -* -* File: ima_boot_aggregate.c -* -* Calculate a SHA1 boot aggregate value based on the TPM -* binary_bios_measurements. -*/ + * Copyright (c) International Business Machines Corp., 2009 + * Copyright (c) 2016-2019 Petr Vorel + * + * Authors: Mimi Zohar + * + * Calculate a SHA1 boot aggregate value based on the TPM 1.2 + * binary_bios_measurements. + */ + +#include "config.h" #include #include #include @@ -23,10 +19,8 @@ #include #include -#include "config.h" -#include "test.h" - -char *TCID = "ima_boot_aggregate"; +#include "tst_test.h" +#include "tst_safe_stdio.h" #if HAVE_LIBCRYPTO #include @@ -36,7 +30,24 @@ char *TCID = "ima_boot_aggregate"; #define MAX_EVENT_DATA_SIZE (MAX_EVENT_SIZE - EVENT_HEADER_SIZE) #define NUM_PCRS 8 /* PCR registers 0-7 in boot aggregate */ -int TST_TOTAL = 1; +static char *debug; +static char *file; + +static unsigned char boot_aggregate[SHA_DIGEST_LENGTH]; + +static struct { + struct { + u_int32_t pcr; + u_int32_t type; + u_int8_t digest[SHA_DIGEST_LENGTH]; + u_int32_t len; + } header __attribute__ ((packed)); + char *data; +} event; + +static struct { + unsigned char digest[SHA_DIGEST_LENGTH]; +} pcr[NUM_PCRS]; static void display_sha1_digest(unsigned char *pcr) { @@ -47,45 +58,24 @@ static void display_sha1_digest(unsigned char *pcr) printf("\n"); } -int main(int argc, char *argv[]) +static void do_test(void) { - unsigned char boot_aggregate[SHA_DIGEST_LENGTH]; - struct { - struct { - u_int32_t pcr; - u_int32_t type; - u_int8_t digest[SHA_DIGEST_LENGTH]; - u_int32_t len; - } header __attribute__ ((packed)); - char *data; - } event; - struct { - unsigned char digest[SHA_DIGEST_LENGTH]; - } pcr[NUM_PCRS]; FILE *fp; - int i; - int debug = 0; SHA_CTX c; + int i; - if (argc != 2) { - printf("format: %s binary_bios_measurement file\n", argv[0]); - return 1; - } - fp = fopen(argv[1], "r"); - if (!fp) { - perror("unable to open pcr file\n"); - return 1; - } + if (!file) + tst_brk(TBROK, "missing binary_bios_measurement file, specify with -f"); + + fp = SAFE_FOPEN(file, "r"); /* Initialize psuedo PCR registers 0 - 7 */ for (i = 0; i < NUM_PCRS; i++) memset(&pcr[i].digest, 0, SHA_DIGEST_LENGTH); event.data = malloc(MAX_EVENT_DATA_SIZE); - if (!event.data) { - printf("Cannot allocate memory\n"); - return 1; - } + if (!event.data) + tst_brk(TBROK, "cannot allocate memory"); /* Extend the pseudo PCRs with the event digest */ while (fread(&event, sizeof(event.header), 1, fp)) { @@ -105,13 +95,14 @@ int main(int argc, char *argv[]) #if MAX_EVENT_DATA_SIZE < USHRT_MAX if (event.header.len > MAX_EVENT_DATA_SIZE) { - printf("Error event too long\n"); + tst_res(TWARN, "error event too long"); break; } #endif fread(event.data, event.header.len, 1, fp); } - fclose(fp); + + SAFE_FCLOSE(fp); free(event.data); /* Extend the boot aggregate with the pseudo PCR digest values */ @@ -126,14 +117,22 @@ int main(int argc, char *argv[]) } SHA1_Final(boot_aggregate, &c); - printf("boot_aggregate:"); + printf("sha1:"); display_sha1_digest(boot_aggregate); - tst_exit(); + tst_res(TPASS, "found sha1 hash"); } +static struct tst_option options[] = { + {"d", &debug, "-d enable debug"}, + {"f:", &file, "-f x binary_bios_measurement file (required)\n"}, + {NULL, NULL, NULL} +}; + +static struct tst_test test = { + .test_all = do_test, + .options = options, +}; + #else -int main(void) -{ - tst_brkm(TCONF, NULL, "test requires libcrypto and openssl development packages"); -} +TST_TEST_TCONF("libcrypto and openssl development packages required"); #endif diff --git a/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh b/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh index c69f891f1..dc958eb5c 100755 --- a/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh +++ b/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh @@ -33,7 +33,7 @@ test1() tst_res TFAIL "bios boot aggregate is not 0" fi else - boot_aggregate=$(ima_boot_aggregate $tpm_bios | grep "boot_aggregate:" | cut -d':' -f2) + boot_aggregate=$(ima_boot_aggregate -f $tpm_bios | grep "sha1:" | cut -d':' -f2) if [ "$boot_hash" = "$boot_aggregate" ]; then tst_res TPASS "bios aggregate matches IMA boot aggregate" else From patchwork Wed Sep 30 16:02:13 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Petr Vorel X-Patchwork-Id: 11809783 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C501913B2 for ; Wed, 30 Sep 2020 16:02:22 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id B586720789 for ; Wed, 30 Sep 2020 16:02:22 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1725860AbgI3QCW (ORCPT ); Wed, 30 Sep 2020 12:02:22 -0400 Received: from mx2.suse.de ([195.135.220.15]:50454 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728480AbgI3QCW (ORCPT ); Wed, 30 Sep 2020 12:02:22 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.221.27]) by mx2.suse.de (Postfix) with ESMTP id 7137DAE1E; Wed, 30 Sep 2020 16:02:20 +0000 (UTC) From: Petr Vorel To: ltp@lists.linux.it Cc: Petr Vorel , Mimi Zohar , Lakshmi Ramasubramanian , linux-integrity@vger.kernel.org Subject: [PATCH v4 3/4] ima_tpm.sh: Fix calculating boot aggregate Date: Wed, 30 Sep 2020 18:02:13 +0200 Message-Id: <20200930160214.29358-4-pvorel@suse.cz> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20200930160214.29358-1-pvorel@suse.cz> References: <20200930160214.29358-1-pvorel@suse.cz> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org for TPM 2.0 or kernel >= v5.8-rc1: 6f1a1d103b48 ima: ("Switch to ima_hash_algo for boot aggregate") Test still fails with newer TPM 2.0 on kernel < v5.8-rc1. Test was failing, because it expect SHA1 (we ignore MD5) hash, but for TPM 2.0 is now used IMA default hash algorithm (by default default SHA256). This is similar for entries in IMA measurement list so we can reuse already existing code. Reading other algorithms than SHA1 or support TPM 2.0 requires evmctl >= 1.3.1 (1.3 would also work for test1, but will be required for test2). Although recent evmctl is recommended, to support older kernels and TPMs which supports only SHA1, get boot aggregate with old our legacy ima_boot_aggregate.c. Also fixed test without TPM device (when IMA TPM-bypass is tested) as some TPM 2.0 devices does not export event log (/sys/kernel/security/tpm0/binary_bios_measurements). This does not require evmctl at all. Signed-off-by: Petr Vorel --- changes v3->v4: * require 1.3.1 instead of 1.3 (simplification) .../security/integrity/ima/tests/ima_tpm.sh | 153 +++++++++++++++--- 1 file changed, 131 insertions(+), 22 deletions(-) diff --git a/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh b/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh index dc958eb5c..ce35a8e27 100755 --- a/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh +++ b/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh @@ -7,41 +7,150 @@ # Verify the boot and PCR aggregates. TST_CNT=2 -TST_NEEDS_CMDS="awk cut ima_boot_aggregate" +TST_NEEDS_CMDS="awk cut" +TST_SETUP="setup" . ima_setup.sh -test1() -{ - tst_res TINFO "verify boot aggregate" +EVMCTL_REQUIRED='1.3.1' +ERRMSG_EVMCTL="install evmctl >= $EVMCTL_REQUIRED" - local zero="0000000000000000000000000000000000000000" - local tpm_bios="$SECURITYFS/tpm0/binary_bios_measurements" - local ima_measurements="$ASCII_MEASUREMENTS" - local boot_aggregate boot_hash line +setup() +{ + local line tmp - # IMA boot aggregate - read line < $ima_measurements - boot_hash=$(echo $line | awk '{print $(NF-1)}' | cut -d':' -f2) + read line < $ASCII_MEASUREMENTS + if tmp=$(get_algorithm_digest "$line"); then + ALGORITHM=$(echo "$tmp" | cut -d'|' -f1) + DIGEST=$(echo "$tmp" | cut -d'|' -f2) + else + tst_res TBROK "failed to get algorithm/digest: $tmp" + fi + tst_res TINFO "used algorithm: $ALGORITHM" - if [ ! -f "$tpm_bios" ]; then - tst_res TINFO "TPM Hardware Support not enabled in kernel or no TPM chip found" + TPM_VERSION="$(get_tpm_version)" + if [ "$TPM_VERSION" ]; then + tst_res TINFO "TMP major version: $TPM_VERSION" + fi - if [ "$boot_hash" = "$zero" ]; then - tst_res TPASS "bios boot aggregate is 0" - else - tst_res TFAIL "bios boot aggregate is not 0" + if ! check_evmctl $EVMCTL_REQUIRED; then + if [ "$ALGORITHM" != "sha1" ]; then + tst_brk TCONF "algorithm not sha1 ($ALGORITHM), $ERRMSG_EVMCTL" fi + MISSING_EVMCTL=1 + fi +} + +# check_evmctl REQUIRED_TPM_VERSION +# return: 0: evmctl is new enough, 1: too old version (or version > v0.9) +check_evmctl() +{ + local required="$1" + + local r1="$(echo $required | cut -d. -f1)" + local r2="$(echo $required | cut -d. -f2)" + local r3="$(echo $required | cut -d. -f3)" + [ -z "$r3" ] && r3=0 + + tst_is_int "$r1" || tst_brk TBROK "required major version not int ($v1)" + tst_is_int "$r2" || tst_brk TBROK "required minor version not int ($v2)" + tst_is_int "$r3" || tst_brk TBROK "required patch version not int ($v3)" + + tst_check_cmds evmctl || return 1 + + local v="$(evmctl --version | cut -d' ' -f2)" + [ -z "$v" ] && return 1 + tst_res TINFO "evmctl version: $v" + + local v1="$(echo $v | cut -d. -f1)" + local v2="$(echo $v | cut -d. -f2)" + local v3="$(echo $v | cut -d. -f3)" + [ -z "$v3" ] && v3=0 + + if [ $v1 -lt $r1 ] || [ $v1 -eq $r1 -a $v2 -lt $r2 ] || \ + [ $v1 -eq $r1 -a $v2 -eq $r2 -a $v3 -lt $r3 ]; then + return 1 + fi + return 0 +} + +# prints major version 1: TPM 1.2, 2: TPM 2.0 +# or nothing when version not detected (no TPM device) +get_tpm_version() +{ + if [ -f /sys/class/tpm/tpm0/tpm_version_major ]; then + cat /sys/class/tpm/tpm0/tpm_version_major + return + fi + + if [ -f /sys/class/tpm/tpm0/device/caps -o \ + -f /sys/class/misc/tpm0/device/caps ]; then + echo 1 + return + fi + + if [ ! -d /sys/class/tpm/tpm0/ -a ! -d /sys/class/misc/tpm0/ ]; then + return + fi + + tst_check_cmds dmesg || return + if dmesg | grep -q '1\.2 TPM (device-id'; then + echo 1 + elif dmesg | grep -q '2\.0 TPM (device-id'; then + echo 2 + fi +} + +test1_virtual_tpm() +{ + local zero=$(echo $DIGEST | awk '{gsub(/./, "0")}; {print}') + + tst_res TINFO "TPM hardware support not enabled in kernel or no TPM chip found, testing TPM-bypass" + + if [ "$DIGEST" = "$zero" ]; then + tst_res TPASS "bios boot aggregate is $zero" else - boot_aggregate=$(ima_boot_aggregate -f $tpm_bios | grep "sha1:" | cut -d':' -f2) - if [ "$boot_hash" = "$boot_aggregate" ]; then - tst_res TPASS "bios aggregate matches IMA boot aggregate" - else - tst_res TFAIL "bios aggregate does not match IMA boot aggregate" + tst_res TFAIL "bios boot aggregate is not $zero ($DIGEST)" + fi +} + +test1_hw_tpm() +{ + local tpm_bios="$SECURITYFS/tpm0/binary_bios_measurements" + local cmd="evmctl ima_boot_aggregate" + local boot_aggregate + + if [ "$MISSING_EVMCTL" = 1 ]; then + if [ ! -f "$tpm_bios" ]; then + tst_res TCONF "missing $tpm_bios, $ERRMSG_EVMCTL" + return fi + tst_check_cmds ima_boot_aggregate || return + cmd="ima_boot_aggregate -f $tpm_bios" + fi + tst_res TINFO "using command: $cmd" + + boot_aggregate=$($cmd | grep "$ALGORITHM:" | cut -d':' -f2) + if [ -z "$boot_aggregate" ]; then + tst_res TBROK "failed to get boot aggregate" + return + fi + tst_res TINFO "IMA boot aggregate: '$boot_aggregate'" + + if [ "$DIGEST" = "$boot_aggregate" ]; then + tst_res TPASS "bios boot aggregate matches IMA boot aggregate" + else + tst_res TFAIL "bios boot aggregate does not match IMA boot aggregate ($DIGEST)" fi } +test1() +{ + tst_res TINFO "verify boot aggregate" + + [ -z "$TPM_VERSION" ] && test1_virtual_tpm || test1_hw_tpm +} + # Probably cleaner to programmatically read the PCR values directly # from the TPM, but that would require a TPM library. For now, use # the PCR values from /sys/devices. From patchwork Wed Sep 30 16:02:14 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Petr Vorel X-Patchwork-Id: 11809785 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 72D0292C for ; Wed, 30 Sep 2020 16:02:23 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 59D9A20789 for ; Wed, 30 Sep 2020 16:02:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728480AbgI3QCW (ORCPT ); Wed, 30 Sep 2020 12:02:22 -0400 Received: from mx2.suse.de ([195.135.220.15]:50474 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725385AbgI3QCW (ORCPT ); Wed, 30 Sep 2020 12:02:22 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.221.27]) by mx2.suse.de (Postfix) with ESMTP id 9794CAE30; Wed, 30 Sep 2020 16:02:20 +0000 (UTC) From: Petr Vorel To: ltp@lists.linux.it Cc: Petr Vorel , Mimi Zohar , Lakshmi Ramasubramanian , linux-integrity@vger.kernel.org Subject: [PATCH v4 4/4] ima_tpm.sh: Fix calculating PCR aggregate Date: Wed, 30 Sep 2020 18:02:14 +0200 Message-Id: <20200930160214.29358-5-pvorel@suse.cz> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20200930160214.29358-1-pvorel@suse.cz> References: <20200930160214.29358-1-pvorel@suse.cz> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org for TPM 2.0 and support more evmctl versions. Because exporting PCR registers for TPM 2.0 has not been upstreamed [1], we use user space code, which requires evmctl >= 1.3.1 and tsspcrread. Using evmctl allows to test for TPM devices which does not export event log (/sys/kernel/security/tpm0/binary_bios_measurements). For TPM 1.2 read tpm0 device pcrs file from kernel. (tss1pcrread could be also used, but it's not yet packaged by distros.) For old kernels which use SHA1/MD5, any evmctl version is required (evmctl ima_measurement was introduced in very old v0.7), but * newer sysctl path /sys/class/tpm/tpm0/device/pcrs requires evmctl 1.1 * using ima_policy=tcb requires 1.3.1 due --ignore-violations We now support output format of ima_measurement command for various evmctl versions: * 1.3: "sha256: TPM PCR-10:" (or other algorithm, e.g. "sha1:") * 1.1-1.2.1: "HW PCR-10:" (the only previously supported format) * 0.7-1.0: "PCR-10:" NOTE: we ignore evmctl failure for evmctl < 1.3.1 (missing --ignore-violations, also evmctl < 1.1 fails with "PCRAgg does not match PCR-10") [1] https://patchwork.kernel.org/patch/11759729/ Signed-off-by: Petr Vorel --- changes v3->v4: * use --ignore-violations if evmctl >= 1.3.1 available * check error only for 1.3.1, for others check only the result * check for ima_policy=tcb (or ima_tcb) and require 1.3.1 if used (to get --ignore-violations) .../security/integrity/ima/tests/ima_setup.sh | 14 +- .../security/integrity/ima/tests/ima_tpm.sh | 151 +++++++++++++----- 2 files changed, 118 insertions(+), 47 deletions(-) diff --git a/testcases/kernel/security/integrity/ima/tests/ima_setup.sh b/testcases/kernel/security/integrity/ima/tests/ima_setup.sh index 83ea62d4f..b801c4371 100644 --- a/testcases/kernel/security/integrity/ima/tests/ima_setup.sh +++ b/testcases/kernel/security/integrity/ima/tests/ima_setup.sh @@ -93,7 +93,7 @@ require_ima_policy_content() fi } -require_ima_policy_cmdline() +check_ima_policy_cmdline() { local policy="$1" local i @@ -101,10 +101,18 @@ require_ima_policy_cmdline() grep -q "ima_$policy" /proc/cmdline && return for i in $(cat /proc/cmdline); do if echo "$i" | grep -q '^ima_policy='; then - echo "$i" | grep -q -e "|[ ]*$policy" -e "$policy[ ]*|" -e "=$policy" && return + echo "$i" | grep -q -e "|[ ]*$policy" -e "$policy[ ]*|" -e "=$policy" && return 0 fi done - tst_brk TCONF "IMA measurement tests require builtin IMA $policy policy (e.g. ima_policy=$policy kernel parameter)" + return 1 +} + +require_ima_policy_cmdline() +{ + local policy="$1" + + check_ima_policy_cmdline $policy || \ + tst_brk TCONF "IMA measurement tests require builtin IMA $policy policy (e.g. ima_policy=$policy kernel parameter)" } mount_helper() diff --git a/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh b/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh index ce35a8e27..43d330802 100755 --- a/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh +++ b/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh @@ -13,7 +13,8 @@ TST_SETUP="setup" . ima_setup.sh EVMCTL_REQUIRED='1.3.1' -ERRMSG_EVMCTL="install evmctl >= $EVMCTL_REQUIRED" +ERRMSG_EVMCTL="=> install evmctl >= $EVMCTL_REQUIRED" +ERRMSG_TPM="TPM hardware support not enabled in kernel or no TPM chip found" setup() { @@ -101,11 +102,91 @@ get_tpm_version() fi } +read_pcr_tpm1() +{ + local pcr_path="/sys/class/tpm/tpm0/device/pcrs" + local evmctl_required="1.1" + local pcr hash + + if [ ! -f "$pcrs_path" ]; then + pcrs_path="/sys/class/misc/tpm0/device/pcrs" + elif ! check_evmctl $evmctl_required; then + tst_res TCONF "evmctl >= $evmctl_required required" + return 1 + fi + + if [ ! -f "$pcr_path" ]; then + tst_res TCONF "missing PCR file $pcrs_path ($ERRMSG_TPM)" + return + fi + + while read line; do + pcr="$(echo $line | cut -d':' -f1)" + hash="$(echo $line | cut -d':' -f2 | awk '{ gsub (" ", "", $0); print tolower($0) }')" + echo "$pcr: $hash" + done < $pcr_path +} + +# NOTE: TPM 1.2 would require to use tss1pcrread which is not fully adopted +# by distros yet. +read_pcr_tpm2() +{ + local pcrmax=23 + local pcrread="tsspcrread -halg $ALGORITHM" + local i pcr + + tst_check_cmds tsspcrread || return 1 + + for i in $(seq 0 $pcrmax); do + pcr=$($pcrread -ha "$i" -ns) + if [ $? -ne 0 ]; then + tst_brk TBROK "tsspcrread failed: $pcr" + fi + printf "PCR-%02d: %s\n" $i "$pcr" + done +} + +get_pcr10_aggregate() +{ + local params pcr + local msg="$ERRMSG_EVMCTL" + local res=TCONF + + if [ -z "$MISSING_EVMCTL" ]; then + params="--ignore-violations" + msg= + res=TFAIL + elif check_ima_policy_cmdline "tcb"; then + tst_res TCONF "using builtin IMA TCB policy $ERRMSG_EVMCTL" + return + fi + + evmctl -v ima_measurement $params $BINARY_MEASUREMENTS > hash.txt 2>&1 + if [ $? -ne 0 -a -z "$MISSING_EVMCTL" ]; then + tst_res TFAIL "evmctl failed $ERRMSG_EVMCTL" + tst_res TINFO "hash file:" + cat hash.txt >&2 + return + fi + + pcr=$(grep -E "^($ALGORITHM: )*PCRAgg.*:" hash.txt \ + | awk '{print $NF}') + + if [ -z "$pcr" ]; then + tst_res $res "failed to find aggregate PCR-10 $msg" + tst_res TINFO "hash file:" + cat hash.txt >&2 + return + fi + + echo "$pcr" +} + test1_virtual_tpm() { local zero=$(echo $DIGEST | awk '{gsub(/./, "0")}; {print}') - tst_res TINFO "TPM hardware support not enabled in kernel or no TPM chip found, testing TPM-bypass" + tst_res TINFO "$ERRMSG_TPM, testing TPM-bypass" if [ "$DIGEST" = "$zero" ]; then tst_res TPASS "bios boot aggregate is $zero" @@ -122,7 +203,7 @@ test1_hw_tpm() if [ "$MISSING_EVMCTL" = 1 ]; then if [ ! -f "$tpm_bios" ]; then - tst_res TCONF "missing $tpm_bios, $ERRMSG_EVMCTL" + tst_res TCONF "missing $tpm_bios $ERRMSG_EVMCTL" return fi tst_check_cmds ima_boot_aggregate || return @@ -151,57 +232,39 @@ test1() [ -z "$TPM_VERSION" ] && test1_virtual_tpm || test1_hw_tpm } -# Probably cleaner to programmatically read the PCR values directly -# from the TPM, but that would require a TPM library. For now, use -# the PCR values from /sys/devices. -validate_pcr() +test2() { - tst_res TINFO "verify PCR (Process Control Register)" + local hash pcr_aggregate - local dev_pcrs="$1" - local pcr hash aggregate_pcr + tst_res TINFO "verify PCR values" - aggregate_pcr="$(evmctl -v ima_measurement $BINARY_MEASUREMENTS 2>&1 | \ - grep 'HW PCR-10:' | awk '{print $3}')" - if [ -z "$aggregate_pcr" ]; then - tst_res TFAIL "failed to get PCR-10" - return 1 + if [ -z "$TPM_VERSION" ]; then + tst_brk TCONF "TMP version not detected ($ERRMSG_TPM)" fi - while read line; do - pcr="$(echo $line | cut -d':' -f1)" - if [ "$pcr" = "PCR-10" ]; then - hash="$(echo $line | cut -d':' -f2 | awk '{ gsub (" ", "", $0); print tolower($0) }')" - [ "$hash" = "$aggregate_pcr" ] - return $? - fi - done < $dev_pcrs - return 1 -} - -test2() -{ - tst_res TINFO "verify PCR values" - tst_check_cmds evmctl || return + if [ "$ALGORITHM" = "sha1" -a "$MISSING_EVMCTL" = 1 ]; then + tst_check_cmds evmctl || return 1 + fi - tst_res TINFO "evmctl version: $(evmctl --version)" + read_pcr_tpm$TPM_VERSION > pcr.txt || return + hash=$(grep "^PCR-10" pcr.txt | cut -d' ' -f2) + if [ -z "$hash" ]; then + tst_res TFAIL "PCR-10 hash not found" + cat pcr.txt + return + fi + tst_res TINFO "real PCR-10: '$hash'" - local pcrs_path="/sys/class/tpm/tpm0/device/pcrs" - if [ -f "$pcrs_path" ]; then - tst_res TINFO "new PCRS path, evmctl >= 1.1 required" - else - pcrs_path="/sys/class/misc/tpm0/device/pcrs" + pcr_aggregate="$(get_pcr10_aggregate)" + if [ -z "$pcr_aggregate" ]; then + return fi + tst_res TINFO "aggregate PCR-10: '$hash'" - if [ -f "$pcrs_path" ]; then - validate_pcr $pcrs_path - if [ $? -eq 0 ]; then - tst_res TPASS "aggregate PCR value matches real PCR value" - else - tst_res TFAIL "aggregate PCR value does not match real PCR value" - fi + if [ "$hash" = "$pcr_aggregate" ]; then + tst_res TPASS "aggregate PCR value matches real PCR value" else - tst_res TCONF "TPM Hardware Support not enabled in kernel or no TPM chip found" + tst_res TFAIL "aggregate PCR value does not match real PCR value" fi }