[libvirt PATCH v3 04/12] tools: support validating SEV direct kernel boot measurements

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



When doing direct kernel boot we need to include the kernel, initrd and
cmdline in the measurement.

Signed-off-by: Daniel P. Berrangé <berrange@xxxxxxxxxx>
---
 docs/manpages/virt-qemu-sev-validate.rst |  43 +++++++++
 tools/virt-qemu-sev-validate             | 108 ++++++++++++++++++++++-
 2 files changed, 150 insertions(+), 1 deletion(-)

diff --git a/docs/manpages/virt-qemu-sev-validate.rst b/docs/manpages/virt-qemu-sev-validate.rst
index ec875028cf..0aee1db76d 100644
--- a/docs/manpages/virt-qemu-sev-validate.rst
+++ b/docs/manpages/virt-qemu-sev-validate.rst
@@ -102,6 +102,20 @@ initialize AMD SEV. For the validation to be trustworthy it important that the
 firmware build used has no support for loading non-volatile variables from
 NVRAM, even if NVRAM is expose to the guest.
 
+``-k PATH``, ``--kernel=PATH``
+
+Path to the kernel binary if doing direct kernel boot.
+
+``-r PATH``, ``--initrd=PATH``
+
+Path to the initrd binary if doing direct kernel boot. Defaults to zero length
+content if omitted.
+
+``-e STRING``, ``--cmdline=STRING``
+
+String containing any kernel command line parameters used during boot of the
+domain. Defaults to the empty string if omitted.
+
 ``--tik PATH``
 
 TIK file for domain. This file must be exactly 16 bytes in size and contains the
@@ -182,6 +196,22 @@ Validate the measurement of a SEV guest booting from disk:
        --build-id 13 \
        --policy 3
 
+Validate the measurement of a SEV guest with direct kernel boot:
+
+::
+
+   # virt-dom-sev-validate \
+       --firmware OVMF.sev.fd \
+       --kernel vmlinuz-5.11.12 \
+       --initrd initramfs-5.11.12 \
+       --cmdline "root=/dev/vda1" \
+       --tk this-guest-tk.bin \
+       --measurement Zs2pf19ubFSafpZ2WKkwquXvACx9Wt/BV+eJwQ/taO8jhyIj/F8swFrybR1fZ2ID \
+       --api-major 0 \
+       --api-minor 24 \
+       --build-id 13 \
+       --policy 3
+
 Fetch from remote libvirt
 -------------------------
 
@@ -202,6 +232,19 @@ Validate the measurement of a SEV guest booting from disk:
        --tk this-guest-tk.bin \
        --domain fedora34x86_64
 
+Validate the measurement of a SEV guest with direct kernel boot:
+
+::
+
+   # virt-dom-sev-validate \
+       --connect qemu+ssh://root@xxxxxxxxxxxxxxxx/system \
+       --firmware OVMF.sev.fd \
+       --kernel vmlinuz-5.11.12 \
+       --initrd initramfs-5.11.12 \
+       --cmdline "root=/dev/vda1" \
+       --tk this-guest-tk.bin \
+       --domain fedora34x86_64
+
 Fetch from local libvirt
 ------------------------
 
diff --git a/tools/virt-qemu-sev-validate b/tools/virt-qemu-sev-validate
index d3a4d5f26b..deef34628b 100755
--- a/tools/virt-qemu-sev-validate
+++ b/tools/virt-qemu-sev-validate
@@ -34,6 +34,7 @@
 #    firmware versions with known flaws.
 #
 
+import abc
 import argparse
 from base64 import b64decode
 from hashlib import sha256
@@ -43,6 +44,7 @@ import re
 import socket
 import sys
 import traceback
+from uuid import UUID
 
 from lxml import etree
 import libvirt
@@ -70,6 +72,86 @@ class InvalidStateException(Exception):
     pass
 
 
