commit
354ec6f451
|
@ -124,6 +124,8 @@
|
|||
inherit cargoArtifacts;
|
||||
cargoClippyExtraArgs = "-- --deny warnings";
|
||||
});
|
||||
|
||||
rustfmt = craneLib.cargoFmt (commonArgs // { inherit cargoArtifacts; });
|
||||
};
|
||||
|
||||
stubCrane = buildRustApp {
|
||||
|
@ -174,6 +176,8 @@
|
|||
checks = {
|
||||
toolClippy = toolCrane.clippy;
|
||||
stubClippy = stubCrane.clippy;
|
||||
toolFmt = toolCrane.rustfmt;
|
||||
stubFmt = stubCrane.rustfmt;
|
||||
} // (import ./nix/tests/lanzaboote.nix {
|
||||
inherit pkgs;
|
||||
lanzabooteModule = self.nixosModules.lanzaboote;
|
||||
|
|
|
@ -1,5 +1,21 @@
|
|||
use alloc::{vec::Vec, vec, string::ToString, format};
|
||||
use uefi::{Result, Handle, prelude::{BootServices, RuntimeServices}, proto::{device_path::{DevicePath, media::{HardDrive, PartitionSignature}, DeviceType, DeviceSubType, text::DevicePathToText}, loaded_image::LoadedImage}, Guid, guid, table::{runtime::{VariableVendor, VariableAttributes}, SystemTable, Boot}, cstr16, CStr16};
|
||||
use alloc::{format, string::ToString, vec, vec::Vec};
|
||||
use uefi::{
|
||||
cstr16, guid,
|
||||
prelude::{BootServices, RuntimeServices},
|
||||
proto::{
|
||||
device_path::{
|
||||
media::{HardDrive, PartitionSignature},
|
||||
text::DevicePathToText,
|
||||
DevicePath, DeviceSubType, DeviceType,
|
||||
},
|
||||
loaded_image::LoadedImage,
|
||||
},
|
||||
table::{
|
||||
runtime::{VariableAttributes, VariableVendor},
|
||||
Boot, SystemTable,
|
||||
},
|
||||
CStr16, Guid, Handle, Result,
|
||||
};
|
||||
|
||||
use bitflags::bitflags;
|
||||
|
||||
|
@ -7,7 +23,9 @@ fn disk_get_part_uuid(boot_services: &BootServices, disk_handle: Handle) -> Resu
|
|||
let dp = boot_services.open_protocol_exclusive::<DevicePath>(disk_handle)?;
|
||||
|
||||
for node in dp.node_iter() {
|
||||
if node.device_type() != DeviceType::MEDIA || node.sub_type() != DeviceSubType::MEDIA_HARD_DRIVE {
|
||||
if node.device_type() != DeviceType::MEDIA
|
||||
|| node.sub_type() != DeviceSubType::MEDIA_HARD_DRIVE
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -21,12 +39,12 @@ fn disk_get_part_uuid(boot_services: &BootServices, disk_handle: Handle) -> Resu
|
|||
Err(uefi::Status::UNSUPPORTED.into())
|
||||
}
|
||||
|
||||
|
||||
/// systemd loader's GUID
|
||||
/// != systemd's GUID
|
||||
/// https://github.com/systemd/systemd/blob/main/src/boot/efi/util.h#L114-L121
|
||||
/// https://systemd.io/BOOT_LOADER_INTERFACE/
|
||||
pub const BOOT_LOADER_VENDOR_UUID: VariableVendor = VariableVendor(guid!("4a67b082-0a4c-41cf-b6c7-440b29bb8c4f"));
|
||||
pub const BOOT_LOADER_VENDOR_UUID: VariableVendor =
|
||||
VariableVendor(guid!("4a67b082-0a4c-41cf-b6c7-440b29bb8c4f"));
|
||||
|
||||
/// Lanzaboote stub name
|
||||
pub static STUB_INFO_STRING: &str = concat!("lanzastub ", env!("CARGO_PKG_VERSION"));
|
||||
|
@ -54,17 +72,20 @@ bitflags! {
|
|||
/// Get the currently supported EFI features from the loader if they do exist
|
||||
/// https://systemd.io/BOOT_LOADER_INTERFACE/
|
||||
pub fn get_loader_features(runtime_services: &RuntimeServices) -> Result<EfiLoaderFeatures> {
|
||||
if let Ok(size) = runtime_services.get_variable_size(cstr16!("LoaderFeatures"), &BOOT_LOADER_VENDOR_UUID) {
|
||||
if let Ok(size) =
|
||||
runtime_services.get_variable_size(cstr16!("LoaderFeatures"), &BOOT_LOADER_VENDOR_UUID)
|
||||
{
|
||||
let mut buffer = vec![0; size].into_boxed_slice();
|
||||
runtime_services.get_variable(
|
||||
cstr16!("LoaderFeatures"),
|
||||
&BOOT_LOADER_VENDOR_UUID,
|
||||
&mut buffer)?;
|
||||
&mut buffer,
|
||||
)?;
|
||||
|
||||
return EfiLoaderFeatures::from_bits(
|
||||
u64::from_le_bytes(
|
||||
(*buffer).try_into()
|
||||
.map_err(|_err| uefi::Status::BAD_BUFFER_SIZE)?
|
||||
return EfiLoaderFeatures::from_bits(u64::from_le_bytes(
|
||||
(*buffer)
|
||||
.try_into()
|
||||
.map_err(|_err| uefi::Status::BAD_BUFFER_SIZE)?,
|
||||
))
|
||||
.ok_or_else(|| uefi::Status::INCOMPATIBLE_VERSION.into());
|
||||
}
|
||||
|
@ -106,21 +127,19 @@ pub fn cstr16_to_bytes(s: &CStr16) -> &[u8] {
|
|||
|
||||
/// Ensures that an UEFI variable is set or set it with a fallback value
|
||||
/// computed in a lazy way.
|
||||
pub fn ensure_efi_variable<F>(runtime_services: &RuntimeServices,
|
||||
pub fn ensure_efi_variable<F>(
|
||||
runtime_services: &RuntimeServices,
|
||||
name: &CStr16,
|
||||
vendor: &VariableVendor,
|
||||
attributes: VariableAttributes,
|
||||
get_fallback_value: F) -> uefi::Result
|
||||
where F: FnOnce() -> uefi::Result<Vec<u8>>
|
||||
get_fallback_value: F,
|
||||
) -> uefi::Result
|
||||
where
|
||||
F: FnOnce() -> uefi::Result<Vec<u8>>,
|
||||
{
|
||||
// If we get a variable size, a variable already exist.
|
||||
if runtime_services.get_variable_size(name, vendor).is_err() {
|
||||
runtime_services.set_variable(
|
||||
name,
|
||||
vendor,
|
||||
attributes,
|
||||
&get_fallback_value()?
|
||||
)?;
|
||||
runtime_services.set_variable(name, vendor, attributes, &get_fallback_value()?)?;
|
||||
}
|
||||
|
||||
uefi::Status::SUCCESS.into()
|
||||
|
@ -131,91 +150,115 @@ pub fn export_efi_variables(system_table: &SystemTable<Boot>) -> Result<()> {
|
|||
let boot_services = system_table.boot_services();
|
||||
let runtime_services = system_table.runtime_services();
|
||||
|
||||
let stub_features: EfiStubFeatures =
|
||||
EfiStubFeatures::ReportBootPartition;
|
||||
let stub_features: EfiStubFeatures = EfiStubFeatures::ReportBootPartition;
|
||||
|
||||
let loaded_image =
|
||||
boot_services.open_protocol_exclusive::<LoadedImage>(boot_services.image_handle())?;
|
||||
|
||||
let default_attributes = VariableAttributes::BOOTSERVICE_ACCESS | VariableAttributes::RUNTIME_ACCESS;
|
||||
let default_attributes =
|
||||
VariableAttributes::BOOTSERVICE_ACCESS | VariableAttributes::RUNTIME_ACCESS;
|
||||
|
||||
#[allow(unused_must_use)]
|
||||
// LoaderDevicePartUUID
|
||||
ensure_efi_variable(runtime_services,
|
||||
ensure_efi_variable(
|
||||
runtime_services,
|
||||
cstr16!("LoaderDevicePartUUID"),
|
||||
&BOOT_LOADER_VENDOR_UUID,
|
||||
default_attributes,
|
||||
|| disk_get_part_uuid(boot_services, loaded_image.device()).map(|guid| guid.to_string()
|
||||
|| {
|
||||
disk_get_part_uuid(boot_services, loaded_image.device()).map(|guid| {
|
||||
guid.to_string()
|
||||
.encode_utf16()
|
||||
.flat_map(|c| c.to_le_bytes())
|
||||
.collect::<Vec<u8>>()
|
||||
})
|
||||
},
|
||||
)
|
||||
).ok();
|
||||
.ok();
|
||||
// LoaderImageIdentifier
|
||||
ensure_efi_variable(runtime_services,
|
||||
ensure_efi_variable(
|
||||
runtime_services,
|
||||
cstr16!("LoaderImageIdentifier"),
|
||||
&BOOT_LOADER_VENDOR_UUID,
|
||||
default_attributes,
|
||||
|| {
|
||||
if let Some(dp) = loaded_image.file_path() {
|
||||
let dp_protocol = boot_services.open_protocol_exclusive::<DevicePathToText>(
|
||||
boot_services.get_handle_for_protocol::<DevicePathToText>()?
|
||||
boot_services.get_handle_for_protocol::<DevicePathToText>()?,
|
||||
)?;
|
||||
dp_protocol.convert_device_path_to_text(
|
||||
dp_protocol
|
||||
.convert_device_path_to_text(
|
||||
boot_services,
|
||||
dp,
|
||||
uefi::proto::device_path::text::DisplayOnly(false),
|
||||
uefi::proto::device_path::text::AllowShortcuts(false)
|
||||
).map(|ps| cstr16_to_bytes(&ps).to_vec())
|
||||
uefi::proto::device_path::text::AllowShortcuts(false),
|
||||
)
|
||||
.map(|ps| cstr16_to_bytes(&ps).to_vec())
|
||||
} else {
|
||||
// If we cannot retrieve the filepath of the loaded image
|
||||
// Then, we cannot set `LoaderImageIdentifier`.
|
||||
Err(uefi::Status::UNSUPPORTED.into())
|
||||
}
|
||||
}).ok();
|
||||
},
|
||||
)
|
||||
.ok();
|
||||
// LoaderFirmwareInfo
|
||||
ensure_efi_variable(runtime_services,
|
||||
ensure_efi_variable(
|
||||
runtime_services,
|
||||
cstr16!("LoaderFirmwareInfo"),
|
||||
&BOOT_LOADER_VENDOR_UUID,
|
||||
default_attributes,
|
||||
|| Ok(
|
||||
format!("{} {}.{:02}", system_table.firmware_vendor(), system_table.firmware_revision() >> 16, system_table.firmware_revision() & 0xFFFFF)
|
||||
|| {
|
||||
Ok(format!(
|
||||
"{} {}.{:02}",
|
||||
system_table.firmware_vendor(),
|
||||
system_table.firmware_revision() >> 16,
|
||||
system_table.firmware_revision() & 0xFFFFF
|
||||
)
|
||||
.encode_utf16()
|
||||
.flat_map(|c| c.to_le_bytes())
|
||||
.collect::<Vec<u8>>()
|
||||
.collect::<Vec<u8>>())
|
||||
},
|
||||
)
|
||||
).ok();
|
||||
.ok();
|
||||
// LoaderFirmwareType
|
||||
ensure_efi_variable(runtime_services,
|
||||
ensure_efi_variable(
|
||||
runtime_services,
|
||||
cstr16!("LoaderFirmwareType"),
|
||||
&BOOT_LOADER_VENDOR_UUID,
|
||||
default_attributes,
|
||||
|| Ok(
|
||||
format!("UEFI {:02}", system_table.uefi_revision())
|
||||
|| {
|
||||
Ok(format!("UEFI {:02}", system_table.uefi_revision())
|
||||
.encode_utf16()
|
||||
.flat_map(|c| c.to_le_bytes())
|
||||
.collect::<Vec<u8>>()
|
||||
.collect::<Vec<u8>>())
|
||||
},
|
||||
)
|
||||
).ok();
|
||||
.ok();
|
||||
// StubInfo
|
||||
// FIXME: ideally, no one should be able to overwrite `StubInfo`, but that would require
|
||||
// constructing an EFI authenticated variable payload. This seems overcomplicated for now.
|
||||
runtime_services.set_variable(
|
||||
runtime_services
|
||||
.set_variable(
|
||||
cstr16!("StubInfo"),
|
||||
&BOOT_LOADER_VENDOR_UUID,
|
||||
default_attributes,
|
||||
&STUB_INFO_STRING.encode_utf16().flat_map(|c| c.to_le_bytes()).collect::<Vec<u8>>()
|
||||
).ok();
|
||||
&STUB_INFO_STRING
|
||||
.encode_utf16()
|
||||
.flat_map(|c| c.to_le_bytes())
|
||||
.collect::<Vec<u8>>(),
|
||||
)
|
||||
.ok();
|
||||
|
||||
// StubFeatures
|
||||
runtime_services.set_variable(
|
||||
runtime_services
|
||||
.set_variable(
|
||||
cstr16!("StubFeatures"),
|
||||
&BOOT_LOADER_VENDOR_UUID,
|
||||
default_attributes,
|
||||
&stub_features.bits().to_le_bytes()
|
||||
).ok();
|
||||
&stub_features.bits().to_le_bytes(),
|
||||
)
|
||||
.ok();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -4,14 +4,15 @@
|
|||
|
||||
extern crate alloc;
|
||||
|
||||
mod efivars;
|
||||
mod linux_loader;
|
||||
mod pe_loader;
|
||||
mod pe_section;
|
||||
mod uefi_helpers;
|
||||
mod efivars;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use log::{info, warn, debug};
|
||||
use efivars::{export_efi_variables, get_loader_features, EfiLoaderFeatures};
|
||||
use log::{debug, info, warn};
|
||||
use pe_loader::Image;
|
||||
use pe_section::{pe_section, pe_section_as_string};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
@ -23,7 +24,6 @@ use uefi::{
|
|||
},
|
||||
CStr16, CString16, Result,
|
||||
};
|
||||
use efivars::{EfiLoaderFeatures, export_efi_variables, get_loader_features};
|
||||
|
||||
use crate::{
|
||||
linux_loader::InitrdLoader,
|
||||
|
@ -244,8 +244,7 @@ fn main(handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
|
|||
debug!("Random seed is available, but lanzaboote does not support it yet.");
|
||||
}
|
||||
}
|
||||
export_efi_variables(&system_table)
|
||||
.expect("Failed to export stub EFI variables");
|
||||
export_efi_variables(&system_table).expect("Failed to export stub EFI variables");
|
||||
|
||||
if is_kernel_hash_correct && is_initrd_hash_correct {
|
||||
boot_linux_unchecked(
|
||||
|
|
|
@ -4,9 +4,9 @@ use std::os::unix::fs::MetadataExt;
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use bootspec::BootSpec;
|
||||
use bootspec::BootJson;
|
||||
use bootspec::generation::Generation as BootspecGeneration;
|
||||
use bootspec::BootJson;
|
||||
use bootspec::BootSpec;
|
||||
use bootspec::SpecialisationName;
|
||||
use time::Date;
|
||||
|
||||
|
@ -55,7 +55,7 @@ impl Generation {
|
|||
// TODO: replace me when https://github.com/DeterminateSystems/bootspec/pull/109 lands.
|
||||
let bootspec: BootSpec = match boot_json.generation {
|
||||
BootspecGeneration::V1(bootspec) => bootspec,
|
||||
_ => return Err(anyhow!("Unsupported bootspec schema"))
|
||||
_ => return Err(anyhow!("Unsupported bootspec schema")),
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
|
@ -104,7 +104,8 @@ impl fmt::Display for Generation {
|
|||
}
|
||||
|
||||
fn read_build_time(path: &Path) -> Result<Date> {
|
||||
let build_time = time::OffsetDateTime::from_unix_timestamp(fs::symlink_metadata(path)?.mtime())?.date();
|
||||
let build_time =
|
||||
time::OffsetDateTime::from_unix_timestamp(fs::symlink_metadata(path)?.mtime())?.date();
|
||||
Ok(build_time)
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,10 @@ impl OsRelease {
|
|||
// Because the ID field here does not have the same meaning as in a real os-release file,
|
||||
// it is fine to use a dummy value.
|
||||
map.insert("ID".into(), String::from("lanza"));
|
||||
map.insert("PRETTY_NAME".into(), generation.spec.bootspec.bootspec.label.clone());
|
||||
map.insert(
|
||||
"PRETTY_NAME".into(),
|
||||
generation.spec.bootspec.bootspec.label.clone(),
|
||||
);
|
||||
map.insert("VERSION_ID".into(), generation.describe());
|
||||
|
||||
Ok(Self(map))
|
||||
|
|
Loading…
Reference in New Issue