From 3e5a528aec09021475a1ed5bb0a94a3d1578e63e Mon Sep 17 00:00:00 2001 From: Andreas Steffen Date: Wed, 30 Sep 2020 16:36:29 +0200 Subject: [PATCH] tpm: Auto-detection of legacy TPM 2.0 devices --- src/libimcv/pts/pts.c | 27 +++- src/libimcv/pts/pts_pcr.c | 5 +- src/libimcv/pts/pts_pcr.h | 4 +- src/libtpmtss/tpm_tss_tss2_v1.c | 15 ++- src/libtpmtss/tpm_tss_tss2_v2.c | 101 +++++++++----- src/oval-updater/oval-updater | 228 ++++++++++++++++++++++++++++++++ 6 files changed, 335 insertions(+), 45 deletions(-) create mode 100755 src/oval-updater/oval-updater diff --git a/src/libimcv/pts/pts.c b/src/libimcv/pts/pts.c index b7f1a0e1f..a03703ddd 100644 --- a/src/libimcv/pts/pts.c +++ b/src/libimcv/pts/pts.c @@ -318,8 +318,8 @@ METHOD(pts_t, get_tpm, tpm_tss_t*, METHOD(pts_t, get_tpm_version_info, bool, private_pts_t *this, chunk_t *info) { - *info = this->tpm ? this->tpm->get_version_info(this->tpm) : - this->tpm_version_info; + *info = this->tpm_version_info; + return info->len > 0; } @@ -369,7 +369,8 @@ METHOD(pts_t, set_tpm_version_info, void, this->tpm_version = TPM_VERSION_2_0; - if (reader->read_uint16(reader, &reserved) && + if (reader->read_uint8 (reader, &reserved) && + reader->read_uint8 (reader, &locality) && reader->read_uint32(reader, &revision) && reader->read_uint32(reader, &year) && reader->read_data (reader, 4, &vendor)) @@ -838,12 +839,29 @@ METHOD(pts_t, verify_quote_signature, bool, return TRUE; } +/** + * Extracts the locality from a TPM 2.0 Version Info struct + */ +static uint8_t get_tpm_locality(chunk_t tpm_version_info) +{ + if (tpm_version_info.len < 4 || + tpm_version_info.ptr[0] != 0x02 || tpm_version_info.ptr[1] != 0x00) + { + return 0; + } + else + { + return tpm_version_info.ptr[3]; + } +} + METHOD(pts_t, get_pcrs, pts_pcr_t*, private_pts_t *this) { if (!this->pcrs) { - this->pcrs = pts_pcr_create(this->tpm_version, this->algorithm); + this->pcrs = pts_pcr_create(this->tpm_version, this->algorithm, + get_tpm_locality(this->tpm_version_info)); } return this->pcrs; } @@ -912,6 +930,7 @@ pts_t *pts_create(bool is_imc) { this->proto_caps |= PTS_PROTO_CAPS_T | PTS_PROTO_CAPS_D; this->tpm_version = this->tpm->get_version(this->tpm); + this->tpm_version_info = this->tpm->get_version_info(this->tpm); load_aik(this); } } diff --git a/src/libimcv/pts/pts_pcr.c b/src/libimcv/pts/pts_pcr.c index 68d36282b..c377cf8d5 100644 --- a/src/libimcv/pts/pts_pcr.c +++ b/src/libimcv/pts/pts_pcr.c @@ -259,7 +259,8 @@ METHOD(pts_pcr_t, destroy, void, /** * See header */ -pts_pcr_t *pts_pcr_create(tpm_version_t tpm_version, pts_meas_algorithms_t algo) +pts_pcr_t *pts_pcr_create(tpm_version_t tpm_version, pts_meas_algorithms_t algo, + uint8_t locality) { private_pts_pcr_t *this; hash_algorithm_t hash_alg; @@ -302,8 +303,6 @@ pts_pcr_t *pts_pcr_create(tpm_version_t tpm_version, pts_meas_algorithms_t algo) /* Set locality indicator in PCR[0] */ if (tpm_version == TPM_VERSION_2_0) { - const uint8_t locality = 3; - DBG2(DBG_PTS, "TPM 2.0 - locality indicator set to %u", (uint32_t)locality); this->pcrs[0].ptr[this->pcr_len - 1] = locality; diff --git a/src/libimcv/pts/pts_pcr.h b/src/libimcv/pts/pts_pcr.h index 6b399e466..7eca70a41 100644 --- a/src/libimcv/pts/pts_pcr.h +++ b/src/libimcv/pts/pts_pcr.h @@ -122,7 +122,9 @@ struct pts_pcr_t { * * @param tpm_version TPM version * @param algo Hash algorithm used by PCR bank + * @param locality TPM locality in which the PCR bank was initialized */ -pts_pcr_t* pts_pcr_create(tpm_version_t tpm_version, pts_meas_algorithms_t algo); +pts_pcr_t* pts_pcr_create(tpm_version_t tpm_version, pts_meas_algorithms_t algo, + uint8_t locality); #endif /** PTS_PCR_H_ @}*/ diff --git a/src/libtpmtss/tpm_tss_tss2_v1.c b/src/libtpmtss/tpm_tss_tss2_v1.c index fb5a6e93a..c9611088a 100644 --- a/src/libtpmtss/tpm_tss_tss2_v1.c +++ b/src/libtpmtss/tpm_tss_tss2_v1.c @@ -185,7 +185,7 @@ static bool is_supported_alg(private_tpm_tss_tss2_t *this, TPM_ALG_ID alg_id) * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | TPM 2.0 Version_Info Tag | Reserved | + * | TPM 2.0 Version_Info Tag | Reserved | Locality | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Revision | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -195,8 +195,9 @@ static bool is_supported_alg(private_tpm_tss_tss2_t *this, TPM_ALG_ID alg_id) * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ #define TPM2_VERSION_INFO_TAG 0x0200 -#define TPM2_VERSION_INFO_RESERVED 0x0000 +#define TPM2_VERSION_INFO_RESERVED 0x00 #define TPM2_VERSION_INFO_SIZE 16 +#define TPM2_DEFAULT_LOCALITY 3 static bool get_algs_capability(private_tpm_tss_tss2_t *this) { @@ -207,6 +208,7 @@ static bool get_algs_capability(private_tpm_tss_tss2_t *this) bio_writer_t *writer; bool fips_140_2 = FALSE; uint32_t rval, i, offset, revision = 0, year = 0, vendor = 0; + uint8_t locality = TPM2_DEFAULT_LOCALITY; size_t len = BUF_LEN; char buf[BUF_LEN], manufacturer[5], vendor_string[17]; char *pos = buf; @@ -269,10 +271,17 @@ static bool get_algs_capability(private_tpm_tss_tss2_t *this) manufacturer, vendor_string, (float)revision/100, year, fips_140_2 ? "FIPS 140-2" : (this->fips_186_4 ? "FIPS 186-4" : "")); + /* determine if TPM uses old event digest format and a different locality */ + if (streq(manufacturer, "INTC") && revision == 116 && year == 2016) + { + locality = 0; + } + /* construct TPM 2.0 version_info object */ writer = bio_writer_create( TPM2_VERSION_INFO_SIZE); writer->write_uint16(writer, TPM2_VERSION_INFO_TAG); - writer->write_uint16(writer, TPM2_VERSION_INFO_RESERVED); + writer->write_uint8(writer, TPM2_VERSION_INFO_RESERVED); + writer->write_uint8(writer, locality); writer->write_uint32(writer, revision); writer->write_uint32(writer, year); writer->write_uint32(writer, vendor); diff --git a/src/libtpmtss/tpm_tss_tss2_v2.c b/src/libtpmtss/tpm_tss_tss2_v2.c index 914f953f5..8c6e7f5fe 100644 --- a/src/libtpmtss/tpm_tss_tss2_v2.c +++ b/src/libtpmtss/tpm_tss_tss2_v2.c @@ -89,6 +89,11 @@ struct private_tpm_tss_tss2_t { */ bool fips_186_4; + /** + * Does the TPM use the old TCG SHA1-only event digest format + */ + bool old_event_digest_format; + /** * Mutex controlling access to the TPM 2.0 context */ @@ -192,7 +197,7 @@ static bool is_supported_alg(private_tpm_tss_tss2_t *this, TPM2_ALG_ID alg_id) * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | TPM 2.0 Version_Info Tag | Reserved | + * | TPM 2.0 Version_Info Tag | Reserved | Locality | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Revision | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -202,8 +207,9 @@ static bool is_supported_alg(private_tpm_tss_tss2_t *this, TPM2_ALG_ID alg_id) * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ #define TPM2_VERSION_INFO_TAG 0x0200 -#define TPM2_VERSION_INFO_RESERVED 0x0000 +#define TPM2_VERSION_INFO_RESERVED 0x00 #define TPM2_VERSION_INFO_SIZE 16 +#define TPM2_DEFAULT_LOCALITY 3 static bool get_algs_capability(private_tpm_tss_tss2_t *this) { @@ -214,6 +220,7 @@ static bool get_algs_capability(private_tpm_tss_tss2_t *this) bio_writer_t *writer; bool fips_140_2 = FALSE; uint32_t rval, i, offset, revision = 0, year = 0, vendor = 0; + uint8_t locality = TPM2_DEFAULT_LOCALITY; size_t len = BUF_LEN; char buf[BUF_LEN], manufacturer[5], vendor_string[17]; char *pos = buf; @@ -277,10 +284,18 @@ static bool get_algs_capability(private_tpm_tss_tss2_t *this) manufacturer, vendor_string, (float)revision/100, year, fips_140_2 ? "FIPS 140-2" : (this->fips_186_4 ? "FIPS 186-4" : "")); + /* determine if TPM uses old event digest format and a different locality */ + if (streq(manufacturer, "INTC") && revision == 116 && year == 2016) + { + this->old_event_digest_format = TRUE; + locality = 0; + } + /* construct TPM 2.0 version_info object */ writer = bio_writer_create( TPM2_VERSION_INFO_SIZE); writer->write_uint16(writer, TPM2_VERSION_INFO_TAG); - writer->write_uint16(writer, TPM2_VERSION_INFO_RESERVED); + writer->write_uint8(writer, TPM2_VERSION_INFO_RESERVED); + writer->write_uint8(writer, locality); writer->write_uint32(writer, revision); writer->write_uint32(writer, year); writer->write_uint32(writer, vendor); @@ -1306,50 +1321,68 @@ METHOD(tpm_tss_t, get_event_digest, bool, hash_algorithm_t hash_alg; TPM2_ALG_ID alg_id; - if (read(fd, &digest_count, 4) != 4) + if (this->old_event_digest_format) { - return FALSE; - } - while (digest_count--) - { - if (read(fd, &alg_id, 2) != 2) + if (alg != HASH_SHA1) { return FALSE; } - hash_alg = hash_alg_from_tpm_alg_id(alg_id); + digest_len = HASH_SIZE_SHA1; - switch (hash_alg) + *digest = chunk_alloc(digest_len); + + if (read(fd, digest->ptr, digest_len) != digest_len) { - case HASH_SHA1: - digest_len = HASH_SIZE_SHA1; - break; - case HASH_SHA256: - digest_len = HASH_SIZE_SHA256; - break; - case HASH_SHA384: - digest_len = HASH_SIZE_SHA384; - break; - case HASH_SHA512: - digest_len = HASH_SIZE_SHA512; - break; - default: - DBG2(DBG_PTS, "alg_id: 0x%04x", alg_id); - return FALSE; + return FALSE; } - if (hash_alg == alg) + } + else + { + if (read(fd, &digest_count, 4) != 4) { - *digest = chunk_alloc(digest_len); - if (read(fd, digest->ptr, digest_len) != digest_len) + return FALSE; + } + while (digest_count--) + { + if (read(fd, &alg_id, 2) != 2) { return FALSE; } - } - else - { - /* read without storing */ - if (read(fd, digest_buf, digest_len) != digest_len) + hash_alg = hash_alg_from_tpm_alg_id(alg_id); + + switch (hash_alg) { + case HASH_SHA1: + digest_len = HASH_SIZE_SHA1; + break; + case HASH_SHA256: + digest_len = HASH_SIZE_SHA256; + break; + case HASH_SHA384: + digest_len = HASH_SIZE_SHA384; + break; + case HASH_SHA512: + digest_len = HASH_SIZE_SHA512; + break; + default: + DBG2(DBG_PTS, "alg_id: 0x%04x", alg_id); + return FALSE; + } + if (hash_alg == alg) + { + *digest = chunk_alloc(digest_len); + if (read(fd, digest->ptr, digest_len) != digest_len) + { return FALSE; + } + } + else + { + /* read without storing */ + if (read(fd, digest_buf, digest_len) != digest_len) + { + return FALSE; + } } } } diff --git a/src/oval-updater/oval-updater b/src/oval-updater/oval-updater new file mode 100755 index 000000000..269357a5c --- /dev/null +++ b/src/oval-updater/oval-updater @@ -0,0 +1,228 @@ +#! /bin/bash + +# oval-updater - temporary wrapper script for .libs/oval-updater +# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-0.1 +# +# The oval-updater program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +sed_quote_subst='s|\([`"$\\]\)|\\\1|g' + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +relink_command="(cd /home/andi/strongswan/src/oval-updater; { test -z \"\${LIBRARY_PATH+set}\" || unset LIBRARY_PATH || { LIBRARY_PATH=; export LIBRARY_PATH; }; }; { test -z \"\${COMPILER_PATH+set}\" || unset COMPILER_PATH || { COMPILER_PATH=; export COMPILER_PATH; }; }; { test -z \"\${GCC_EXEC_PREFIX+set}\" || unset GCC_EXEC_PREFIX || { GCC_EXEC_PREFIX=; export GCC_EXEC_PREFIX; }; }; { test -z \"\${LD_RUN_PATH+set}\" || unset LD_RUN_PATH || { LD_RUN_PATH=; export LD_RUN_PATH; }; }; { test -z \"\${LD_LIBRARY_PATH+set}\" || unset LD_LIBRARY_PATH || { LD_LIBRARY_PATH=; export LD_LIBRARY_PATH; }; }; PATH=/home/andi/bin:/home/andi/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/lib/jvm/java-8-oracle/bin:/usr/lib/jvm/java-8-oracle/db/bin:/usr/lib/jvm/java-8-oracle/jre/bin; export PATH; gcc -I/usr/include/libxml2 -g -O2 -Wall -Wno-format -Wno-format-security -Wno-pointer-sign -include /home/andi/strongswan/config.h -o \$progdir/\$file oval-updater.o oval.o ../../src/libstrongswan/.libs/libstrongswan.so -lxml2 -Wl,-rpath -Wl,/home/andi/strongswan/src/libstrongswan/.libs -Wl,-rpath -Wl,/usr/lib/ipsec)" + +# This environment variable determines our operation mode. +if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then + # install mode needs the following variables: + generated_by_libtool_version='2.4.6' + notinst_deplibs=' ../../src/libstrongswan/libstrongswan.la' +else + # When we are sourced in execute mode, $file and $ECHO are already set. + if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then + file="$0" + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' +} + ECHO="printf %s\\n" + fi + +# Very basic option parsing. These options are (a) specific to +# the libtool wrapper, (b) are identical between the wrapper +# /script/ and the wrapper /executable/ that is used only on +# windows platforms, and (c) all begin with the string --lt- +# (application programs are unlikely to have options that match +# this pattern). +# +# There are only two supported options: --lt-debug and +# --lt-dump-script. There is, deliberately, no --lt-help. +# +# The first argument to this parsing function should be the +# script's ../../libtool value, followed by no. +lt_option_debug= +func_parse_lt_options () +{ + lt_script_arg0=$0 + shift + for lt_opt + do + case "$lt_opt" in + --lt-debug) lt_option_debug=1 ;; + --lt-dump-script) + lt_dump_D=`$ECHO "X$lt_script_arg0" | /bin/sed -e 's/^X//' -e 's%/[^/]*$%%'` + test "X$lt_dump_D" = "X$lt_script_arg0" && lt_dump_D=. + lt_dump_F=`$ECHO "X$lt_script_arg0" | /bin/sed -e 's/^X//' -e 's%^.*/%%'` + cat "$lt_dump_D/$lt_dump_F" + exit 0 + ;; + --lt-*) + $ECHO "Unrecognized --lt- option: '$lt_opt'" 1>&2 + exit 1 + ;; + esac + done + + # Print the debug banner immediately: + if test -n "$lt_option_debug"; then + echo "oval-updater:oval-updater:$LINENO: libtool wrapper (GNU libtool) 2.4.6 Debian-2.4.6-0.1" 1>&2 + fi +} + +# Used when --lt-debug. Prints its arguments to stdout +# (redirection is the responsibility of the caller) +func_lt_dump_args () +{ + lt_dump_args_N=1; + for lt_arg + do + $ECHO "oval-updater:oval-updater:$LINENO: newargv[$lt_dump_args_N]: $lt_arg" + lt_dump_args_N=`expr $lt_dump_args_N + 1` + done +} + +# Core function for launching the target application +func_exec_program_core () +{ + + if test -n "$lt_option_debug"; then + $ECHO "oval-updater:oval-updater:$LINENO: newargv[0]: $progdir/$program" 1>&2 + func_lt_dump_args ${1+"$@"} 1>&2 + fi + exec "$progdir/$program" ${1+"$@"} + + $ECHO "$0: cannot exec $program $*" 1>&2 + exit 1 +} + +# A function to encapsulate launching the target application +# Strips options in the --lt-* namespace from $@ and +# launches target application with the remaining arguments. +func_exec_program () +{ + case " $* " in + *\ --lt-*) + for lt_wr_arg + do + case $lt_wr_arg in + --lt-*) ;; + *) set x "$@" "$lt_wr_arg"; shift;; + esac + shift + done ;; + esac + func_exec_program_core ${1+"$@"} +} + + # Parse options + func_parse_lt_options "$0" ${1+"$@"} + + # Find the directory that this script lives in. + thisdir=`$ECHO "$file" | /bin/sed 's%/[^/]*$%%'` + test "x$thisdir" = "x$file" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=`ls -ld "$file" | /bin/sed -n 's/.*-> //p'` + while test -n "$file"; do + destdir=`$ECHO "$file" | /bin/sed 's%/[^/]*$%%'` + + # If there was a directory component, then change thisdir. + if test "x$destdir" != "x$file"; then + case "$destdir" in + [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;; + *) thisdir="$thisdir/$destdir" ;; + esac + fi + + file=`$ECHO "$file" | /bin/sed 's%^.*/%%'` + file=`ls -ld "$thisdir/$file" | /bin/sed -n 's/.*-> //p'` + done + + # Usually 'no', except on cygwin/mingw when embedded into + # the cwrapper. + WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no + if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then + # special case for '.' + if test "$thisdir" = "."; then + thisdir=`pwd` + fi + # remove .libs from thisdir + case "$thisdir" in + *[\\/].libs ) thisdir=`$ECHO "$thisdir" | /bin/sed 's%[\\/][^\\/]*$%%'` ;; + .libs ) thisdir=. ;; + esac + fi + + # Try to get the absolute directory name. + absdir=`cd "$thisdir" && pwd` + test -n "$absdir" && thisdir="$absdir" + + program=lt-'oval-updater' + progdir="$thisdir/.libs" + + if test ! -f "$progdir/$program" || + { file=`ls -1dt "$progdir/$program" "$progdir/../$program" 2>/dev/null | /bin/sed 1q`; \ + test "X$file" != "X$progdir/$program"; }; then + + file="$$-$program" + + if test ! -d "$progdir"; then + mkdir "$progdir" + else + rm -f "$progdir/$file" + fi + + # relink executable if necessary + if test -n "$relink_command"; then + if relink_command_output=`eval $relink_command 2>&1`; then : + else + $ECHO "$relink_command_output" >&2 + rm -f "$progdir/$file" + exit 1 + fi + fi + + mv -f "$progdir/$file" "$progdir/$program" 2>/dev/null || + { rm -f "$progdir/$program"; + mv -f "$progdir/$file" "$progdir/$program"; } + rm -f "$progdir/$file" + fi + + if test -f "$progdir/$program"; then + if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then + # Run the actual program with our arguments. + func_exec_program ${1+"$@"} + fi + else + # The program doesn't exist. + $ECHO "$0: error: '$progdir/$program' does not exist" 1>&2 + $ECHO "This script is just a wrapper for $program." 1>&2 + $ECHO "See the libtool documentation for more information." 1>&2 + exit 1 + fi +fi