Merge pull request #21 from nix-community/boot-file-integrity
Verify Kernel/Initrd Integrity using Blake3
This commit is contained in:
		
						commit
						06da27529f
					
				
							
								
								
									
										33
									
								
								flake.nix
								
								
								
								
							
							
						
						
									
										33
									
								
								flake.nix
								
								
								
								
							| 
						 | 
					@ -54,21 +54,12 @@
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      # This is basically an empty EFI application that we use as a
 | 
					 | 
				
			||||||
      # carrier for the initrd.
 | 
					 | 
				
			||||||
      initrdStubCrane = buildRustApp {
 | 
					 | 
				
			||||||
        src = ./rust/initrd-stub;
 | 
					 | 
				
			||||||
        target = "x86_64-unknown-uefi";
 | 
					 | 
				
			||||||
        doCheck = false;
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      lanzabooteCrane = buildRustApp {
 | 
					      lanzabooteCrane = buildRustApp {
 | 
				
			||||||
        src = ./rust/lanzaboote;
 | 
					        src = ./rust/lanzaboote;
 | 
				
			||||||
        target = "x86_64-unknown-uefi";
 | 
					        target = "x86_64-unknown-uefi";
 | 
				
			||||||
        doCheck = false;
 | 
					        doCheck = false;
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      initrd-stub = initrdStubCrane.package;
 | 
					 | 
				
			||||||
      lanzaboote = lanzabooteCrane.package;
 | 
					      lanzaboote = lanzabooteCrane.package;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      lanzatoolCrane = buildRustApp {
 | 
					      lanzatoolCrane = buildRustApp {
 | 
				
			||||||
| 
						 | 
					@ -87,8 +78,7 @@
 | 
				
			||||||
        makeWrapper ${lanzatool-unwrapped}/bin/lanzatool $out/bin/lanzatool \
 | 
					        makeWrapper ${lanzatool-unwrapped}/bin/lanzatool $out/bin/lanzatool \
 | 
				
			||||||
          --set PATH ${lib.makeBinPath [ pkgs.binutils-unwrapped pkgs.sbsigntool ]} \
 | 
					          --set PATH ${lib.makeBinPath [ pkgs.binutils-unwrapped pkgs.sbsigntool ]} \
 | 
				
			||||||
          --set RUST_BACKTRACE full \
 | 
					          --set RUST_BACKTRACE full \
 | 
				
			||||||
          --set LANZABOOTE_STUB ${lanzaboote}/bin/lanzaboote.efi \
 | 
					          --set LANZABOOTE_STUB ${lanzaboote}/bin/lanzaboote.efi
 | 
				
			||||||
          --set LANZABOOTE_INITRD_STUB ${initrd-stub}/bin/initrd-stub.efi \
 | 
					 | 
				
			||||||
      '';
 | 
					      '';
 | 
				
			||||||
    in {
 | 
					    in {
 | 
				
			||||||
      overlays.default = final: prev: {
 | 
					      overlays.default = final: prev: {
 | 
				
			||||||
| 
						 | 
					@ -98,7 +88,7 @@
 | 
				
			||||||
      nixosModules.lanzaboote = import ./nix/lanzaboote.nix;
 | 
					      nixosModules.lanzaboote = import ./nix/lanzaboote.nix;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      packages.x86_64-linux = {
 | 
					      packages.x86_64-linux = {
 | 
				
			||||||
        inherit initrd-stub lanzaboote lanzatool;
 | 
					        inherit lanzaboote lanzatool;
 | 
				
			||||||
        default = lanzatool;
 | 
					        default = lanzatool;
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -149,7 +139,10 @@
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
          };
 | 
					          };
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        mkUnsignedTest = { name, path }: mkSecureBootTest {
 | 
					
 | 
				
			||||||
 | 
					        # Execute a boot test that is intended to fail.
 | 
				
			||||||
 | 
					        #
 | 
				
			||||||
 | 
					        mkUnsignedTest = { name, path, appendCrap ? false }: mkSecureBootTest {
 | 
				
			||||||
          inherit name;
 | 
					          inherit name;
 | 
				
			||||||
          testScript = ''
 | 
					          testScript = ''
 | 
				
			||||||
            import json
 | 
					            import json
 | 
				
			||||||
| 
						 | 
					@ -166,10 +159,14 @@
 | 
				
			||||||
            src_path = ${path.src}
 | 
					            src_path = ${path.src}
 | 
				
			||||||
            dst_path = ${path.dst}
 | 
					            dst_path = ${path.dst}
 | 
				
			||||||
            machine.succeed(f"cp -rf {src_path} {dst_path}")
 | 
					            machine.succeed(f"cp -rf {src_path} {dst_path}")
 | 
				
			||||||
 | 
					          '' + lib.optionalString appendCrap ''
 | 
				
			||||||
 | 
					            machine.succeed(f"echo Foo >> {dst_path}")
 | 
				
			||||||
 | 
					          '' +
 | 
				
			||||||
 | 
					          ''
 | 
				
			||||||
            machine.succeed("sync")
 | 
					            machine.succeed("sync")
 | 
				
			||||||
            machine.crash()
 | 
					            machine.crash()
 | 
				
			||||||
            machine.start()
 | 
					            machine.start()
 | 
				
			||||||
            machine.wait_for_console_text("panicked")
 | 
					            machine.wait_for_console_text("Hash mismatch")
 | 
				
			||||||
          '';
 | 
					          '';
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
      in
 | 
					      in
 | 
				
			||||||
| 
						 | 
					@ -221,13 +218,21 @@
 | 
				
			||||||
            assert "Secure Boot: enabled (user)" in machine.succeed("bootctl status")
 | 
					            assert "Secure Boot: enabled (user)" in machine.succeed("bootctl status")
 | 
				
			||||||
          '';
 | 
					          '';
 | 
				
			||||||
          };
 | 
					          };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          # The initrd is not directly signed. Its hash is embedded
 | 
				
			||||||
 | 
					          # into lanzaboote. To make integrity verification fail, we
 | 
				
			||||||
 | 
					          # actually have to modify the initrd. Appending crap to the
 | 
				
			||||||
 | 
					          # end is a harmless way that would make the kernel still
 | 
				
			||||||
 | 
					          # accept it.
 | 
				
			||||||
          is-initrd-secured = mkUnsignedTest {
 | 
					          is-initrd-secured = mkUnsignedTest {
 | 
				
			||||||
            name = "unsigned-initrd-do-not-boot-under-secureboot";
 | 
					            name = "unsigned-initrd-do-not-boot-under-secureboot";
 | 
				
			||||||
            path = {
 | 
					            path = {
 | 
				
			||||||
              src = "bootspec.get('initrd')";
 | 
					              src = "bootspec.get('initrd')";
 | 
				
			||||||
              dst = "convert_to_esp(bootspec.get('initrd'))";
 | 
					              dst = "convert_to_esp(bootspec.get('initrd'))";
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					            appendCrap = true;
 | 
				
			||||||
          };
 | 
					          };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          is-kernel-secured = mkUnsignedTest {
 | 
					          is-kernel-secured = mkUnsignedTest {
 | 
				
			||||||
            name = "unsigned-kernel-do-not-boot-under-secureboot";
 | 
					            name = "unsigned-kernel-do-not-boot-under-secureboot";
 | 
				
			||||||
            path = {
 | 
					            path = {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,2 +0,0 @@
 | 
				
			||||||
[build]
 | 
					 | 
				
			||||||
target = "x86_64-unknown-uefi"
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,104 +0,0 @@
 | 
				
			||||||
# This file is automatically @generated by Cargo.
 | 
					 | 
				
			||||||
# It is not intended for manual editing.
 | 
					 | 
				
			||||||
version = 3
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "bit_field"
 | 
					 | 
				
			||||||
version = "0.10.1"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "bitflags"
 | 
					 | 
				
			||||||
version = "1.3.2"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "cfg-if"
 | 
					 | 
				
			||||||
version = "1.0.0"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "initrd-stub"
 | 
					 | 
				
			||||||
version = "0.1.0"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "uefi",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "log"
 | 
					 | 
				
			||||||
version = "0.4.17"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "cfg-if",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "proc-macro2"
 | 
					 | 
				
			||||||
version = "1.0.47"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "unicode-ident",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "quote"
 | 
					 | 
				
			||||||
version = "1.0.21"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "proc-macro2",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "syn"
 | 
					 | 
				
			||||||
version = "1.0.103"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "proc-macro2",
 | 
					 | 
				
			||||||
 "quote",
 | 
					 | 
				
			||||||
 "unicode-ident",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "ucs2"
 | 
					 | 
				
			||||||
version = "0.3.2"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "bad643914094137d475641b6bab89462505316ec2ce70907ad20102d28a79ab8"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "bit_field",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "uefi"
 | 
					 | 
				
			||||||
version = "0.18.0"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "07b87700863d65dd4841556be3374d8d4f9f8dbb577ad93a39859e70b3b91f35"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "bitflags",
 | 
					 | 
				
			||||||
 "log",
 | 
					 | 
				
			||||||
 "ucs2",
 | 
					 | 
				
			||||||
 "uefi-macros",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "uefi-macros"
 | 
					 | 
				
			||||||
version = "0.9.0"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "275f054a1d9fd7e43a2ce91cc24298a87b281117dea8afc120ae95faa0e96b94"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "proc-macro2",
 | 
					 | 
				
			||||||
 "quote",
 | 
					 | 
				
			||||||
 "syn",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "unicode-ident"
 | 
					 | 
				
			||||||
version = "1.0.5"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,12 +0,0 @@
 | 
				
			||||||
[package]
 | 
					 | 
				
			||||||
name = "initrd-stub"
 | 
					 | 
				
			||||||
version = "0.1.0"
 | 
					 | 
				
			||||||
edition = "2021"
 | 
					 | 
				
			||||||
publish = false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[dependencies]
 | 
					 | 
				
			||||||
uefi = { version = "0.18.0", default-features = false, features = [ ] }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[profile.release]
 | 
					 | 
				
			||||||
opt-level = "s"
 | 
					 | 
				
			||||||
lto = true
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,19 +0,0 @@
 | 
				
			||||||
#![no_main]
 | 
					 | 
				
			||||||
#![no_std]
 | 
					 | 
				
			||||||
#![feature(abi_efiapi)]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use core::panic::PanicInfo;
 | 
					 | 
				
			||||||
use uefi::{
 | 
					 | 
				
			||||||
    prelude::{entry, Boot, SystemTable},
 | 
					 | 
				
			||||||
    Handle, Status,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[panic_handler]
 | 
					 | 
				
			||||||
fn panic(_info: &PanicInfo) -> ! {
 | 
					 | 
				
			||||||
    loop {}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[entry]
 | 
					 | 
				
			||||||
fn main(_handle: Handle, mut _system_table: SystemTable<Boot>) -> Status {
 | 
					 | 
				
			||||||
    Status::UNSUPPORTED
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,18 @@
 | 
				
			||||||
# It is not intended for manual editing.
 | 
					# It is not intended for manual editing.
 | 
				
			||||||
version = 3
 | 
					version = 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[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 = "bit_field"
 | 
					name = "bit_field"
 | 
				
			||||||
version = "0.10.1"
 | 
					version = "0.10.1"
 | 
				
			||||||
| 
						 | 
					@ -14,12 +26,37 @@ 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",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[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"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 | 
					checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "constant_time_eq"
 | 
				
			||||||
 | 
					version = "0.2.4"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "ed25519-compact"
 | 
					name = "ed25519-compact"
 | 
				
			||||||
version = "2.0.2"
 | 
					version = "2.0.2"
 | 
				
			||||||
| 
						 | 
					@ -41,6 +78,7 @@ dependencies = [
 | 
				
			||||||
name = "lanzaboote"
 | 
					name = "lanzaboote"
 | 
				
			||||||
version = "0.1.0"
 | 
					version = "0.1.0"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "blake3",
 | 
				
			||||||
 "ed25519-compact",
 | 
					 "ed25519-compact",
 | 
				
			||||||
 "goblin",
 | 
					 "goblin",
 | 
				
			||||||
 "log",
 | 
					 "log",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,6 +11,9 @@ log = "0.4.17"
 | 
				
			||||||
ed25519-compact = { version = "2.0.2", default-features = false, features = [] }
 | 
					ed25519-compact = { version = "2.0.2", default-features = false, features = [] }
 | 
				
			||||||
goblin = { version = "0.6.0", default-features = false, features = [ "pe64", "alloc" ]}
 | 
					goblin = { version = "0.6.0", default-features = false, features = [ "pe64", "alloc" ]}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# We don't want the assembly implementations for now.
 | 
				
			||||||
 | 
					blake3 = { version = "1.3.3", default-features = false, features = [ "pure" ]}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[profile.release]
 | 
					[profile.release]
 | 
				
			||||||
opt-level = "s"
 | 
					opt-level = "s"
 | 
				
			||||||
lto = true
 | 
					lto = true
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,22 +5,18 @@
 | 
				
			||||||
//! because we read the initrd multiple times. The code needs to be
 | 
					//! because we read the initrd multiple times. The code needs to be
 | 
				
			||||||
//! restructured to solve this.
 | 
					//! restructured to solve this.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use core::{ffi::c_void, ops::Range, pin::Pin, ptr::slice_from_raw_parts_mut};
 | 
					use core::{ffi::c_void, pin::Pin, ptr::slice_from_raw_parts_mut};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use alloc::boxed::Box;
 | 
					use alloc::{boxed::Box, vec::Vec};
 | 
				
			||||||
use uefi::{
 | 
					use uefi::{
 | 
				
			||||||
    prelude::BootServices,
 | 
					    prelude::BootServices,
 | 
				
			||||||
    proto::{
 | 
					    proto::{
 | 
				
			||||||
        device_path::{DevicePath, FfiDevicePath},
 | 
					        device_path::{DevicePath, FfiDevicePath},
 | 
				
			||||||
        media::file::RegularFile,
 | 
					 | 
				
			||||||
        Protocol,
 | 
					        Protocol,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    table::boot::LoadImageSource,
 | 
					 | 
				
			||||||
    unsafe_guid, Handle, Identify, Result, ResultExt, Status,
 | 
					    unsafe_guid, Handle, Identify, Result, ResultExt, Status,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::uefi_helpers::read_all;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// The Linux kernel's initrd loading device path.
 | 
					/// The Linux kernel's initrd loading device path.
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// The Linux kernel points us to
 | 
					/// The Linux kernel points us to
 | 
				
			||||||
| 
						 | 
					@ -65,23 +61,10 @@ struct LoadFile2Protocol {
 | 
				
			||||||
    ) -> Status,
 | 
					    ) -> Status,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // This is not part of the official protocol struct.
 | 
					    // This is not part of the official protocol struct.
 | 
				
			||||||
    file: RegularFile,
 | 
					    initrd_data: Vec<u8>,
 | 
				
			||||||
    range: Range<usize>,
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl LoadFile2Protocol {
 | 
					impl LoadFile2Protocol {
 | 
				
			||||||
    fn initrd_start(&self) -> usize {
 | 
					 | 
				
			||||||
        self.range.start
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn initrd_size(&self) -> usize {
 | 
					 | 
				
			||||||
        if self.range.is_empty() {
 | 
					 | 
				
			||||||
            0
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            self.range.end - self.range.start
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn load_file(
 | 
					    fn load_file(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        _file_path: *const FfiDevicePath,
 | 
					        _file_path: *const FfiDevicePath,
 | 
				
			||||||
| 
						 | 
					@ -89,24 +72,21 @@ impl LoadFile2Protocol {
 | 
				
			||||||
        buffer_size: *mut usize,
 | 
					        buffer_size: *mut usize,
 | 
				
			||||||
        buffer: *mut c_void,
 | 
					        buffer: *mut c_void,
 | 
				
			||||||
    ) -> Result<()> {
 | 
					    ) -> Result<()> {
 | 
				
			||||||
        if buffer.is_null() || unsafe { *buffer_size } < self.initrd_size() {
 | 
					        if buffer.is_null() || unsafe { *buffer_size } < self.initrd_data.len() {
 | 
				
			||||||
            unsafe {
 | 
					            unsafe {
 | 
				
			||||||
                *buffer_size = self.initrd_size();
 | 
					                *buffer_size = self.initrd_data.len();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            return Err(Status::BUFFER_TOO_SMALL.into());
 | 
					            return Err(Status::BUFFER_TOO_SMALL.into());
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.file
 | 
					 | 
				
			||||||
            .set_position(self.initrd_start().try_into().unwrap())?;
 | 
					 | 
				
			||||||
        unsafe {
 | 
					        unsafe {
 | 
				
			||||||
            *buffer_size = self.initrd_size();
 | 
					            *buffer_size = self.initrd_data.len();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let output_slice: &mut [u8] =
 | 
					        let output_slice: &mut [u8] =
 | 
				
			||||||
            unsafe { &mut *slice_from_raw_parts_mut(buffer as *mut u8, *buffer_size) };
 | 
					            unsafe { &mut *slice_from_raw_parts_mut(buffer as *mut u8, *buffer_size) };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let read_bytes = self.file.read(output_slice).map_err(|e| e.status())?;
 | 
					        output_slice.copy_from_slice(&self.initrd_data);
 | 
				
			||||||
        assert_eq!(read_bytes, unsafe { *buffer_size });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -134,73 +114,15 @@ pub struct InitrdLoader {
 | 
				
			||||||
    registered: bool,
 | 
					    registered: bool,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Returns the data range of the initrd in the PE binary.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// The initrd has to be embedded in the file as a .initrd PE section.
 | 
					 | 
				
			||||||
fn initrd_location(initrd_efi: &mut RegularFile) -> Result<Range<usize>> {
 | 
					 | 
				
			||||||
    initrd_efi.set_position(0)?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let file_data = read_all(initrd_efi)?;
 | 
					 | 
				
			||||||
    let pe_binary = goblin::pe::PE::parse(&file_data).map_err(|_| Status::INVALID_PARAMETER)?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pe_binary
 | 
					 | 
				
			||||||
        .sections
 | 
					 | 
				
			||||||
        .iter()
 | 
					 | 
				
			||||||
        .find(|s| s.name().unwrap() == ".initrd")
 | 
					 | 
				
			||||||
        .map(|s| {
 | 
					 | 
				
			||||||
            let section_start: usize = s.pointer_to_raw_data.try_into().unwrap();
 | 
					 | 
				
			||||||
            let section_size: usize = s.size_of_raw_data.try_into().unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Range {
 | 
					 | 
				
			||||||
                start: section_start,
 | 
					 | 
				
			||||||
                end: section_start + section_size,
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        .ok_or_else(|| Status::END_OF_FILE.into())
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Check the signature of the initrd.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// For this to work, the initrd needs to be a PE binary. We misuse
 | 
					 | 
				
			||||||
/// [`BootServices::load_image`] for this.
 | 
					 | 
				
			||||||
fn initrd_verify(boot_services: &BootServices, initrd_efi: &mut RegularFile) -> Result<()> {
 | 
					 | 
				
			||||||
    initrd_efi.set_position(0)?;
 | 
					 | 
				
			||||||
    let file_data = read_all(initrd_efi)?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let initrd_handle = boot_services.load_image(
 | 
					 | 
				
			||||||
        boot_services.image_handle(),
 | 
					 | 
				
			||||||
        LoadImageSource::FromBuffer {
 | 
					 | 
				
			||||||
            buffer: &file_data,
 | 
					 | 
				
			||||||
            file_path: None,
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    )?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // If we get here, the security policy allowed loading the
 | 
					 | 
				
			||||||
    // image. This means that it was signed with an acceptable key in
 | 
					 | 
				
			||||||
    // the Secure Boot scenario.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    boot_services.unload_image(initrd_handle)?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Ok(())
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl InitrdLoader {
 | 
					impl InitrdLoader {
 | 
				
			||||||
    /// Create a new [`InitrdLoader`].
 | 
					    /// Create a new [`InitrdLoader`].
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// `handle` is the handle where the protocols are registered
 | 
					    /// `handle` is the handle where the protocols are registered
 | 
				
			||||||
    /// on. `file` is the file that is served to Linux.
 | 
					    /// on. `file` is the file that is served to Linux.
 | 
				
			||||||
    pub fn new(
 | 
					    pub fn new(boot_services: &BootServices, handle: Handle, initrd_data: Vec<u8>) -> Result<Self> {
 | 
				
			||||||
        boot_services: &BootServices,
 | 
					 | 
				
			||||||
        handle: Handle,
 | 
					 | 
				
			||||||
        mut file: RegularFile,
 | 
					 | 
				
			||||||
    ) -> Result<Self> {
 | 
					 | 
				
			||||||
        initrd_verify(boot_services, &mut file)?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let range = initrd_location(&mut file)?;
 | 
					 | 
				
			||||||
        let mut proto = Box::pin(LoadFile2Protocol {
 | 
					        let mut proto = Box::pin(LoadFile2Protocol {
 | 
				
			||||||
            load_file: raw_load_file,
 | 
					            load_file: raw_load_file,
 | 
				
			||||||
            file,
 | 
					            initrd_data,
 | 
				
			||||||
            range,
 | 
					 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Linux finds the right handle by looking for something that
 | 
					        // Linux finds the right handle by looking for something that
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,8 @@ mod linux_loader;
 | 
				
			||||||
mod pe_section;
 | 
					mod pe_section;
 | 
				
			||||||
mod uefi_helpers;
 | 
					mod uefi_helpers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use pe_section::pe_section_as_string;
 | 
					use blake3::Hash;
 | 
				
			||||||
 | 
					use pe_section::{pe_section, pe_section_as_string};
 | 
				
			||||||
use uefi::{
 | 
					use uefi::{
 | 
				
			||||||
    prelude::*,
 | 
					    prelude::*,
 | 
				
			||||||
    proto::{
 | 
					    proto::{
 | 
				
			||||||
| 
						 | 
					@ -52,9 +53,33 @@ struct EmbeddedConfiguration {
 | 
				
			||||||
    /// lanzaboote binary.
 | 
					    /// lanzaboote binary.
 | 
				
			||||||
    kernel_filename: CString16,
 | 
					    kernel_filename: CString16,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// The cryptographic hash of the kernel.
 | 
				
			||||||
 | 
					    kernel_hash: Hash,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// The filename of the initrd to be passed to the kernel. See
 | 
					    /// The filename of the initrd to be passed to the kernel. See
 | 
				
			||||||
    /// `kernel_filename` for how to interpret these filenames.
 | 
					    /// `kernel_filename` for how to interpret these filenames.
 | 
				
			||||||
    initrd_filename: CString16,
 | 
					    initrd_filename: CString16,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// The cryptographic hash of the initrd. This hash is computed
 | 
				
			||||||
 | 
					    /// over the whole PE binary, not only the embedded initrd.
 | 
				
			||||||
 | 
					    initrd_hash: Hash,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Extract a filename from a PE section. The filename is stored as UTF-8.
 | 
				
			||||||
 | 
					fn extract_filename(file_data: &[u8], section: &str) -> Result<CString16> {
 | 
				
			||||||
 | 
					    let filename = pe_section_as_string(file_data, section).ok_or(Status::INVALID_PARAMETER)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(CString16::try_from(filename.as_str()).map_err(|_| Status::INVALID_PARAMETER)?)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Extract a Blake3 hash from a PE section.
 | 
				
			||||||
 | 
					fn extract_hash(file_data: &[u8], section: &str) -> Result<Hash> {
 | 
				
			||||||
 | 
					    let array: [u8; 32] = pe_section(file_data, section)
 | 
				
			||||||
 | 
					        .ok_or(Status::INVALID_PARAMETER)?
 | 
				
			||||||
 | 
					        .try_into()
 | 
				
			||||||
 | 
					        .map_err(|_| Status::INVALID_PARAMETER)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(array.into())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl EmbeddedConfiguration {
 | 
					impl EmbeddedConfiguration {
 | 
				
			||||||
| 
						 | 
					@ -62,14 +87,12 @@ impl EmbeddedConfiguration {
 | 
				
			||||||
        file.set_position(0)?;
 | 
					        file.set_position(0)?;
 | 
				
			||||||
        let file_data = read_all(file)?;
 | 
					        let file_data = read_all(file)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let kernel_filename =
 | 
					 | 
				
			||||||
            pe_section_as_string(&file_data, ".kernelp").ok_or(Status::INVALID_PARAMETER)?;
 | 
					 | 
				
			||||||
        let initrd_filename =
 | 
					 | 
				
			||||||
            pe_section_as_string(&file_data, ".initrdp").ok_or(Status::INVALID_PARAMETER)?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Ok(Self {
 | 
					        Ok(Self {
 | 
				
			||||||
            kernel_filename: CString16::try_from(kernel_filename.as_str()).unwrap(),
 | 
					            kernel_filename: extract_filename(&file_data, ".kernelp")?,
 | 
				
			||||||
            initrd_filename: CString16::try_from(initrd_filename.as_str()).unwrap(),
 | 
					            kernel_hash: extract_hash(&file_data, ".kernelh")?,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            initrd_filename: extract_filename(&file_data, ".initrdp")?,
 | 
				
			||||||
 | 
					            initrd_hash: extract_hash(&file_data, ".initrdh")?,
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -84,8 +107,10 @@ fn main(handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
 | 
				
			||||||
        EmbeddedConfiguration::new(&mut booted_image_file(system_table.boot_services()).unwrap())
 | 
					        EmbeddedConfiguration::new(&mut booted_image_file(system_table.boot_services()).unwrap())
 | 
				
			||||||
            .expect("Failed to extract configuration from binary. Did you run lanzatool?");
 | 
					            .expect("Failed to extract configuration from binary. Did you run lanzatool?");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut kernel_file;
 | 
					    let kernel_data;
 | 
				
			||||||
    let initrd = {
 | 
					    let initrd_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
        let mut file_system = system_table
 | 
					        let mut file_system = system_table
 | 
				
			||||||
            .boot_services()
 | 
					            .boot_services()
 | 
				
			||||||
            .get_image_file_system(handle)
 | 
					            .get_image_file_system(handle)
 | 
				
			||||||
| 
						 | 
					@ -94,7 +119,7 @@ fn main(handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
 | 
				
			||||||
            .open_volume()
 | 
					            .open_volume()
 | 
				
			||||||
            .expect("Failed to find ESP root directory");
 | 
					            .expect("Failed to find ESP root directory");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        kernel_file = root
 | 
					        let mut kernel_file = root
 | 
				
			||||||
            .open(
 | 
					            .open(
 | 
				
			||||||
                &config.kernel_filename,
 | 
					                &config.kernel_filename,
 | 
				
			||||||
                FileMode::Read,
 | 
					                FileMode::Read,
 | 
				
			||||||
| 
						 | 
					@ -104,23 +129,41 @@ fn main(handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
 | 
				
			||||||
            .into_regular_file()
 | 
					            .into_regular_file()
 | 
				
			||||||
            .expect("Kernel is not a regular file");
 | 
					            .expect("Kernel is not a regular file");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        root.open(
 | 
					        kernel_data = read_all(&mut kernel_file).expect("Failed to read kernel file into memory");
 | 
				
			||||||
            &config.initrd_filename,
 | 
					
 | 
				
			||||||
            FileMode::Read,
 | 
					        let mut initrd_file = root
 | 
				
			||||||
            FileAttribute::empty(),
 | 
					            .open(
 | 
				
			||||||
        )
 | 
					                &config.initrd_filename,
 | 
				
			||||||
        .expect("Failed to open initrd for reading")
 | 
					                FileMode::Read,
 | 
				
			||||||
        .into_regular_file()
 | 
					                FileAttribute::empty(),
 | 
				
			||||||
        .expect("Initrd is not a regular file")
 | 
					            )
 | 
				
			||||||
    };
 | 
					            .expect("Failed to open initrd for reading")
 | 
				
			||||||
 | 
					            .into_regular_file()
 | 
				
			||||||
 | 
					            .expect("Initrd is not a regular file");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        initrd_data = read_all(&mut initrd_file).expect("Failed to read kernel file into memory");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if blake3::hash(&kernel_data) != config.kernel_hash {
 | 
				
			||||||
 | 
					        system_table
 | 
				
			||||||
 | 
					            .stdout()
 | 
				
			||||||
 | 
					            .output_string(cstr16!("Hash mismatch for kernel. Refusing to load!\r\n"))
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
 | 
					        return Status::SECURITY_VIOLATION;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if blake3::hash(&initrd_data) != config.initrd_hash {
 | 
				
			||||||
 | 
					        system_table
 | 
				
			||||||
 | 
					            .stdout()
 | 
				
			||||||
 | 
					            .output_string(cstr16!("Hash mismatch for initrd. Refusing to load!\r\n"))
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
 | 
					        return Status::SECURITY_VIOLATION;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let kernel_cmdline =
 | 
					    let kernel_cmdline =
 | 
				
			||||||
        booted_image_cmdline(system_table.boot_services()).expect("Failed to fetch command line");
 | 
					        booted_image_cmdline(system_table.boot_services()).expect("Failed to fetch command line");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let kernel_handle = {
 | 
					    let kernel_handle = {
 | 
				
			||||||
        let kernel_data =
 | 
					 | 
				
			||||||
            read_all(&mut kernel_file).expect("Failed to read kernel file into memory");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        system_table
 | 
					        system_table
 | 
				
			||||||
            .boot_services()
 | 
					            .boot_services()
 | 
				
			||||||
            .load_image(
 | 
					            .load_image(
 | 
				
			||||||
| 
						 | 
					@ -145,7 +188,7 @@ fn main(handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut initrd_loader = InitrdLoader::new(system_table.boot_services(), handle, initrd)
 | 
					    let mut initrd_loader = InitrdLoader::new(system_table.boot_services(), handle, initrd_data)
 | 
				
			||||||
        .expect("Failed to load the initrd. It may not be there or it is not signed");
 | 
					        .expect("Failed to load the initrd. It may not be there or it is not signed");
 | 
				
			||||||
    let status = system_table
 | 
					    let status = system_table
 | 
				
			||||||
        .boot_services()
 | 
					        .boot_services()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -59,14 +59,11 @@ impl Commands {
 | 
				
			||||||
fn install(args: InstallCommand) -> Result<()> {
 | 
					fn install(args: InstallCommand) -> Result<()> {
 | 
				
			||||||
    let lanzaboote_stub =
 | 
					    let lanzaboote_stub =
 | 
				
			||||||
        std::env::var("LANZABOOTE_STUB").context("Failed to read LANZABOOTE_STUB env variable")?;
 | 
					        std::env::var("LANZABOOTE_STUB").context("Failed to read LANZABOOTE_STUB env variable")?;
 | 
				
			||||||
    let initrd_stub = std::env::var("LANZABOOTE_INITRD_STUB")
 | 
					 | 
				
			||||||
        .context("Failed to read LANZABOOTE_INITRD_STUB env variable")?;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let key_pair = KeyPair::new(&args.public_key, &args.private_key);
 | 
					    let key_pair = KeyPair::new(&args.public_key, &args.private_key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    install::Installer::new(
 | 
					    install::Installer::new(
 | 
				
			||||||
        PathBuf::from(lanzaboote_stub),
 | 
					        PathBuf::from(lanzaboote_stub),
 | 
				
			||||||
        PathBuf::from(initrd_stub),
 | 
					 | 
				
			||||||
        key_pair,
 | 
					        key_pair,
 | 
				
			||||||
        args.pki_bundle,
 | 
					        args.pki_bundle,
 | 
				
			||||||
        args.auto_enroll,
 | 
					        args.auto_enroll,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,6 @@ use crate::signature::KeyPair;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct Installer {
 | 
					pub struct Installer {
 | 
				
			||||||
    lanzaboote_stub: PathBuf,
 | 
					    lanzaboote_stub: PathBuf,
 | 
				
			||||||
    initrd_stub: PathBuf,
 | 
					 | 
				
			||||||
    key_pair: KeyPair,
 | 
					    key_pair: KeyPair,
 | 
				
			||||||
    _pki_bundle: Option<PathBuf>,
 | 
					    _pki_bundle: Option<PathBuf>,
 | 
				
			||||||
    _auto_enroll: bool,
 | 
					    _auto_enroll: bool,
 | 
				
			||||||
| 
						 | 
					@ -25,7 +24,6 @@ pub struct Installer {
 | 
				
			||||||
impl Installer {
 | 
					impl Installer {
 | 
				
			||||||
    pub fn new(
 | 
					    pub fn new(
 | 
				
			||||||
        lanzaboote_stub: PathBuf,
 | 
					        lanzaboote_stub: PathBuf,
 | 
				
			||||||
        initrd_stub: PathBuf,
 | 
					 | 
				
			||||||
        key_pair: KeyPair,
 | 
					        key_pair: KeyPair,
 | 
				
			||||||
        _pki_bundle: Option<PathBuf>,
 | 
					        _pki_bundle: Option<PathBuf>,
 | 
				
			||||||
        _auto_enroll: bool,
 | 
					        _auto_enroll: bool,
 | 
				
			||||||
| 
						 | 
					@ -34,7 +32,6 @@ impl Installer {
 | 
				
			||||||
    ) -> Self {
 | 
					    ) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            lanzaboote_stub,
 | 
					            lanzaboote_stub,
 | 
				
			||||||
            initrd_stub,
 | 
					 | 
				
			||||||
            key_pair,
 | 
					            key_pair,
 | 
				
			||||||
            _pki_bundle,
 | 
					            _pki_bundle,
 | 
				
			||||||
            _auto_enroll,
 | 
					            _auto_enroll,
 | 
				
			||||||
| 
						 | 
					@ -68,14 +65,10 @@ impl Installer {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn install_generation(&self, generation: &Generation) -> Result<()> {
 | 
					    fn install_generation(&self, generation: &Generation) -> Result<()> {
 | 
				
			||||||
        println!("Reading bootspec...");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let bootspec = &generation.bootspec;
 | 
					        let bootspec = &generation.bootspec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let esp_paths = EspPaths::new(&self.esp, generation)?;
 | 
					        let esp_paths = EspPaths::new(&self.esp, generation)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        println!("Assembling lanzaboote image...");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let kernel_cmdline =
 | 
					        let kernel_cmdline =
 | 
				
			||||||
            assemble_kernel_cmdline(&bootspec.init, bootspec.kernel_params.clone());
 | 
					            assemble_kernel_cmdline(&bootspec.init, bootspec.kernel_params.clone());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -87,6 +80,31 @@ 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()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        println!("Appending secrets to initrd...");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let initrd_location = secure_temp_dir.path().join("initrd");
 | 
				
			||||||
 | 
					        copy(&bootspec.initrd, &initrd_location)?;
 | 
				
			||||||
 | 
					        if let Some(initrd_secrets_script) = &bootspec.initrd_secrets {
 | 
				
			||||||
 | 
					            append_initrd_secrets(initrd_secrets_script, &initrd_location)?;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let systemd_boot = bootspec
 | 
				
			||||||
 | 
					            .toplevel
 | 
				
			||||||
 | 
					            .join("systemd/lib/systemd/boot/efi/systemd-bootx64.efi");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [
 | 
				
			||||||
 | 
					            (&systemd_boot, &esp_paths.efi_fallback),
 | 
				
			||||||
 | 
					            (&systemd_boot, &esp_paths.systemd_boot),
 | 
				
			||||||
 | 
					            (&bootspec.kernel, &esp_paths.kernel),
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        .into_iter()
 | 
				
			||||||
 | 
					        .try_for_each(|(from, to)| install_signed(&self.key_pair, from, to))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // The initrd doesn't need to be signed. Lanzaboote has its
 | 
				
			||||||
 | 
					        // hash embedded and will refuse loading it when the hash
 | 
				
			||||||
 | 
					        // mismatches.
 | 
				
			||||||
 | 
					        install(&initrd_location, &esp_paths.initrd).context("Failed to install initrd to ESP")?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let lanzaboote_image = pe::lanzaboote_image(
 | 
					        let lanzaboote_image = pe::lanzaboote_image(
 | 
				
			||||||
            &secure_temp_dir,
 | 
					            &secure_temp_dir,
 | 
				
			||||||
            &self.lanzaboote_stub,
 | 
					            &self.lanzaboote_stub,
 | 
				
			||||||
| 
						 | 
					@ -98,40 +116,17 @@ impl Installer {
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        .context("Failed to assemble stub")?;
 | 
					        .context("Failed to assemble stub")?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        println!("Wrapping initrd into a PE binary...");
 | 
					        install_signed(
 | 
				
			||||||
 | 
					            &self.key_pair,
 | 
				
			||||||
 | 
					            &lanzaboote_image,
 | 
				
			||||||
 | 
					            &esp_paths.lanzaboote_image,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .context("Failed to install lanzaboote")?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let initrd_location = secure_temp_dir.path().join("initrd");
 | 
					        // Sync files to persistent storage. This may improve the
 | 
				
			||||||
        copy(&bootspec.initrd, &initrd_location)?;
 | 
					        // chance of a consistent boot directory in case the system
 | 
				
			||||||
        if let Some(initrd_secrets_script) = &bootspec.initrd_secrets {
 | 
					        // crashes.
 | 
				
			||||||
            append_initrd_secrets(initrd_secrets_script, &initrd_location)?;
 | 
					        sync();
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        let wrapped_initrd = pe::wrap_initrd(&secure_temp_dir, &self.initrd_stub, &initrd_location)
 | 
					 | 
				
			||||||
            .context("Failed to assemble stub")?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        println!("Sign and copy files to EFI system partition...");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let systemd_boot = bootspec
 | 
					 | 
				
			||||||
            .toplevel
 | 
					 | 
				
			||||||
            .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.systemd_boot),
 | 
					 | 
				
			||||||
            (&lanzaboote_image, &esp_paths.lanzaboote_image),
 | 
					 | 
				
			||||||
            (&bootspec.kernel, &esp_paths.kernel),
 | 
					 | 
				
			||||||
            (&wrapped_initrd, &esp_paths.initrd),
 | 
					 | 
				
			||||||
        ];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (from, to) in files_to_copy_and_sign {
 | 
					 | 
				
			||||||
            println!("Signing {}...", to.display());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            ensure_parent_dir(to);
 | 
					 | 
				
			||||||
            self.key_pair.sign_and_copy(from, to).with_context(|| {
 | 
					 | 
				
			||||||
                format!("Failed to copy and sign file from {:?} to {:?}", from, to)
 | 
					 | 
				
			||||||
            })?;
 | 
					 | 
				
			||||||
            // Call sync to improve the likelihood that file is actually written to disk
 | 
					 | 
				
			||||||
            sync();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        println!(
 | 
					        println!(
 | 
				
			||||||
            "Successfully installed lanzaboote to '{}'",
 | 
					            "Successfully installed lanzaboote to '{}'",
 | 
				
			||||||
| 
						 | 
					@ -142,6 +137,38 @@ impl Installer {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Install a PE file. The PE gets signed in the process.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// The file is only signed and copied if it doesn't exist at the destination
 | 
				
			||||||
 | 
					fn install_signed(key_pair: &KeyPair, from: &Path, to: &Path) -> Result<()> {
 | 
				
			||||||
 | 
					    if to.exists() {
 | 
				
			||||||
 | 
					        println!("{} already exists, skipping...", to.display());
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        println!("Signing and installing {}...", 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(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Install an arbitrary file
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// The file is only copied if it doesn't exist at the destination
 | 
				
			||||||
 | 
					fn install(from: &Path, to: &Path) -> Result<()> {
 | 
				
			||||||
 | 
					    if to.exists() {
 | 
				
			||||||
 | 
					        println!("{} already exists, skipping...", to.display());
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        println!("Installing {}...", to.display());
 | 
				
			||||||
 | 
					        ensure_parent_dir(to);
 | 
				
			||||||
 | 
					        copy(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,49 +26,67 @@ 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)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn wrap_initrd(target_dir: &TempDir, initrd_stub: &Path, initrd: &Path) -> Result<PathBuf> {
 | 
					/// Compute the blake3 hash of a file.
 | 
				
			||||||
    let initrd_offs = stub_offset(initrd_stub)?;
 | 
					fn file_hash(file: &Path) -> Result<blake3::Hash> {
 | 
				
			||||||
    let sections = vec![s(".initrd", initrd, initrd_offs)];
 | 
					    Ok(blake3::hash(&fs::read(file)?))
 | 
				
			||||||
    wrap_in_pe(target_dir, "wrapped-initrd.exe", initrd_stub, sections)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Take a PE binary stub and attach sections to it.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// The result is then written to a new file. Returns the filename of
 | 
				
			||||||
 | 
					/// the generated file.
 | 
				
			||||||
fn wrap_in_pe(
 | 
					fn wrap_in_pe(
 | 
				
			||||||
    target_dir: &TempDir,
 | 
					    target_dir: &TempDir,
 | 
				
			||||||
    filename: &str,
 | 
					    output_filename: &str,
 | 
				
			||||||
    stub: &Path,
 | 
					    stub: &Path,
 | 
				
			||||||
    sections: Vec<Section>,
 | 
					    sections: Vec<Section>,
 | 
				
			||||||
) -> Result<PathBuf> {
 | 
					) -> Result<PathBuf> {
 | 
				
			||||||
    let image_path = target_dir.path().join(filename);
 | 
					    let image_path = target_dir.path().join(output_filename);
 | 
				
			||||||
    let _ = fs::OpenOptions::new()
 | 
					    let _ = fs::OpenOptions::new()
 | 
				
			||||||
        .create(true)
 | 
					        .create(true)
 | 
				
			||||||
        .write(true)
 | 
					        .write(true)
 | 
				
			||||||
| 
						 | 
					@ -117,21 +140,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