1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
extern crate libc;
#[cfg(target_os = "openbsd")]
mod openbsd;
use std::ffi::NulError;
use std::{error, fmt};
#[derive(Debug, PartialEq)]
pub enum Error {
NotSupported,
Path(NulError),
Permissions(NulError),
Os(i32),
}
impl Error {
pub fn ignore_platform(self) -> Result<(), Self> {
match self {
Error::NotSupported => Ok(()),
x => Err(x),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::NotSupported => write!(f, "platform is not supported"),
Error::Path(_) => write!(f, "unexpected NUL character in path argument"),
Error::Permissions(_) => {
write!(f, "unexpected NUL character in permissions argument")
}
Error::Os(errno) => write!(f, "unable to unveil ({})", errno),
}
}
}
impl error::Error for Error {}
#[cfg(target_os = "openbsd")]
pub fn unveil(path: impl AsRef<[u8]>, permissions: &str) -> Result<(), Error> {
use std::ffi::CString;
let path = path.as_ref();
if path.is_empty() {
return openbsd::unveil(None, None).map_err(Error::Os);
}
let path = CString::new(path).map_err(Error::Path)?;
let permissions = CString::new(permissions).map_err(Error::Permissions)?;
openbsd::unveil(Some(&path), Some(&permissions)).map_err(Error::Os)
}
#[cfg(not(target_os = "openbsd"))]
#[allow(unused_variables)]
pub fn unveil(path: impl AsRef<[u8]>, permissions: &str) -> Result<(), Error> {
Err(Error::NotSupported)
}
#[cfg(test)]
mod tests {
use *;
#[test]
#[cfg(target_os = "openbsd")]
fn test_unveil() {
assert_eq!(unveil(".", "r"), Ok(()), "simple unveil should succeed");
assert_eq!(
unveil(b"/dev/null/\xFF", "r").unwrap_err(),
Error::Os(libc::ENOTDIR),
"unveil binary path under regular file should throw ENOTDIR",
);
assert_eq!(
unveil("/dev/null", ""),
Ok(()),
"unveil child with empty permissions should succeed",
);
assert_eq!(unveil("/dev", "r"), Ok(()), "unveil parent should succeed");
assert_eq!(
unveil("", ""),
Ok(()),
"unveil empty strings should lock successfully",
);
assert_eq!(
unveil(".", "r").unwrap_err(),
Error::Os(libc::EPERM),
"simple unveil after locking should throw EPERM",
);
use std::fs::File;
assert!(
File::open("/dev/zero").is_ok(),
"opening /dev/zero should succeed",
);
assert!(
File::open("/dev/null").is_err(),
"opening /dev/null should fail",
);
}
#[test]
#[cfg(not(target_os = "openbsd"))]
fn test_unveil_not_supported() {
assert_eq!(unveil(".", "r").unwrap_err(), Error::NotSupported);
}
}