1 use bitflags::bitflags;
2 use std::path::Path;
3 
4 use crate::{read_value, ProcResult};
5 
6 /// Returns true if the miscellaneous Binary Formats system is enabled.
enabled() -> ProcResult<bool>7 pub fn enabled() -> ProcResult<bool> {
8     let val: String = read_value("/proc/sys/fs/binfmt_misc/status")?;
9     Ok(val == "enabled")
10 }
11 
hex_to_vec(hex: &str) -> ProcResult<Vec<u8>>12 fn hex_to_vec(hex: &str) -> ProcResult<Vec<u8>> {
13     if hex.len() % 2 != 0 {
14         return Err(build_internal_error!(format!(
15             "Hex string {:?} has non-even length",
16             hex
17         )));
18     }
19     let mut idx = 0;
20     let mut data = Vec::new();
21     while idx < hex.len() {
22         let byte = from_str!(u8, &hex[idx..idx + 2], 16);
23         data.push(byte);
24         idx += 2;
25     }
26 
27     Ok(data)
28 }
29 
30 #[derive(Debug, Clone)]
31 pub enum BinFmtData {
32     /// A BinFmt entry based on a file extension (does not include the period)
33     Extension(String),
34     /// A BinFmt entry based on magic string matching
35     Magic { offset: u8, magic: Vec<u8>, mask: Vec<u8> },
36 }
37 
38 /// A registered binary format entry
39 ///
40 /// For more info, see the kernel doc Documentation/admin-guide/binfmt-misc.rst
41 #[derive(Debug, Clone)]
42 pub struct BinFmtEntry {
43     /// The name of the entry
44     ///
45     /// Corresponds to a file in /proc/sys/fs/binfmt_misc/
46     pub name: String,
47     /// Is the entry enabled or not
48     pub enabled: bool,
49     /// Full path to the interpreter to run this entry
50     pub interpreter: String,
51     ///
52     pub flags: BinFmtFlags,
53     pub data: BinFmtData,
54 }
55 
56 impl BinFmtEntry {
from_string(name: String, data: &str) -> ProcResult<Self>57     pub(crate) fn from_string(name: String, data: &str) -> ProcResult<Self> {
58         let mut enabled = false;
59         let mut interpreter = String::new();
60 
61         let mut ext = None;
62 
63         let mut offset = 0;
64         let mut magic = Vec::new();
65         let mut mask = Vec::new();
66         let mut flags = BinFmtFlags::empty();
67 
68         for line in data.lines() {
69             if line == "enabled" {
70                 enabled = true;
71             } else if line.starts_with("interpreter ") {
72                 interpreter = line[12..].to_string();
73             } else if line.starts_with("flags:") {
74                 flags = BinFmtFlags::from_str(&line[6..]);
75             } else if line.starts_with("extension .") {
76                 ext = Some(line[11..].to_string());
77             } else if line.starts_with("offset ") {
78                 offset = from_str!(u8, &line[7..]);
79             } else if line.starts_with("magic ") {
80                 let hex = &line[6..];
81                 magic = hex_to_vec(dbg!(hex))?;
82             } else if line.starts_with("mask ") {
83                 let hex = &line[5..];
84                 mask = hex_to_vec(hex)?;
85             }
86         }
87 
88         if magic.len() > 0 && mask.len() == 0 {
89             mask.resize(magic.len(), 0xff);
90         }
91 
92         Ok(BinFmtEntry {
93             name,
94             enabled,
95             interpreter,
96             flags,
97             data: if let Some(ext) = ext {
98                 BinFmtData::Extension(ext)
99             } else {
100                 BinFmtData::Magic { magic, mask, offset }
101             },
102         })
103     }
104 }
105 
106 bitflags! {
107     /// Various key flags
108     pub struct BinFmtFlags: u8 {
109             /// Preserve Argv[0]
110             ///
111             /// Legacy behavior of binfmt_misc is to overwrite the original argv[0] with the full path to the binary. When
112             /// this flag is included, binfmt_misc will add an argument to the argument vector for this purpose, thus
113             /// preserving the original `argv[0]`.
114             ///
115             /// For example, If your interp is set to `/bin/foo` and you run `blah` (which is in `/usr/local/bin`),
116             /// then the kernel will execute `/bin/foo` with `argv[]` set to `["/bin/foo", "/usr/local/bin/blah", "blah"]`.
117             ///
118             /// The interp has to be aware of this so it can execute `/usr/local/bin/blah` with `argv[]` set to `["blah"]`.
119             const P = 0x01;
120 
121             /// Open Binary
122             ///
123             /// Legacy behavior of binfmt_misc is to pass the full path
124             /// of the binary to the interpreter as an argument. When this flag is
125             /// included, binfmt_misc will open the file for reading and pass its
126             /// descriptor as an argument, instead of the full path, thus allowing
127             /// the interpreter to execute non-readable binaries. This feature
128             /// should be used with care - the interpreter has to be trusted not to
129             //// emit the contents of the non-readable binary.
130             const O = 0x02;
131 
132             /// Credentials
133             ///
134             ///  Currently, the behavior of binfmt_misc is to calculate
135             /// the credentials and security token of the new process according to
136             /// the interpreter. When this flag is included, these attributes are
137             /// calculated according to the binary. It also implies the `O` flag.
138             /// This feature should be used with care as the interpreter
139             /// will run with root permissions when a setuid binary owned by root
140             /// is run with binfmt_misc.
141             const C = 0x04;
142 
143             /// Fix binary
144             ///
145             /// The usual behaviour of binfmt_misc is to spawn the
146             /// binary lazily when the misc format file is invoked.  However,
147             /// this doesn't work very well in the face of mount namespaces and
148             /// changeroots, so the `F` mode opens the binary as soon as the
149             /// emulation is installed and uses the opened image to spawn the
150             /// emulator, meaning it is always available once installed,
151             /// regardless of how the environment changes.
152             const F = 0x08;
153     }
154 }
155 
156 impl BinFmtFlags {
from_str(s: &str) -> Self157     pub(crate) fn from_str(s: &str) -> Self {
158         s.chars()
159             .filter_map(|c| match c {
160                 'P' => Some(BinFmtFlags::P),
161                 'O' => Some(BinFmtFlags::O),
162                 'C' => Some(BinFmtFlags::C),
163                 'F' => Some(BinFmtFlags::F),
164                 _ => None,
165             })
166             .fold(BinFmtFlags::empty(), |a, b| a | b)
167     }
168 }
169 
list() -> ProcResult<Vec<BinFmtEntry>>170 pub fn list() -> ProcResult<Vec<BinFmtEntry>> {
171     let path = Path::new("/proc/sys/fs/binfmt_misc/");
172 
173     let mut v = Vec::new();
174 
175     for entry in wrap_io_error!(path, path.read_dir())? {
176         let entry = entry?;
177         if entry.file_name() == "status" || entry.file_name() == "register" {
178             // these entries do not represent real entries
179             continue;
180         }
181 
182         let name = entry.file_name().to_string_lossy().to_string();
183 
184         let data = std::fs::read_to_string(entry.path())?;
185 
186         v.push(BinFmtEntry::from_string(name, &data)?);
187     }
188 
189     Ok(v)
190 }
191 
192 #[cfg(test)]
193 mod tests {
194     use super::*;
195 
196     #[test]
test_enabled()197     fn test_enabled() {
198         match enabled() {
199             Ok(_) => {}
200             Err(crate::ProcError::NotFound(_)) => {}
201             Err(e) => panic!("{}", e),
202         }
203     }
204 
205     #[test]
parse_magic()206     fn parse_magic() {
207         let mask = "7f454c460201010000000000000000000200f300";
208 
209         let data = hex_to_vec(mask).unwrap();
210         println!("{:?}", data);
211         assert_eq!(data.len(), 20);
212         assert_eq!(data[0], 0x7f);
213         assert_eq!(data[1], 0x45);
214 
215         assert!(hex_to_vec("a").is_err());
216         assert!(hex_to_vec("zz").is_err());
217     }
218 
219     #[test]
flags_parsing()220     fn flags_parsing() {
221         assert!(BinFmtFlags::from_str("").is_empty());
222 
223         let flags = BinFmtFlags::from_str("F");
224         assert_eq!(flags, BinFmtFlags::F);
225 
226         let flags = BinFmtFlags::from_str("OCF");
227         assert_eq!(flags, BinFmtFlags::F | BinFmtFlags::C | BinFmtFlags::O);
228     }
229 
230     #[test]
binfmt()231     fn binfmt() {
232         let data = r#"enabled
233 interpreter /usr/bin/qemu-riscv64-static
234 flags: OCF
235 offset 12
236 magic 7f454c460201010000000000000000000200f300
237 mask ffffffffffffff00fffffffffffffffffeffffff"#;
238 
239         let entry = BinFmtEntry::from_string("test".to_owned(), &data).unwrap();
240         println!("{:#?}", entry);
241         assert_eq!(entry.flags, BinFmtFlags::F | BinFmtFlags::C | BinFmtFlags::O);
242         assert!(entry.enabled);
243         assert_eq!(entry.interpreter, "/usr/bin/qemu-riscv64-static");
244         if let BinFmtData::Magic { offset, magic, mask } = entry.data {
245             assert_eq!(offset, 12);
246             assert_eq!(magic.len(), mask.len());
247             assert_eq!(
248                 magic,
249                 vec![
250                     0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
251                     0x02, 0x00, 0xf3, 0x00
252                 ]
253             );
254         } else {
255             panic!("Unexpected data");
256         }
257 
258         let data = r#"enabled
259 interpreter /bin/hello
260 flags:
261 extension .hello"#;
262         let entry = BinFmtEntry::from_string("test".to_owned(), &data).unwrap();
263         println!("{:#?}", entry);
264         assert_eq!(entry.flags, BinFmtFlags::empty());
265         assert!(entry.enabled);
266         assert_eq!(entry.interpreter, "/bin/hello");
267         if let BinFmtData::Extension(ext) = entry.data {
268             assert_eq!(ext, "hello");
269         } else {
270             panic!("Unexpected data");
271         }
272     }
273 
274     #[test]
live()275     fn live() {
276         for entry in super::list().unwrap() {
277             println!("{:?}", entry);
278         }
279     }
280 }
281