diff --git a/lucet-module/Cargo.toml b/lucet-module/Cargo.toml index 16078ab0b..72f5291a6 100644 --- a/lucet-module/Cargo.toml +++ b/lucet-module/Cargo.toml @@ -17,6 +17,10 @@ serde_json = "1.0" bincode = "1.1.4" num-derive = "0.2" num-traits = "0.2" -minisign = "0.5.11" +minisign = { version = "0.5.11", optional = true } object = "0.12" byteorder = "1.3" + +[features] +default = ["signature_checking"] +signature_checking = ["minisign"] \ No newline at end of file diff --git a/lucet-module/src/error.rs b/lucet-module/src/error.rs index 311459392..a19ec8a33 100644 --- a/lucet-module/src/error.rs +++ b/lucet-module/src/error.rs @@ -9,6 +9,7 @@ pub enum Error { DeserializationError(#[cause] bincode::Error), #[fail(display = "Serialization error: {}", _0)] SerializationError(#[cause] bincode::Error), + #[cfg(feature = "signature_checking")] #[fail(display = "Module signature error: {}", _0)] ModuleSignatureError(#[cause] minisign::PError), #[fail(display = "I/O error: {}", _0)] diff --git a/lucet-module/src/lib.rs b/lucet-module/src/lib.rs index 8be0f1797..ede81ee2d 100644 --- a/lucet-module/src/lib.rs +++ b/lucet-module/src/lib.rs @@ -28,7 +28,9 @@ pub use crate::linear_memory::{HeapSpec, LinearMemorySpec, SparseData}; pub use crate::module::{Module, SerializedModule, LUCET_MODULE_SYM}; pub use crate::module_data::{ModuleData, MODULE_DATA_SYM}; pub use crate::runtime::InstanceRuntimeData; -pub use crate::signature::{ModuleSignature, PublicKey}; +pub use crate::signature::ModuleSignature; +#[cfg(feature = "signature_checking")] +pub use crate::signature::PublicKey; pub use crate::tables::TableElement; pub use crate::traps::{TrapCode, TrapManifest, TrapSite}; pub use crate::types::{Signature, ValueType}; diff --git a/lucet-module/src/module_data.rs b/lucet-module/src/module_data.rs index 9baaeeeb7..da1b5e968 100644 --- a/lucet-module/src/module_data.rs +++ b/lucet-module/src/module_data.rs @@ -7,6 +7,7 @@ use crate::{ types::Signature, Error, }; +#[cfg(feature = "signature_checking")] use minisign::SignatureBones; use serde::{Deserialize, Serialize}; @@ -37,6 +38,7 @@ pub struct ModuleData<'a> { } impl<'a> ModuleData<'a> { + #[cfg(feature = "signature_checking")] pub fn new( linear_memory: Option>, globals_spec: Vec>, @@ -57,6 +59,27 @@ impl<'a> ModuleData<'a> { } } + #[cfg(not(feature = "signature_checking"))] + pub fn new( + linear_memory: Option>, + globals_spec: Vec>, + function_info: Vec>, + import_functions: Vec>, + export_functions: Vec>, + signatures: Vec, + ) -> Self { + let module_signature = vec![0u8; 0]; + Self { + linear_memory, + globals_spec, + function_info, + import_functions, + export_functions, + signatures, + module_signature, + } + } + pub fn heap_spec(&self) -> Option<&HeapSpec> { if let Some(ref linear_memory) = self.linear_memory { Some(&linear_memory.heap) @@ -113,6 +136,7 @@ impl<'a> ModuleData<'a> { &self.module_signature } + #[cfg(feature = "signature_checking")] pub fn patch_module_signature( module_data_bin: &'a [u8], module_signature: &[u8], @@ -127,6 +151,7 @@ impl<'a> ModuleData<'a> { Ok(patched_module_data_bin) } + #[cfg(feature = "signature_checking")] pub fn clear_module_signature(module_data_bin: &'a [u8]) -> Result, Error> { let module_signature = vec![0u8; SignatureBones::BYTES]; Self::patch_module_signature(module_data_bin, &module_signature) diff --git a/lucet-module/src/signature.rs b/lucet-module/src/signature.rs index 5e6e52b4f..e1b9a2351 100644 --- a/lucet-module/src/signature.rs +++ b/lucet-module/src/signature.rs @@ -1,17 +1,24 @@ +#[cfg(feature = "signature_checking")] use crate::error::Error::{self, IOError, ModuleSignatureError}; use crate::module::LUCET_MODULE_SYM; use crate::module_data::MODULE_DATA_SYM; +#[cfg(feature = "signature_checking")] use crate::ModuleData; use byteorder::{ByteOrder, LittleEndian}; +#[cfg(feature = "signature_checking")] pub use minisign::{PublicKey, SecretKey}; +#[cfg(feature = "signature_checking")] use minisign::{SignatureBones, SignatureBox}; use object::*; use std::fs::{File, OpenOptions}; -use std::io::{self, Cursor, Read, Seek, SeekFrom, Write}; +#[cfg(feature = "signature_checking")] +use std::io::Cursor; +use std::io::{self, Read, Seek, SeekFrom, Write}; use std::path::Path; pub struct ModuleSignature; +#[cfg(feature = "signature_checking")] impl ModuleSignature { pub fn verify>( so_path: P, @@ -68,12 +75,14 @@ struct SymbolData { len: usize, } +#[allow(dead_code)] struct RawModuleAndData { pub obj_bin: Vec, pub module_data_offset: usize, pub module_data_len: usize, } +#[allow(dead_code)] impl RawModuleAndData { pub fn from_file>(path: P) -> Result { let mut obj_bin: Vec = Vec::new(); diff --git a/lucet-runtime/Cargo.toml b/lucet-runtime/Cargo.toml index 7f021f79d..47894062e 100644 --- a/lucet-runtime/Cargo.toml +++ b/lucet-runtime/Cargo.toml @@ -31,6 +31,9 @@ tempfile = "3.0" # only used for tests cc = "1.0" +[features] +signature_checking = ["lucet-module/signature_checking"] + [lib] name = "lucet_runtime" crate-type = ["rlib", "staticlib", "cdylib"] diff --git a/lucet-runtime/lucet-runtime-internals/Cargo.toml b/lucet-runtime/lucet-runtime-internals/Cargo.toml index 4d7e0b7c2..d17f65992 100644 --- a/lucet-runtime/lucet-runtime-internals/Cargo.toml +++ b/lucet-runtime/lucet-runtime-internals/Cargo.toml @@ -37,3 +37,6 @@ byteorder = "1.2" [build-dependencies] cc = "1.0" + +[features] +signature_checking = ["lucet-module/signature_checking"] diff --git a/lucet-runtime/lucet-runtime-internals/src/module/dl.rs b/lucet-runtime/lucet-runtime-internals/src/module/dl.rs index 350a08640..b07535084 100644 --- a/lucet-runtime/lucet-runtime-internals/src/module/dl.rs +++ b/lucet-runtime/lucet-runtime-internals/src/module/dl.rs @@ -3,9 +3,11 @@ use crate::module::{AddrDetails, GlobalSpec, HeapSpec, Module, ModuleInternal, T use libc::c_void; use libloading::Library; use lucet_module::{ - FunctionHandle, FunctionIndex, FunctionPointer, FunctionSpec, ModuleData, ModuleSignature, - PublicKey, SerializedModule, Signature, LUCET_MODULE_SYM, + FunctionHandle, FunctionIndex, FunctionPointer, FunctionSpec, ModuleData, SerializedModule, + Signature, LUCET_MODULE_SYM, }; +#[cfg(feature = "signature_checking")] +use lucet_module::{ModuleSignature, PublicKey}; use std::ffi::CStr; use std::mem::MaybeUninit; use std::path::Path; @@ -31,18 +33,24 @@ unsafe impl Sync for DlModule {} impl DlModule { /// Create a module, loading code from a shared object on the filesystem. pub fn load>(so_path: P) -> Result, Error> { - Self::load_and_maybe_verify(so_path, None) + Self::load_and_maybe_verify(so_path, |_module_data| Ok(())) } /// Create a module, loading code from a shared object on the filesystem /// and verifying it using a public key if one has been supplied. + #[cfg(feature = "signature_checking")] pub fn load_and_verify>(so_path: P, pk: PublicKey) -> Result, Error> { - Self::load_and_maybe_verify(so_path, Some(pk)) + Self::load_and_maybe_verify(so_path, |module_data| { + // Public key has been provided, verify the module signature + // The TOCTOU issue is unavoidable without reimplenting `dlopen(3)` + ModuleSignature::verify(so_path, &pk, &module_data) + }) } fn load_and_maybe_verify>( so_path: P, - pk: Option, + verifier: fn(&ModuleData) -> Result<(), Error>, + // pk: Option, ) -> Result, Error> { // Load the dynamic library. The undefined symbols corresponding to the lucet_syscall_ // functions will be provided by the current executable. We trust our wasm->dylib compiler @@ -50,7 +58,22 @@ impl DlModule { // stack and heap. let abs_so_path = so_path.as_ref().canonicalize().map_err(Error::DlError)?; let lib = Library::new(abs_so_path.as_os_str()).map_err(Error::DlError)?; + return Self::parse_and_maybe_verify(lib, verifier); + } + + /// Use the WASM module statically linked into the current binary + /// Currently Unix only as LibLoading does not support getting a handle to the running executable on windows + #[cfg(unix)] + pub fn from_current_binary() -> Result, Error> { + let curr = libloading::os::unix::Library::this(); + let lib = Library::from(curr); + return Self::parse_and_maybe_verify(lib, |_module_data| Ok(())); + } + fn parse_and_maybe_verify( + lib: Library, + verifier: fn(&ModuleData) -> Result<(), Error>, + ) -> Result, Error> { let serialized_module_ptr = unsafe { lib.get::<*const SerializedModule>(LUCET_MODULE_SYM.as_bytes()) .map_err(|e| { @@ -75,12 +98,7 @@ impl DlModule { ) }; let module_data = ModuleData::deserialize(module_data_slice)?; - - // If a public key has been provided, verify the module signature - // The TOCTOU issue is unavoidable without reimplenting `dlopen(3)` - if let Some(pk) = pk { - ModuleSignature::verify(so_path, &pk, &module_data)?; - } + verifier(&module_data)?; let fbase = if let Some(dli) = dladdr(serialized_module as *const SerializedModule as *const c_void) diff --git a/lucet-runtime/src/lib.rs b/lucet-runtime/src/lib.rs index fd5511773..60126a9e4 100644 --- a/lucet-runtime/src/lib.rs +++ b/lucet-runtime/src/lib.rs @@ -342,7 +342,9 @@ pub mod c_api; -pub use lucet_module::{PublicKey, TrapCode}; +#[cfg(feature = "signature_checking")] +pub use lucet_module::PublicKey; +pub use lucet_module::TrapCode; pub use lucet_runtime_internals::alloc::Limits; pub use lucet_runtime_internals::error::Error; pub use lucet_runtime_internals::instance::{ diff --git a/lucet-wasi/src/main.rs b/lucet-wasi/src/main.rs index 7919e3662..ad4b2f4e0 100644 --- a/lucet-wasi/src/main.rs +++ b/lucet-wasi/src/main.rs @@ -5,7 +5,9 @@ extern crate clap; use clap::Arg; use failure::{format_err, Error}; -use lucet_runtime::{self, DlModule, Limits, MmapRegion, Module, PublicKey, Region, RunResult}; +#[cfg(feature = "signature_checking")] +use lucet_runtime::PublicKey; +use lucet_runtime::{self, DlModule, Limits, MmapRegion, Module, Region, RunResult}; use lucet_wasi::{hostcalls, WasiCtxBuilder}; use std::fs::File; use std::path::PathBuf; @@ -18,6 +20,7 @@ struct Config<'a> { preopen_dirs: Vec<(File, &'a str)>, limits: Limits, verify: bool, + #[allow(dead_code)] pk_path: Option, } @@ -191,23 +194,37 @@ fn main() { run(config) } +#[cfg(feature = "signature_checking")] +fn get_module(config: &Config<'_>) -> Arc { + let pk = match (config.verify, config.pk_path) { + (false, _) => None, + (true, Some(pk_path)) => { + Some(PublicKey::from_file(pk_path).expect("public key can be loaded")) + } + (true, None) => panic!("signature verification requires a public key"), + }; + let module = if let Some(pk) = pk { + DlModule::load_and_verify(&config.lucet_module, pk).expect("signed module can be loaded") + } else { + DlModule::load(&config.lucet_module).expect("module can be loaded") + }; + return module; +} + +#[cfg(not(feature = "signature_checking"))] +fn get_module(config: &Config<'_>) -> Arc { + if config.verify { + panic!("Lucet not compiled with signature verification support"); + } + let module = DlModule::load(&config.lucet_module).expect("module can be loaded"); + return module; +} + fn run(config: Config<'_>) { lucet_wasi::hostcalls::ensure_linked(); let exitcode = { // doing all of this in a block makes sure everything gets dropped before exiting - let pk = match (config.verify, config.pk_path) { - (false, _) => None, - (true, Some(pk_path)) => { - Some(PublicKey::from_file(pk_path).expect("public key can be loaded")) - } - (true, None) => panic!("signature verification requires a public key"), - }; - let module = if let Some(pk) = pk { - DlModule::load_and_verify(&config.lucet_module, pk) - .expect("signed module can be loaded") - } else { - DlModule::load(&config.lucet_module).expect("module can be loaded") - }; + let module = get_module(&config); let min_globals_size = module.initial_globals_size(); let globals_size = ((min_globals_size + 4096 - 1) / 4096) * 4096; diff --git a/lucetc/Cargo.toml b/lucetc/Cargo.toml index fd934ce6b..f44001cc1 100644 --- a/lucetc/Cargo.toml +++ b/lucetc/Cargo.toml @@ -43,3 +43,6 @@ human-size = "0.4" parity-wasm = "0.38" minisign = "0.5.11" memoffset = "0.5.1" + +[features] +signature_checking = ["lucet-module/signature_checking"] diff --git a/lucetc/src/error.rs b/lucetc/src/error.rs index 7ac842842..784430929 100644 --- a/lucetc/src/error.rs +++ b/lucetc/src/error.rs @@ -67,4 +67,6 @@ pub enum LucetcErrorKind { Signature, #[fail(display = "Unsupported")] Unsupported, + #[fail(display = "Module Signing disabled at compile time")] + NoSigningSupport, } diff --git a/lucetc/src/signature.rs b/lucetc/src/signature.rs index 17a64b3ad..6205de989 100644 --- a/lucetc/src/signature.rs +++ b/lucetc/src/signature.rs @@ -1,4 +1,6 @@ +use crate::error::LucetcErrorKind::NoSigningSupport; use failure::*; +#[cfg(feature = "signature_checking")] use lucet_module::ModuleSignature; pub use minisign::{KeyPair, PublicKey, SecretKey, SignatureBones, SignatureBox}; use std::fs::File; @@ -74,6 +76,12 @@ pub fn verify_source_code( } // Sign the compiled code +#[cfg(feature = "signature_checking")] pub fn sign_module>(path: P, sk: &SecretKey) -> Result<(), Error> { ModuleSignature::sign(path, sk).map_err(|e| e.into()) } + +#[cfg(not(feature = "signature_checking"))] +pub fn sign_module>(_path: P, _sk: &SecretKey) -> Result<(), Error> { + Err(NoSigningSupport).map_err(|e| e.into()) +}