mirror of
https://github.com/edera-dev/sprout.git
synced 2026-06-21 22:30:52 +00:00
sprout: measure xen and dom0 payloads and cmdlines into PCRs 9/11/12
Extend PCR 9 with the initrd, PCR 11 with the dom0 kernel, and PCR 12 with the Xen options and dom0 cmdline. Signed-off-by: Luca Di Maio <luca.dimaio1@gmail.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
use crate::{actions, context::SproutContext};
|
use crate::{actions, context::SproutContext};
|
||||||
use alloc::rc::Rc;
|
use alloc::rc::Rc;
|
||||||
use alloc::string::String;
|
use alloc::vec::Vec;
|
||||||
use alloc::{format, vec};
|
use alloc::{format, vec};
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use edera_sprout_config::actions::chainload::ChainloadConfiguration;
|
use edera_sprout_config::actions::chainload::ChainloadConfiguration;
|
||||||
@@ -12,74 +12,96 @@ use eficore::media_loader::{
|
|||||||
XEN_EFI_CONFIG_MEDIA_GUID, XEN_EFI_KERNEL_MEDIA_GUID, XEN_EFI_RAMDISK_MEDIA_GUID,
|
XEN_EFI_CONFIG_MEDIA_GUID, XEN_EFI_KERNEL_MEDIA_GUID, XEN_EFI_RAMDISK_MEDIA_GUID,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use eficore::platform::tpm::PlatformTpm;
|
||||||
use uefi::Guid;
|
use uefi::Guid;
|
||||||
|
|
||||||
/// Builds a configuration string for the Xen EFI stub using the specified `configuration`.
|
/// Register a media loader for the provided `bytes` with the vendor `guid`.
|
||||||
fn make_xen_config(context: Rc<SproutContext>, configuration: &EderaConfiguration) -> String {
|
|
||||||
let xen_options = combine_options(context.stamp_iter(configuration.xen_options.iter()));
|
|
||||||
let kernel_options = combine_options(context.stamp_iter(configuration.kernel_options.iter()));
|
|
||||||
build_xen_config(&xen_options, &kernel_options)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register a media loader for some `text` with the vendor `guid`.
|
|
||||||
/// `what` should indicate some identifying value for error messages
|
/// `what` should indicate some identifying value for error messages
|
||||||
/// like `config` or `kernel`.
|
/// like `config` or `kernel`.
|
||||||
/// Provides a [MediaLoaderHandle] that can be used to unregister the media loader.
|
/// Provides a [MediaLoaderHandle] that can be used to unregister the media loader.
|
||||||
fn register_media_loader_text(guid: Guid, what: &str, text: String) -> Result<MediaLoaderHandle> {
|
fn register_media_loader_bytes(
|
||||||
MediaLoaderHandle::register(guid, text.as_bytes().to_vec().into_boxed_slice())
|
|
||||||
.context(format!("unable to register {} media loader", what)) /* */
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register a media loader for the file `path` with the vendor `guid`.
|
|
||||||
/// `what` should indicate some identifying value for error messages
|
|
||||||
/// like `config` or `kernel`.
|
|
||||||
/// Provides a [MediaLoaderHandle] that can be used to unregister the media loader.
|
|
||||||
fn register_media_loader_file(
|
|
||||||
context: &Rc<SproutContext>,
|
|
||||||
guid: Guid,
|
guid: Guid,
|
||||||
what: &str,
|
what: &str,
|
||||||
path: &str,
|
bytes: Vec<u8>,
|
||||||
) -> Result<MediaLoaderHandle> {
|
) -> Result<MediaLoaderHandle> {
|
||||||
// Stamp the path to the file.
|
MediaLoaderHandle::register(guid, bytes.into_boxed_slice())
|
||||||
|
.context(format!("unable to register {} media loader", what))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read the contents of the loader payload at `path` relative to the sprout image.
|
||||||
|
/// `what` should indicate some identifying value for error messages
|
||||||
|
/// like `kernel` or `initrd`.
|
||||||
|
fn read_loader_payload(context: &Rc<SproutContext>, what: &str, path: &str) -> Result<Vec<u8>> {
|
||||||
let path = context.stamp(path);
|
let path = context.stamp(path);
|
||||||
// Read the file contents.
|
eficore::path::read_file_contents(Some(context.root().loaded_image_path()?), &path)
|
||||||
let content =
|
.context(format!("unable to read {} file", what))
|
||||||
eficore::path::read_file_contents(Some(context.root().loaded_image_path()?), &path)
|
|
||||||
.context(format!("unable to read {} file", what))?;
|
|
||||||
// Register the media loader.
|
|
||||||
let handle = MediaLoaderHandle::register(guid, content.into_boxed_slice())
|
|
||||||
.context(format!("unable to register {} media loader", what))?;
|
|
||||||
Ok(handle)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes the edera action which will boot the Edera hypervisor with the specified
|
/// Executes the edera action which will boot the Edera hypervisor with the specified
|
||||||
/// `configuration` and `context`. This action uses Edera-specific Xen EFI stub functionality.
|
/// `configuration` and `context`. This action uses Edera-specific Xen EFI stub functionality.
|
||||||
pub fn edera(context: Rc<SproutContext>, configuration: &EderaConfiguration) -> Result<()> {
|
pub fn edera(context: Rc<SproutContext>, configuration: &EderaConfiguration) -> Result<()> {
|
||||||
// Build the Xen config file content for this configuration.
|
// Only register the initrd media loader if the user actually configured one.
|
||||||
let config = make_xen_config(context.clone(), configuration);
|
let xen_opts = combine_options(context.stamp_iter(configuration.xen_options.iter()));
|
||||||
|
let dom0_args = combine_options(context.stamp_iter(configuration.kernel_options.iter()));
|
||||||
|
|
||||||
// Register the media loader for the config.
|
// Measure xen options and dom0 cmdline into PCR 12 in a fixed order.
|
||||||
let config = register_media_loader_text(XEN_EFI_CONFIG_MEDIA_GUID, "config", config)
|
PlatformTpm::log_event(
|
||||||
.context("unable to register config media loader")?;
|
PlatformTpm::PCR_KERNEL_PARAMETERS,
|
||||||
|
xen_opts.as_bytes(),
|
||||||
// Register the media loaders for the kernel.
|
"sprout: xen options",
|
||||||
let kernel = register_media_loader_file(
|
|
||||||
&context,
|
|
||||||
XEN_EFI_KERNEL_MEDIA_GUID,
|
|
||||||
"kernel",
|
|
||||||
&configuration.kernel,
|
|
||||||
)
|
)
|
||||||
.context("unable to register kernel media loader")?;
|
.context("unable to measure xen options into the TPM")?;
|
||||||
|
PlatformTpm::log_event(
|
||||||
|
PlatformTpm::PCR_KERNEL_PARAMETERS,
|
||||||
|
dom0_args.as_bytes(),
|
||||||
|
"sprout: dom0 cmdline",
|
||||||
|
)
|
||||||
|
.context("unable to measure dom0 cmdline into the TPM")?;
|
||||||
|
|
||||||
|
// Build the Xen config text and register it as the config media loader. The
|
||||||
|
// assembled text is a derived artifact, intentionally not measured.
|
||||||
|
let config_handle = register_media_loader_bytes(
|
||||||
|
XEN_EFI_CONFIG_MEDIA_GUID,
|
||||||
|
"config",
|
||||||
|
build_xen_config(&xen_opts, &dom0_args).into_bytes(),
|
||||||
|
)
|
||||||
|
.context("unable to register config media loader")?;
|
||||||
|
|
||||||
|
// Read the dom0 kernel, measure it into PCR 11, then register the kernel
|
||||||
|
// media loader.
|
||||||
|
let kernel_bytes = read_loader_payload(&context, "kernel", &configuration.kernel)?;
|
||||||
|
PlatformTpm::log_event(
|
||||||
|
PlatformTpm::PCR_KERNEL,
|
||||||
|
&kernel_bytes,
|
||||||
|
"sprout: dom0 kernel",
|
||||||
|
)
|
||||||
|
.context("unable to measure dom0 kernel into the TPM")?;
|
||||||
|
let kernel_handle =
|
||||||
|
register_media_loader_bytes(XEN_EFI_KERNEL_MEDIA_GUID, "kernel", kernel_bytes)
|
||||||
|
.context("unable to register kernel media loader")?;
|
||||||
|
|
||||||
|
// Extend PCR 9 with the initrd bytes (empty when no initrd is
|
||||||
|
// configured).
|
||||||
|
let initrd_bytes = match empty_is_none(configuration.initrd.as_ref()) {
|
||||||
|
Some(p) => read_loader_payload(&context, "initrd", p)?,
|
||||||
|
None => Vec::new(),
|
||||||
|
};
|
||||||
|
PlatformTpm::log_event(
|
||||||
|
PlatformTpm::PCR_INITRD,
|
||||||
|
&initrd_bytes,
|
||||||
|
"sprout: dom0 initrd",
|
||||||
|
)
|
||||||
|
.context("unable to measure dom0 initrd into the TPM")?;
|
||||||
|
|
||||||
// Create a vector of media loaders to drop them only after this function completes.
|
// Create a vector of media loaders to drop them only after this function completes.
|
||||||
let mut media_loaders = vec![config, kernel];
|
let mut media_loaders = vec![config_handle, kernel_handle];
|
||||||
|
|
||||||
// Register the initrd if it is provided.
|
// Register the initrd if it is provided.
|
||||||
if let Some(initrd) = empty_is_none(configuration.initrd.as_ref()) {
|
if !initrd_bytes.is_empty() {
|
||||||
let initrd =
|
let initrd_handle =
|
||||||
register_media_loader_file(&context, XEN_EFI_RAMDISK_MEDIA_GUID, "initrd", initrd)
|
register_media_loader_bytes(XEN_EFI_RAMDISK_MEDIA_GUID, "initrd", initrd_bytes)
|
||||||
.context("unable to register initrd media loader")?;
|
.context("unable to register initrd media loader")?;
|
||||||
media_loaders.push(initrd);
|
media_loaders.push(initrd_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chainload to the Xen EFI stub.
|
// Chainload to the Xen EFI stub.
|
||||||
|
|||||||
@@ -38,6 +38,15 @@ impl PlatformTpm {
|
|||||||
/// The PCR for measuring the bootloader configuration into.
|
/// The PCR for measuring the bootloader configuration into.
|
||||||
pub const PCR_BOOT_LOADER_CONFIG: PcrIndex = PcrIndex(5);
|
pub const PCR_BOOT_LOADER_CONFIG: PcrIndex = PcrIndex(5);
|
||||||
|
|
||||||
|
/// The PCR for measuring the dom0 initrd payload into.
|
||||||
|
pub const PCR_INITRD: PcrIndex = PcrIndex(9);
|
||||||
|
|
||||||
|
/// The PCR for measuring the dom0 kernel payload into.
|
||||||
|
pub const PCR_KERNEL: PcrIndex = PcrIndex(11);
|
||||||
|
|
||||||
|
/// The PCR for measuring kernel command line and xen options into.
|
||||||
|
pub const PCR_KERNEL_PARAMETERS: PcrIndex = PcrIndex(12);
|
||||||
|
|
||||||
/// Acquire access to the TPM protocol handle, if possible.
|
/// Acquire access to the TPM protocol handle, if possible.
|
||||||
/// Returns None if TPM is not available.
|
/// Returns None if TPM is not available.
|
||||||
fn protocol() -> Result<Option<TpmProtocolHandle>> {
|
fn protocol() -> Result<Option<TpmProtocolHandle>> {
|
||||||
|
|||||||
Reference in New Issue
Block a user