+class GUIDTable(abc.ABC):
+    GUID_LEN = 16
+
+    def __init__(self, guid, lenlen=2):
+        self.guid = guid
+        self.lenlen = lenlen
+
+    @abc.abstractmethod
+    def entries(self):
+        pass
+
+    def build_entry(self, guid, payload, lenlen):
+        dummylen = int(0).to_bytes(lenlen, 'little')
+        entry = bytearray(guid + dummylen + payload)
+
+        lenle = len(entry).to_bytes(lenlen, 'little')
+        entry[self.GUID_LEN:(self.GUID_LEN + lenlen)] = lenle
+
+        return bytes(entry)
+
+    def build(self):
+        payload = self.entries()
+
+        if len(payload) == 0:
+            return bytes([])
+
+        dummylen = int(0).to_bytes(self.lenlen, 'little')
+        table = bytearray(self.guid + dummylen + payload)
+
+        guidlen = len(table).to_bytes(self.lenlen, 'little')
+        table[self.GUID_LEN:(self.GUID_LEN + self.lenlen)] = guidlen
+
+        pad = 16 - (len(table) % 16)
+        table += bytes([0]) * pad
+
+        log.debug("Table(hex): %s", bytes(table).hex())
+        return bytes(table)
+
+
+class KernelTable(GUIDTable):
+
+    TABLE_GUID = UUID('{9438d606-4f22-4cc9-b479-a793-d411fd21}').bytes_le
+    KERNEL_GUID = UUID('{4de79437-abd2-427f-b835-d5b1-72d2045b}').bytes_le
+    INITRD_GUID = UUID('{44baf731-3a2f-4bd7-9af1-41e2-9169781d}').bytes_le
+    CMDLINE_GUID = UUID('{97d02dd8-bd20-4c94-aa78-e771-4d36ab2a}').bytes_le
+
+    def __init__(self):
+        super().__init__(guid=self.TABLE_GUID,
+                         lenlen=2)
+
+        self.kernel = None
+        self.initrd = sha256(bytes([])).digest()
+        self.cmdline = sha256(bytes([0])).digest()
+
+    def load_kernel(self, path):
+        with open(path, "rb") as fh:
+            self.kernel = sha256(fh.read()).digest()
+
+    def load_initrd(self, path):
+        with open(path, "rb") as fh:
+            self.initrd = sha256(fh.read()).digest()
+
+    def load_cmdline(self, val):
+        self.cmdline = sha256(val.encode("utf8") + bytes([0])).digest()
+
+    def entries(self):
+        entries = bytes([])
+        if self.kernel is None:
+            return entries
+
+        log.debug("Kernel(sha256): %s", self.kernel.hex())
+        log.debug("Initrd(sha256): %s", self.initrd.hex())
+        log.debug("Cmdline(sha256): %s", self.cmdline.hex())
+        entries += self.build_entry(self.CMDLINE_GUID, self.cmdline, 2)
+        entries += self.build_entry(self.INITRD_GUID, self.initrd, 2)
+        entries += self.build_entry(self.KERNEL_GUID, self.kernel, 2)
+
+        return entries
+
+
 class ConfidentialVM(object):
 
     def __init__(self,
@@ -88,6 +170,8 @@ class ConfidentialVM(object):
         self.tik = None
         self.tek = None
 
+        self.kernel_table = KernelTable()
+
     def load_tik_tek(self, tik_path, tek_path):
         with open(tik_path, 'rb') as fh:
             self.tik = fh.read()
@@ -129,8 +213,10 @@ class ConfidentialVM(object):
     # of the following:
     #
     #  - The firmware blob
+    #  - The kernel GUID table
     def get_measured_data(self):
-        measured_data = self.firmware
+        measured_data = (self.firmware +
+                         self.kernel_table.build())
         log.debug("Measured-data(sha256): %s",
                   sha256(measured_data).hexdigest())
         return measured_data
@@ -303,6 +389,12 @@ def parse_command_line():
     vmconfig = parser.add_argument_group("Virtual machine config")
     vmconfig.add_argument('--firmware', '-f',
                           help='Path to the firmware binary')
+    vmconfig.add_argument('--kernel', '-k',
+                          help='Path to the kernel binary')
+    vmconfig.add_argument('--initrd', '-r',
+                          help='Path to the initrd binary')
+    vmconfig.add_argument('--cmdline', '-e',
+                          help='Cmdline string booted with')
     vmconfig.add_argument('--tik',
                           help='TIK file for domain')
     vmconfig.add_argument('--tek',
@@ -361,6 +453,11 @@ def check_usage(args):
             raise UnsupportedUsageException(
                 "Either --firmware or --domain is required")
 
+    if args.kernel is None:
+        if args.initrd is not None or args.cmdline is not None:
+            raise UnsupportedUsageException(
+                "--initrd/--cmdline require --kernel")
+
 
 def attest(args):
     if args.domain is None:
@@ -384,6 +481,15 @@ def attest(args):
     else:
         cvm.load_tik_tek(args.tik, args.tek)
 
+    if args.kernel is not None:
+        cvm.kernel_table.load_kernel(args.kernel)
+
+    if args.initrd is not None:
+        cvm.kernel_table.load_initrd(args.initrd)
+
+    if args.cmdline is not None:
+        cvm.kernel_table.load_cmdline(args.cmdline)
+
     if args.domain is not None:
         cvm.load_domain(args.connect,
                         args.domain,
-- 
2.37.3




[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]

  Powered by Linux