1use std::io::{Read, Write};
11use std::path::PathBuf;
12
13use log::*;
14
15use crate::error::ErrorKind::*;
16use crate::error::*;
17
18use crate::{
19 ControllIdentifier, ControllerInternal, Controllers, DeviceResource, DeviceResources,
20 Resources, Subsystem,
21};
22
23#[derive(Debug, Clone)]
28pub struct DevicesController {
29 base: PathBuf,
30 path: PathBuf,
31}
32
33#[derive(Debug, Copy, Clone, PartialEq, Eq)]
35#[cfg_attr(
36 feature = "serde",
37 derive(serde::Serialize, serde::Deserialize),
38 serde(rename_all = "snake_case")
39)]
40pub enum DeviceType {
41 All,
43 Char,
45 Block,
47}
48
49#[allow(clippy::derivable_impls)]
50impl Default for DeviceType {
51 fn default() -> Self {
52 DeviceType::All
53 }
54}
55
56impl DeviceType {
57 #[allow(clippy::should_implement_trait, clippy::wrong_self_convention)]
59 pub fn to_char(&self) -> char {
60 match self {
61 DeviceType::All => 'a',
62 DeviceType::Char => 'c',
63 DeviceType::Block => 'b',
64 }
65 }
66
67 pub fn from_char(c: Option<char>) -> Option<DeviceType> {
69 match c {
70 Some('a') => Some(DeviceType::All),
71 Some('c') => Some(DeviceType::Char),
72 Some('b') => Some(DeviceType::Block),
73 _ => None,
74 }
75 }
76}
77
78#[derive(Debug, Copy, Clone, PartialEq, Eq)]
80#[cfg_attr(
81 feature = "serde",
82 derive(serde::Serialize, serde::Deserialize),
83 serde(rename_all = "snake_case")
84)]
85pub enum DevicePermissions {
86 Read,
88 Write,
90 MkNod,
93}
94
95impl DevicePermissions {
96 #[allow(clippy::should_implement_trait, clippy::wrong_self_convention)]
98 pub fn to_char(&self) -> char {
99 match self {
100 DevicePermissions::Read => 'r',
101 DevicePermissions::Write => 'w',
102 DevicePermissions::MkNod => 'm',
103 }
104 }
105
106 pub fn from_char(c: char) -> Option<DevicePermissions> {
108 match c {
109 'r' => Some(DevicePermissions::Read),
110 'w' => Some(DevicePermissions::Write),
111 'm' => Some(DevicePermissions::MkNod),
112 _ => None,
113 }
114 }
115
116 pub fn is_valid(s: &str) -> bool {
118 if s.is_empty() {
119 return false;
120 }
121 for i in s.chars() {
122 if i != 'r' && i != 'w' && i != 'm' {
123 return false;
124 }
125 }
126 true
127 }
128
129 pub fn all() -> Vec<DevicePermissions> {
131 vec![
132 DevicePermissions::Read,
133 DevicePermissions::Write,
134 DevicePermissions::MkNod,
135 ]
136 }
137
138 #[allow(clippy::should_implement_trait)]
140 pub fn from_str(s: &str) -> Result<Vec<DevicePermissions>> {
141 let mut v = Vec::new();
142 if s.is_empty() {
143 return Ok(v);
144 }
145 for e in s.chars() {
146 let perm = DevicePermissions::from_char(e).ok_or_else(|| Error::new(ParseError))?;
147 v.push(perm);
148 }
149
150 Ok(v)
151 }
152}
153
154impl ControllerInternal for DevicesController {
155 fn control_type(&self) -> Controllers {
156 Controllers::Devices
157 }
158 fn get_path(&self) -> &PathBuf {
159 &self.path
160 }
161 fn get_path_mut(&mut self) -> &mut PathBuf {
162 &mut self.path
163 }
164 fn get_base(&self) -> &PathBuf {
165 &self.base
166 }
167
168 fn apply(&self, res: &Resources) -> Result<()> {
169 let res: &DeviceResources = &res.devices;
171
172 for i in &res.devices {
173 if i.allow {
174 self.allow_device(i.devtype, i.major, i.minor, &i.access)?;
175 } else {
176 self.deny_device(i.devtype, i.major, i.minor, &i.access)?;
177 }
178 }
179
180 Ok(())
181 }
182}
183
184impl ControllIdentifier for DevicesController {
185 fn controller_type() -> Controllers {
186 Controllers::Devices
187 }
188}
189
190impl<'a> From<&'a Subsystem> for &'a DevicesController {
191 fn from(sub: &'a Subsystem) -> &'a DevicesController {
192 unsafe {
193 match sub {
194 Subsystem::Devices(c) => c,
195 _ => {
196 assert_eq!(1, 0);
197 let v = std::mem::MaybeUninit::uninit();
198 v.assume_init()
199 }
200 }
201 }
202 }
203}
204
205impl DevicesController {
206 pub fn new(root: PathBuf) -> Self {
208 Self {
209 base: root.clone(),
210 path: root,
211 }
212 }
213
214 pub fn allow_device(
219 &self,
220 devtype: DeviceType,
221 major: i64,
222 minor: i64,
223 perm: &[DevicePermissions],
224 ) -> Result<()> {
225 let perms = perm
226 .iter()
227 .map(DevicePermissions::to_char)
228 .collect::<String>();
229 let minor = if minor == -1 {
230 "*".to_string()
231 } else {
232 format!("{}", minor)
233 };
234 let major = if major == -1 {
235 "*".to_string()
236 } else {
237 format!("{}", major)
238 };
239 let final_str = format!("{} {}:{} {}", devtype.to_char(), major, minor, perms);
240 self.open_path("devices.allow", true).and_then(|mut file| {
241 file.write_all(final_str.as_ref()).map_err(|e| {
242 Error::with_cause(
243 WriteFailed(
244 self.get_path().join("devices.allow").display().to_string(),
245 final_str,
246 ),
247 e,
248 )
249 })
250 })
251 }
252
253 pub fn deny_device(
258 &self,
259 devtype: DeviceType,
260 major: i64,
261 minor: i64,
262 perm: &[DevicePermissions],
263 ) -> Result<()> {
264 let perms = perm
265 .iter()
266 .map(DevicePermissions::to_char)
267 .collect::<String>();
268 let minor = if minor == -1 {
269 "*".to_string()
270 } else {
271 format!("{}", minor)
272 };
273 let major = if major == -1 {
274 "*".to_string()
275 } else {
276 format!("{}", major)
277 };
278 let final_str = format!("{} {}:{} {}", devtype.to_char(), major, minor, perms);
279 self.open_path("devices.deny", true).and_then(|mut file| {
280 file.write_all(final_str.as_ref()).map_err(|e| {
281 Error::with_cause(
282 WriteFailed(
283 self.get_path().join("devices.deny").display().to_string(),
284 final_str,
285 ),
286 e,
287 )
288 })
289 })
290 }
291
292 pub fn allowed_devices(&self) -> Result<Vec<DeviceResource>> {
294 self.open_path("devices.list", false).and_then(|mut file| {
295 let mut s = String::new();
296 let res = file.read_to_string(&mut s);
297 match res {
298 Ok(_) => {
299 s.lines().fold(Ok(Vec::new()), |acc, line| {
300 let ls = line.to_string().split(|c| c == ' ' || c == ':').map(|x| x.to_string()).collect::<Vec<String>>();
301 if acc.is_err() || ls.len() != 4 {
302 error!("allowed_devices: acc: {:?}, ls: {:?}", acc, ls);
303 Err(Error::new(ParseError))
304 } else {
305 let devtype = DeviceType::from_char(ls[0].chars().next());
306 let mut major = ls[1].parse::<i64>();
307 let mut minor = ls[2].parse::<i64>();
308 if major.is_err() && ls[1] == "*" {
309 major = Ok(-1);
310 }
311 if minor.is_err() && ls[2] == "*" {
312 minor = Ok(-1);
313 }
314 if devtype.is_none() || major.is_err() || minor.is_err() || !DevicePermissions::is_valid(&ls[3]) {
315 error!("allowed_devices: acc: {:?}, ls: {:?}, devtype: {:?}, major {:?} minor {:?} ls3 {:?}",
316 acc, ls, devtype, major, minor, &ls[3]);
317 Err(Error::new(ParseError))
318 } else {
319 let access = DevicePermissions::from_str(&ls[3])?;
320 let mut acc = acc.unwrap();
321 acc.push(DeviceResource {
322 allow: true,
323 devtype: devtype.unwrap(),
324 major: major.unwrap(),
325 minor: minor.unwrap(),
326 access,
327 });
328 Ok(acc)
329 }
330 }
331 })
332 },
333 Err(e) => Err(Error::with_cause(ReadFailed("devices.list".to_string()), e)),
334 }
335 })
336 }
337}