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