lanzatool: embed kernel and initrd hashes
This commit is contained in:
parent
ba119d398f
commit
3f78939d0a
|
@ -8,6 +8,18 @@ version = "1.0.66"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
|
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayref"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayvec"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atty"
|
name = "atty"
|
||||||
version = "0.2.14"
|
version = "0.2.14"
|
||||||
|
@ -31,6 +43,35 @@ version = "1.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blake3"
|
||||||
|
version = "1.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef"
|
||||||
|
dependencies = [
|
||||||
|
"arrayref",
|
||||||
|
"arrayvec",
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"constant_time_eq",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.77"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -74,6 +115,33 @@ dependencies = [
|
||||||
"os_str_bytes",
|
"os_str_bytes",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "constant_time_eq"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"crypto-common",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
|
@ -83,6 +151,16 @@ dependencies = [
|
||||||
"instant",
|
"instant",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "goblin"
|
name = "goblin"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
|
@ -129,6 +207,7 @@ name = "lanzatool"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"blake3",
|
||||||
"clap",
|
"clap",
|
||||||
"goblin",
|
"goblin",
|
||||||
"nix",
|
"nix",
|
||||||
|
@ -305,6 +384,12 @@ version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.103"
|
version = "1.0.103"
|
||||||
|
@ -339,6 +424,12 @@ dependencies = [
|
||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
|
|
|
@ -13,3 +13,4 @@ nix = { version = "0.25.0", default-features = false, features = [ "fs" ] }
|
||||||
serde = { version = "1.0.147", features = ["derive"] }
|
serde = { version = "1.0.147", features = ["derive"] }
|
||||||
serde_json = "1.0.89"
|
serde_json = "1.0.89"
|
||||||
tempfile = "3.3.0"
|
tempfile = "3.3.0"
|
||||||
|
blake3 = "1.3.3"
|
||||||
|
|
|
@ -87,17 +87,6 @@ impl Installer {
|
||||||
// TODO(Raito): prove to niksnur this is actually acceptable.
|
// TODO(Raito): prove to niksnur this is actually acceptable.
|
||||||
let secure_temp_dir = tempdir()?;
|
let secure_temp_dir = tempdir()?;
|
||||||
|
|
||||||
let lanzaboote_image = pe::lanzaboote_image(
|
|
||||||
&secure_temp_dir,
|
|
||||||
&self.lanzaboote_stub,
|
|
||||||
&bootspec.extension.os_release,
|
|
||||||
&kernel_cmdline,
|
|
||||||
&esp_paths.kernel,
|
|
||||||
&esp_paths.initrd,
|
|
||||||
&esp_paths.esp,
|
|
||||||
)
|
|
||||||
.context("Failed to assemble stub")?;
|
|
||||||
|
|
||||||
println!("Wrapping initrd into a PE binary...");
|
println!("Wrapping initrd into a PE binary...");
|
||||||
|
|
||||||
let initrd_location = secure_temp_dir.path().join("initrd");
|
let initrd_location = secure_temp_dir.path().join("initrd");
|
||||||
|
@ -114,24 +103,37 @@ impl Installer {
|
||||||
.toplevel
|
.toplevel
|
||||||
.join("systemd/lib/systemd/boot/efi/systemd-bootx64.efi");
|
.join("systemd/lib/systemd/boot/efi/systemd-bootx64.efi");
|
||||||
|
|
||||||
let files_to_copy_and_sign = [
|
[
|
||||||
(&systemd_boot, &esp_paths.efi_fallback),
|
(&systemd_boot, &esp_paths.efi_fallback),
|
||||||
(&systemd_boot, &esp_paths.systemd_boot),
|
(&systemd_boot, &esp_paths.systemd_boot),
|
||||||
(&lanzaboote_image, &esp_paths.lanzaboote_image),
|
|
||||||
(&bootspec.kernel, &esp_paths.kernel),
|
(&bootspec.kernel, &esp_paths.kernel),
|
||||||
(&wrapped_initrd, &esp_paths.initrd),
|
(&wrapped_initrd, &esp_paths.initrd),
|
||||||
];
|
]
|
||||||
|
.into_iter()
|
||||||
|
.try_for_each(|(from, to)| install_signed(&self.key_pair, from, to))?;
|
||||||
|
|
||||||
for (from, to) in files_to_copy_and_sign {
|
let lanzaboote_image = pe::lanzaboote_image(
|
||||||
println!("Signing {}...", to.display());
|
&secure_temp_dir,
|
||||||
|
&self.lanzaboote_stub,
|
||||||
|
&bootspec.extension.os_release,
|
||||||
|
&kernel_cmdline,
|
||||||
|
&esp_paths.kernel,
|
||||||
|
&esp_paths.initrd,
|
||||||
|
&esp_paths.esp,
|
||||||
|
)
|
||||||
|
.context("Failed to assemble stub")?;
|
||||||
|
|
||||||
ensure_parent_dir(to);
|
install_signed(
|
||||||
self.key_pair.sign_and_copy(from, to).with_context(|| {
|
&self.key_pair,
|
||||||
format!("Failed to copy and sign file from {:?} to {:?}", from, to)
|
&lanzaboote_image,
|
||||||
})?;
|
&esp_paths.lanzaboote_image,
|
||||||
// Call sync to improve the likelihood that file is actually written to disk
|
)
|
||||||
|
.context("Failed to install lanzaboote")?;
|
||||||
|
|
||||||
|
// Sync files to persistent storage. This may improve the
|
||||||
|
// chance of a consistent boot directory in case the system
|
||||||
|
// crashes.
|
||||||
sync();
|
sync();
|
||||||
}
|
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"Successfully installed lanzaboote to '{}'",
|
"Successfully installed lanzaboote to '{}'",
|
||||||
|
@ -142,6 +144,18 @@ impl Installer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Install a PE file. The PE gets signed in the process.
|
||||||
|
fn install_signed(key_pair: &KeyPair, from: &Path, to: &Path) -> Result<()> {
|
||||||
|
println!("Signing {}...", to.display());
|
||||||
|
|
||||||
|
ensure_parent_dir(to);
|
||||||
|
key_pair
|
||||||
|
.sign_and_copy(from, to)
|
||||||
|
.with_context(|| format!("Failed to copy and sign file from {:?} to {:?}", from, to))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn append_initrd_secrets(
|
pub fn append_initrd_secrets(
|
||||||
append_initrd_secrets_path: &Path,
|
append_initrd_secrets_path: &Path,
|
||||||
initrd_path: &PathBuf,
|
initrd_path: &PathBuf,
|
||||||
|
|
|
@ -12,6 +12,11 @@ use crate::utils;
|
||||||
|
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
|
/// Attach all information that lanzaboote needs into the PE binary.
|
||||||
|
///
|
||||||
|
/// When this function is called the referenced files already need to
|
||||||
|
/// be present in the ESP. This is required, because we need to read
|
||||||
|
/// them to compute hashes.
|
||||||
pub fn lanzaboote_image(
|
pub fn lanzaboote_image(
|
||||||
target_dir: &TempDir,
|
target_dir: &TempDir,
|
||||||
lanzaboote_stub: &Path,
|
lanzaboote_stub: &Path,
|
||||||
|
@ -21,36 +26,56 @@ pub fn lanzaboote_image(
|
||||||
initrd_path: &Path,
|
initrd_path: &Path,
|
||||||
esp: &Path,
|
esp: &Path,
|
||||||
) -> Result<PathBuf> {
|
) -> Result<PathBuf> {
|
||||||
// objcopy copies files into the PE binary. That's why we have to write the contents
|
// objcopy can only copy files into the PE binary. That's why we
|
||||||
// of some bootspec properties to disk
|
// have to write the contents of some bootspec properties to disk.
|
||||||
let (kernel_cmdline_file, _) =
|
let kernel_cmdline_file = write_to_tmp(target_dir, "kernel-cmdline", kernel_cmdline.join(" "))?;
|
||||||
write_to_tmp(target_dir, "kernel-cmdline", kernel_cmdline.join(" "))?;
|
|
||||||
let (kernel_path_file, _) = write_to_tmp(
|
let kernel_path_file = write_to_tmp(
|
||||||
target_dir,
|
target_dir,
|
||||||
"kernel-esp-path",
|
"kernel-esp-path",
|
||||||
esp_relative_path_string(esp, kernel_path),
|
esp_relative_path_string(esp, kernel_path),
|
||||||
)?;
|
)?;
|
||||||
let (initrd_path_file, _) = write_to_tmp(
|
let kernel_hash_file = write_to_tmp(
|
||||||
|
target_dir,
|
||||||
|
"kernel-hash",
|
||||||
|
file_hash(kernel_path)?.as_bytes(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let initrd_path_file = write_to_tmp(
|
||||||
target_dir,
|
target_dir,
|
||||||
"initrd-esp-path",
|
"initrd-esp-path",
|
||||||
esp_relative_path_string(esp, initrd_path),
|
esp_relative_path_string(esp, initrd_path),
|
||||||
)?;
|
)?;
|
||||||
|
let initrd_hash_file = write_to_tmp(
|
||||||
|
target_dir,
|
||||||
|
"initrd-hash",
|
||||||
|
file_hash(initrd_path)?.as_bytes(),
|
||||||
|
)?;
|
||||||
|
|
||||||
let os_release_offs = stub_offset(lanzaboote_stub)?;
|
let os_release_offs = stub_offset(lanzaboote_stub)?;
|
||||||
let kernel_cmdline_offs = os_release_offs + file_size(os_release)?;
|
let kernel_cmdline_offs = os_release_offs + file_size(os_release)?;
|
||||||
let initrd_path_offs = kernel_cmdline_offs + file_size(&kernel_cmdline_file)?;
|
let initrd_path_offs = kernel_cmdline_offs + file_size(&kernel_cmdline_file)?;
|
||||||
let kernel_path_offs = initrd_path_offs + file_size(&initrd_path_file)?;
|
let kernel_path_offs = initrd_path_offs + file_size(&initrd_path_file)?;
|
||||||
|
let initrd_hash_offs = kernel_path_offs + file_size(&kernel_path_file)?;
|
||||||
|
let kernel_hash_offs = initrd_hash_offs + file_size(&initrd_hash_file)?;
|
||||||
|
|
||||||
let sections = vec![
|
let sections = vec![
|
||||||
s(".osrel", os_release, os_release_offs),
|
s(".osrel", os_release, os_release_offs),
|
||||||
s(".cmdline", kernel_cmdline_file, kernel_cmdline_offs),
|
s(".cmdline", kernel_cmdline_file, kernel_cmdline_offs),
|
||||||
s(".initrdp", initrd_path_file, initrd_path_offs),
|
s(".initrdp", initrd_path_file, initrd_path_offs),
|
||||||
s(".kernelp", kernel_path_file, kernel_path_offs),
|
s(".kernelp", kernel_path_file, kernel_path_offs),
|
||||||
|
s(".initrdh", initrd_hash_file, initrd_hash_offs),
|
||||||
|
s(".kernelh", kernel_hash_file, kernel_hash_offs),
|
||||||
];
|
];
|
||||||
|
|
||||||
wrap_in_pe(target_dir, "lanzaboote-stub.efi", lanzaboote_stub, sections)
|
wrap_in_pe(target_dir, "lanzaboote-stub.efi", lanzaboote_stub, sections)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compute the blake3 hash of a file.
|
||||||
|
fn file_hash(file: &Path) -> Result<blake3::Hash> {
|
||||||
|
Ok(blake3::hash(&fs::read(file)?))
|
||||||
|
}
|
||||||
|
|
||||||
/// Wrap an initrd into a PE binary.
|
/// Wrap an initrd into a PE binary.
|
||||||
///
|
///
|
||||||
/// This is required for lanzaboote to verify the signature of the
|
/// This is required for lanzaboote to verify the signature of the
|
||||||
|
@ -125,21 +150,26 @@ fn s(name: &'static str, file_path: impl AsRef<Path>, offset: u64) -> Section {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write a `u8` slice to a temporary file.
|
||||||
fn write_to_tmp(
|
fn write_to_tmp(
|
||||||
secure_temp: &TempDir,
|
secure_temp: &TempDir,
|
||||||
filename: &str,
|
filename: &str,
|
||||||
contents: impl AsRef<[u8]>,
|
contents: impl AsRef<[u8]>,
|
||||||
) -> Result<(PathBuf, fs::File)> {
|
) -> Result<PathBuf> {
|
||||||
|
let path = secure_temp.path().join(filename);
|
||||||
|
|
||||||
let mut tmpfile = fs::OpenOptions::new()
|
let mut tmpfile = fs::OpenOptions::new()
|
||||||
.create(true)
|
.create(true)
|
||||||
.write(true)
|
.write(true)
|
||||||
.mode(0o600)
|
.mode(0o600)
|
||||||
.open(secure_temp.path().join(filename))
|
.open(&path)
|
||||||
.context("Failed to create tempfile")?;
|
.context("Failed to create tempfile")?;
|
||||||
|
|
||||||
tmpfile
|
tmpfile
|
||||||
.write_all(contents.as_ref())
|
.write_all(contents.as_ref())
|
||||||
.context("Failed to write to tempfile")?;
|
.context("Failed to write to tempfile")?;
|
||||||
Ok((secure_temp.path().join(filename), tmpfile))
|
|
||||||
|
Ok(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn esp_relative_path_string(esp: &Path, path: &Path) -> String {
|
fn esp_relative_path_string(esp: &Path, path: &Path) -> String {
|
||||||
|
|
Loading…
Reference in New Issue