Skip to content

Commit

Permalink
config, tpm_main: explicitly handle YAML load errors
Browse files Browse the repository at this point in the history
yaml_to_dict(..) now returns None if loading failed.
This might happen when non text characters are in the input string.
(Which can be the case for tpm2_eventlog)

Signed-off-by: Thore Sommer <mail@thson.de>
  • Loading branch information
THS-on committed Dec 13, 2021
1 parent 3fd0536 commit 12952a3
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 15 deletions.
12 changes: 9 additions & 3 deletions keylime/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
SPDX-License-Identifier: Apache-2.0
Copyright 2017 Massachusetts Institute of Technology.
'''

import os
import os.path
import configparser
Expand All @@ -11,13 +10,15 @@
import re
from http.server import BaseHTTPRequestHandler
import http.client
from typing import Optional

import tornado.web
import yaml
try:
from yaml import CSafeLoader as SafeLoader
except ImportError:
from yaml import SafeLoader
from yaml.reader import ReaderError

from keylime import api_version as keylime_api_version
from keylime import json
Expand Down Expand Up @@ -245,10 +246,15 @@ def list_to_dict(alist):
return params


def yaml_to_dict(arry, add_newlines=True):
def yaml_to_dict(arry, add_newlines=True, logger=None) -> Optional[dict]:
arry = convert(arry)
sep = "\n" if add_newlines else ""
return yaml.load(sep.join(arry), Loader=SafeLoader)
try:
return yaml.load(sep.join(arry), Loader=SafeLoader)
except ReaderError as err:
if logger is not None:
logger.warning("Could not load yaml as dict: %s", str(err))
return None


def get_restful_params(urlstring):
Expand Down
44 changes: 32 additions & 12 deletions keylime/tpm/tpm_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,10 @@ def __get_tpm_algorithms(self):
output = output.replace("clear", "0")
output = [output]

retyaml = config.yaml_to_dict(output)
retyaml = config.yaml_to_dict(output, logger=logger)
if retyaml is None:
logger.warning("Could not read YAML output of tpm2_getcap.")
return
for algorithm, details in retyaml.items():
if details["asymmetric"] == 1 and details["object"] == 1 and algorithms.Encrypt.is_recognized(algorithm):
self.supported['encrypt'].add(algorithm)
Expand Down Expand Up @@ -419,7 +422,7 @@ def __create_ek(self, asym_alg=None):
if code != tpm_abstract.AbstractTPM.EXIT_SUCESS:
raise Exception("tpm2_getcap failed with code " + str(code) + ": " + str(reterr))

outjson = config.yaml_to_dict(output)
outjson = config.yaml_to_dict(output, logger=logger)
if outjson is not None and hex(current_handle) in outjson:
if self.tools_version == "3.2":
cmd = ["tpm2_evictcontrol", "-A", "o", "-H",
Expand Down Expand Up @@ -469,7 +472,9 @@ def __create_ek(self, asym_alg=None):
handle = int(0x81010007)
elif self.tools_version in ["4.0", "4.2"]:
handle = None
retyaml = config.yaml_to_dict(output)
retyaml = config.yaml_to_dict(output, logger=logger)
if retyaml is None:
raise Exception("Could not read YAML output of tpm2_createek.")
if "persistent-handle" in retyaml:
handle = retyaml["persistent-handle"]

Expand Down Expand Up @@ -603,7 +608,7 @@ def __create_aik(self, asym_alg=None, hash_alg=None, sign_alg=None):
output = output.replace("0x", " - 0x")
output = [output]

outjson = config.yaml_to_dict(output)
outjson = config.yaml_to_dict(output, logger=logger)
if self.tools_version == "3.2":
evict_it = outjson is not None and aik_handle in outjson
elif self.tools_version in ["4.0", "4.2"]:
Expand Down Expand Up @@ -664,7 +669,9 @@ def __create_aik(self, asym_alg=None, hash_alg=None, sign_alg=None):
if code != tpm_abstract.AbstractTPM.EXIT_SUCESS:
raise Exception("tpm2_createak failed with code " + str(code) + ": " + str(reterr))

jsonout = config.yaml_to_dict(retout)
jsonout = config.yaml_to_dict(retout, logger=logger)
if jsonout is None:
raise Exception("unable to parse YAML output of tpm2_createak. Is your tpm2-tools installation up to date?")
aik_tpm = retDict['fileouts'][akpubfile.name]
if aik_tpm == "":
raise Exception("unable to read public aik from create identity. Is your tpm2-tools installation up to date?")
Expand Down Expand Up @@ -711,7 +718,10 @@ def flush_keys(self):
retout = [retout]

owner_pw = self.get_tpm_metadata("owner_pw")
jsonout = config.yaml_to_dict(retout)
jsonout = config.yaml_to_dict(retout, logger=logger)
if jsonout is None:
logger.warning("Could not read YAML output of tpm2_getcap.")
jsonout = {}
for key in jsonout:
if str(hex(key)) != self.defaults['ek_handle']:
logger.debug("Flushing key handle %s" % hex(key))
Expand Down Expand Up @@ -907,7 +917,9 @@ def get_tpm_manufacturer(self):
for i, s in enumerate(output):
output[i] = re.sub(r"[\x01-\x1F\x7F]", "", s.decode('utf-8')).encode('utf-8')

retyaml = config.yaml_to_dict(output)
retyaml = config.yaml_to_dict(output, logger=logger)
if retyaml is None:
raise Exception("Could not read YAML output of tpm2_getcap.")
if "TPM2_PT_VENDOR_STRING_1" in retyaml:
vendorStr = retyaml["TPM2_PT_VENDOR_STRING_1"]["value"]
elif "TPM_PT_VENDOR_STRING_1" in retyaml:
Expand Down Expand Up @@ -1107,7 +1119,11 @@ def check_quote(self, agentAttestState, nonce, data, quote, aikTpmFromRegistrar,
return failure

pcrs = []
jsonout = config.yaml_to_dict(retout)
jsonout = config.yaml_to_dict(retout, logger=logger)
if jsonout is None:
failure.add_event("quote_validation", {"message": "YAML parsing failed for quote validation using tpm2-tools.",
"data": retout}, False)
return failure
if "pcrs" in jsonout:
if hash_alg in jsonout["pcrs"]:
alg_size = algorithms.get_hash_size(hash_alg) // 4
Expand Down Expand Up @@ -1151,7 +1167,9 @@ def readPCR(self, pcrval, hash_alg=None):
elif self.tools_version in ["4.0", "4.2"]:
output = config.convert(self.__run("tpm2_pcrread")['retout'])

jsonout = config.yaml_to_dict(output)
jsonout = config.yaml_to_dict(output, logger=logger)
if jsonout is None:
raise Exception("Could not read YAML output of tpm2_pcrread.")

if hash_alg not in jsonout:
raise Exception("Invalid hashing algorithm '%s' for reading PCR number %d." % (hash_alg, pcrval))
Expand Down Expand Up @@ -1213,7 +1231,7 @@ def read_ekcert_nvram(self):
if self.tools_version in ["4.0", "4.2"]:
raise Exception("tpm2_nvreadpublic for ekcert failed with code " + str(code) + ": " + str(reterr))

outjson = config.yaml_to_dict(output)
outjson = config.yaml_to_dict(output, logger=logger)

if outjson is None or 0x1c00002 not in outjson or "size" not in outjson[0x1c00002]:
logger.warning("No EK certificate found in TPM NVRAM")
Expand Down Expand Up @@ -1320,7 +1338,7 @@ def __add_boot_aggregate(self, log: dict) -> None :
except Exception:
pass

def parse_binary_bootlog(self, log_bin:bytes) -> dict:
def parse_binary_bootlog(self, log_bin:bytes) -> typing.Optional[dict]:
'''Parse and enrich a BIOS boot log
The input is the binary log.
Expand All @@ -1330,7 +1348,9 @@ def parse_binary_bootlog(self, log_bin:bytes) -> dict:
log_bin_filename = log_bin_file.name
retDict_tpm2 = self.__run(['tpm2_eventlog', '--eventlog-version=2', log_bin_filename])
log_parsed_strs = retDict_tpm2['retout']
log_parsed_data = config.yaml_to_dict(log_parsed_strs, add_newlines=False)
log_parsed_data = config.yaml_to_dict(log_parsed_strs, add_newlines=False, logger=logger)
if log_parsed_data is None:
return None
#pylint: disable=import-outside-toplevel
try:
from keylime import tpm_bootlog_enrich
Expand Down

0 comments on commit 12952a3

Please sign in to comment.