1 #[cfg(feature = "signature_checking")] 2 use crate::error::Error::{self, IOError, ModuleSignatureError}; 3 use crate::module::LUCET_MODULE_SYM; 4 use crate::module_data::MODULE_DATA_SYM; 5 #[cfg(feature = "signature_checking")] 6 use crate::ModuleData; 7 use byteorder::{ByteOrder, LittleEndian}; 8 #[cfg(feature = "signature_checking")] 9 pub use minisign::{PublicKey, SecretKey}; 10 #[cfg(feature = "signature_checking")] 11 use minisign::{SignatureBones, SignatureBox}; 12 use object::*; 13 use std::fs::{File, OpenOptions}; 14 #[cfg(feature = "signature_checking")] 15 use std::io::Cursor; 16 use std::io::{self, Read, Seek, SeekFrom, Write}; 17 use std::path::Path; 18 19 pub struct ModuleSignature; 20 21 #[cfg(feature = "signature_checking")] 22 impl ModuleSignature { verify<P: AsRef<Path>>( so_path: P, pk: &PublicKey, module_data: &ModuleData, ) -> Result<(), Error>23 pub fn verify<P: AsRef<Path>>( 24 so_path: P, 25 pk: &PublicKey, 26 module_data: &ModuleData, 27 ) -> Result<(), Error> { 28 let signature_box: SignatureBox = 29 SignatureBones::from_bytes(&module_data.get_module_signature()) 30 .map_err(|e| ModuleSignatureError(e))? 31 .into(); 32 33 let mut raw_module_and_data = 34 RawModuleAndData::from_file(&so_path).map_err(|e| IOError(e))?; 35 let cleared_module_data_bin = 36 ModuleData::clear_module_signature(raw_module_and_data.module_data_bin())?; 37 raw_module_and_data.patch_module_data(&cleared_module_data_bin); 38 39 minisign::verify( 40 &pk, 41 &signature_box, 42 Cursor::new(&raw_module_and_data.obj_bin), 43 true, 44 false, 45 ) 46 .map_err(|e| ModuleSignatureError(e)) 47 } 48 sign<P: AsRef<Path>>(path: P, sk: &SecretKey) -> Result<(), Error>49 pub fn sign<P: AsRef<Path>>(path: P, sk: &SecretKey) -> Result<(), Error> { 50 let raw_module_and_data = RawModuleAndData::from_file(&path).map_err(|e| IOError(e))?; 51 let signature_box = minisign::sign( 52 None, 53 sk, 54 Cursor::new(&raw_module_and_data.obj_bin), 55 true, 56 None, 57 None, 58 ) 59 .map_err(|e| ModuleSignatureError(e))?; 60 let signature_bones: SignatureBones = signature_box.into(); 61 let patched_module_data_bin = ModuleData::patch_module_signature( 62 raw_module_and_data.module_data_bin(), 63 &signature_bones.to_bytes(), 64 )?; 65 raw_module_and_data 66 .write_patched_module_data(&path, &patched_module_data_bin) 67 .map_err(|e| IOError(e))?; 68 Ok(()) 69 } 70 } 71 72 #[allow(dead_code)] 73 struct SymbolData { 74 offset: usize, 75 len: usize, 76 } 77 78 #[allow(dead_code)] 79 struct RawModuleAndData { 80 pub obj_bin: Vec<u8>, 81 pub module_data_offset: usize, 82 pub module_data_len: usize, 83 } 84 85 #[allow(dead_code)] 86 impl RawModuleAndData { from_file<P: AsRef<Path>>(path: P) -> Result<Self, io::Error>87 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, io::Error> { 88 let mut obj_bin: Vec<u8> = Vec::new(); 89 File::open(&path)?.read_to_end(&mut obj_bin)?; 90 91 let native_data_symbol_data = 92 Self::symbol_data(&obj_bin, LUCET_MODULE_SYM, true)?.ok_or(io::Error::new( 93 io::ErrorKind::InvalidInput, 94 format!("`{}` symbol not present", LUCET_MODULE_SYM), 95 ))?; 96 97 // While `module_data` is the first field of the `SerializedModule` that `lucet_module` points 98 // to, it is a virtual address, not a file offset. The translation is somewhat tricky at 99 // the moment, so just look at the corresponding `lucet_module_data` symbol for now. 100 let module_data_symbol_data = 101 Self::symbol_data(&obj_bin, MODULE_DATA_SYM, true)?.ok_or(io::Error::new( 102 io::ErrorKind::InvalidInput, 103 format!("`{}` symbol not present", MODULE_DATA_SYM), 104 ))?; 105 106 let module_data_len = 107 LittleEndian::read_u64(&obj_bin[(native_data_symbol_data.offset + 8)..]) as usize; 108 109 Ok(RawModuleAndData { 110 obj_bin, 111 module_data_offset: module_data_symbol_data.offset, 112 module_data_len: module_data_len, 113 }) 114 } 115 module_data_bin(&self) -> &[u8]116 pub fn module_data_bin(&self) -> &[u8] { 117 &self.obj_bin[self.module_data_offset as usize 118 ..self.module_data_offset as usize + self.module_data_len] 119 } 120 module_data_bin_mut(&mut self) -> &mut [u8]121 pub fn module_data_bin_mut(&mut self) -> &mut [u8] { 122 &mut self.obj_bin[self.module_data_offset as usize 123 ..self.module_data_offset as usize + self.module_data_len] 124 } 125 patch_module_data(&mut self, module_data_bin: &[u8])126 pub fn patch_module_data(&mut self, module_data_bin: &[u8]) { 127 self.module_data_bin_mut().copy_from_slice(&module_data_bin); 128 } 129 write_patched_module_data<P: AsRef<Path>>( &self, path: P, patched_module_data_bin: &[u8], ) -> Result<(), io::Error>130 pub fn write_patched_module_data<P: AsRef<Path>>( 131 &self, 132 path: P, 133 patched_module_data_bin: &[u8], 134 ) -> Result<(), io::Error> { 135 let mut fp = OpenOptions::new() 136 .write(true) 137 .create_new(false) 138 .open(&path)?; 139 fp.seek(SeekFrom::Start(self.module_data_offset as u64))?; 140 fp.write_all(&patched_module_data_bin)?; 141 Ok(()) 142 } 143 144 // Retrieving the offset of a symbol is not supported by the object crate. 145 // In Mach-O, actual file offsets are encoded, whereas Elf encodes virtual 146 // addresses, requiring extra steps to retrieve the section, its base 147 // address as well as the section offset. 148 149 // Elf 150 #[cfg(all(target_family = "unix", not(target_os = "macos")))] symbol_data( obj_bin: &[u8], symbol_name: &str, _mangle: bool, ) -> Result<Option<SymbolData>, io::Error>151 fn symbol_data( 152 obj_bin: &[u8], 153 symbol_name: &str, 154 _mangle: bool, 155 ) -> Result<Option<SymbolData>, io::Error> { 156 let obj = object::ElfFile::parse(obj_bin) 157 .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?; 158 let symbol_map = obj.symbol_map(); 159 for symbol in symbol_map.symbols() { 160 let kind = symbol.kind(); 161 if kind != SymbolKind::Data { 162 continue; 163 } 164 if symbol.name() != Some(symbol_name) { 165 continue; 166 } 167 let section_index = match symbol.section_index() { 168 Some(section_index) => section_index, 169 None => continue, 170 }; 171 let section = &obj.elf().section_headers[section_index.0]; 172 let offset = (symbol.address() - section.sh_addr + section.sh_offset) as usize; 173 let len = symbol.size() as usize; 174 return Ok(Some(SymbolData { offset, len })); 175 } 176 Ok(None) 177 } 178 179 // Mach-O 180 #[cfg(target_os = "macos")] symbol_data( obj_bin: &[u8], symbol_name: &str, mangle: bool, ) -> Result<Option<SymbolData>, io::Error>181 fn symbol_data( 182 obj_bin: &[u8], 183 symbol_name: &str, 184 mangle: bool, 185 ) -> Result<Option<SymbolData>, io::Error> { 186 let obj = object::File::parse(obj_bin) 187 .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?; 188 let symbol_map = obj.symbol_map(); 189 let mangled_symbol_name = format!("_{}", symbol_name); 190 let symbol_name = if mangle { 191 &mangled_symbol_name 192 } else { 193 symbol_name 194 }; 195 for symbol in symbol_map.symbols() { 196 let kind = symbol.kind(); 197 if kind != SymbolKind::Data && kind != SymbolKind::Unknown { 198 continue; 199 } 200 if symbol.name() != Some(symbol_name) { 201 continue; 202 } 203 let offset = symbol.address() as usize; 204 let len = symbol.size() as usize; 205 return Ok(Some(SymbolData { offset, len })); 206 } 207 Ok(None) 208 } 209 } 210