On Tue, 14 Apr 2020, Lukasz Hawrylko wrote:
I don't know if that tool exists. Anyway, I will look at that multiple
SINITs bug in TBOOT, when it will be fixed, that kind of tool will not
be required.
True, that would mostly not be needed if tboot worked automatically. I can
think of two use cases where it might still be useful:
1) You could save disk space and boot speed by not having grub read all
SINIT modules from disk.
2) You might want to know before reboot that you have the matching SINIT
module if you are enabling TPM support remotely for your server :)
In mean time, you can check acminfo from utils directory. It examines
SINIT binary and also can check if SINIT is compatible with current
platform. You can easily adopt it (with bash scripting help) to do what
you need.
Thanks, I will look into that.
Currently I am trying to automate sealing of disk encryption keys
after an upgrade. Here's a quick and dirty prototype that "works on my
server"(TM) but probably contains many bugs. In particular it does not
currently know how to predict PCR-17 and just assumes that it has the same
value on next reboot. Posting it here in the hope that this will activate
discussion on the list for potential alternatives :)
#!/usr/bin/python3
# tpm-luks-auto-seal 2020-04-14
import argparse
import subprocess
import hashlib
import binascii
import glob
import re
import os
import tempfile
INITIAL_HASH = b'\x00'*20
def text_to_hash(text):
hash = binascii.unhexlify(text.replace(' ', '').replace('\n', ''))
assert len(hash) == 20
return hash
def hash_to_text(hash):
assert len(hash) == 20
return binascii.hexlify(hash).decode('utf-8')
def extend_hash(hash1, hash2):
assert len(hash1) == 20
assert len(hash2) == 20
return sha1(hash1 + hash2)
def sha1(data):
m = hashlib.sha1()
m.update(data)
return m.digest()
def predict_pcrs(configuration):
pcr = {}
pcr[17] = get_current_pcrs()[17]
pcr[18] = INITIAL_HASH
tboot_hash = text_to_hash(subprocess.check_output([
'/usr/sbin/lcp_mlehash',
'-c',
configuration.tboot_cmdline,
configuration.tboot], encoding='utf-8'))
pcr[18] = extend_hash(pcr[18], tboot_hash)
with open(configuration.kernel, 'rb') as f:
kernel_hash = sha1(sha1(configuration.kernel_cmdline.encode('utf-8')) +
sha1(f.read()))
pcr[18] = extend_hash(pcr[18], kernel_hash)
pcr[19] = INITIAL_HASH
with open(configuration.initrd, 'rb') as f:
initrd_hash = sha1(sha1(b'') + sha1(f.read()))
pcr[19] = extend_hash(pcr[19], initrd_hash)
return pcr
class Configuration:
def __init__(self, tboot, tboot_cmdline, kernel, kernel_cmdline, initrd):
self.tboot = tboot
self.tboot_cmdline = tboot_cmdline
self.kernel = kernel
self.kernel_cmdline = kernel_cmdline
self.initrd = initrd
def __str__(self):
return "{}({}) {}({}) {}".format(self.tboot, self.tboot_cmdline,
self.kernel, self.kernel_cmdline, self.initrd)
def get_current_pcrs():
pcrs = {}
with open("/sys/devices/pnp0/00:05/tpm/tpm0/pcrs") as f:
for line in f.readlines():
assert line.startswith("PCR-")
n = int(line.split(":")[0].split("-")[1])
pcrs[n] = text_to_hash(line.split(":")[1])
return pcrs
def get_grub_entries(cfg, prefix):
inside_entry = False
entries = []
with open(cfg) as f:
for line in f.readlines():
line = line.rstrip().strip()
if line.startswith("menuentry"):
inside_entry = True
entry = {}
elif inside_entry:
parts = re.split("\s+", line)
if line.startswith("}"):
if "tboot_cmdline" in entry:
entries.append(entry)
inside_entry = False
elif parts[0] == "multiboot":
entry["tboot"] = prefix + parts[1]
entry["tboot_cmdline"] = parts[2]
elif parts[0] == "module" and "vmlinuz" in parts[1]:
entry["kernel"] = prefix + parts[1]
entry["kernel_cmdline"] = " ".join(parts[2:])
elif parts[0] == "module" and "initrd" in parts[1]:
entry["initrd"] = prefix + parts[1]
return entries
def get_configurations():
configurations = []
for e in get_grub_entries("/boot/grub/grub.cfg", "/boot"):
assert os.path.exists(e["tboot"])
assert os.path.exists(e["kernel"])
assert os.path.exists(e["initrd"])
if " single " in e["kernel_cmdline"]:
# Skip for now
continue
c = Configuration(tboot=e["tboot"],
tboot_cmdline=e["tboot_cmdline"],
kernel=e["kernel"],
kernel_cmdline=e["kernel_cmdline"],
initrd=e["initrd"])
configurations.append(c)
return configurations
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--luks-key-file", default="/etc/luks.key")
parser.add_argument("--force", action="store_true")
args = parser.parse_args()
print("==== Current configuration")
current_pcr = get_current_pcrs()
for i in range(17, 20):
print("PCR-{} {}".format(i, hash_to_text(current_pcr[i])))
configurations = get_configurations()
count = 1
pcrs = []
for c in configurations:
print("==== Predicted configuration #{}".format(count))
print("# {}".format(c))
pcr = predict_pcrs(c)
for i in range(17, 20):
print("PCR-{} {}{}".format(i, hash_to_text(pcr[i]), "*" if
pcr[i]!=current_pcr[i] else ""))
count += 1
pcrs.append(pcr)
with open(args.luks_key_file) as f:
luks_key_length = len(f.read())
assert luks_key_length >= 12
assert luks_key_length < 128
if not args.force:
reply = input("Do you want to enable these configurations (use --force to
automate this) (Y/n)? ")
if not reply.lower().startswith("y"):
print("Aborting")
sys.exit(0)
print("Removing old configurations (1-20)")
for i in range(20):
try:
subprocess.check_call(["tpm_nvrelease",
"-i", str(i+1),
"--pwdo=1234"])
except subprocess.CalledProcessError:
pass
for i in range(len(configurations)):
print("Enabling configuration #{}".format(i+1))
with tempfile.NamedTemporaryFile(mode="w+") as f:
for j in range(17, 20):
f.write("r {} {}\n".format(j, hash_to_text(pcrs[i][j])))
f.flush()
subprocess.check_call(["tpm_nvdefine",
"-i", str(i+1),
"-s", str(luks_key_length),
"-p", "OWNERWRITE|READ_STCLEAR",
"--pwdo=1234",
"-z",
"-f", f.name])
subprocess.check_call(["tpm_nvwrite",
"-i", str(i+1),
"-f", args.luks_key_file,
"-z", "--password=1234"])
-Timo
_______________________________________________
tboot-devel mailing list
tboot-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/tboot-devel