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