1use crate::{Access, AccessFs, AccessNet, BitFlags, HandledAccess, PrivateHandledAccess, Scope};
4use libc::c_int;
5use std::io;
6use std::path::PathBuf;
7use thiserror::Error;
8
9#[derive(Debug, Error)]
11#[non_exhaustive]
12pub enum RulesetError {
13 #[error(transparent)]
14 HandleAccesses(#[from] HandleAccessesError),
15 #[error(transparent)]
16 CreateRuleset(#[from] CreateRulesetError),
17 #[error(transparent)]
18 AddRules(#[from] AddRulesError),
19 #[error(transparent)]
20 RestrictSelf(#[from] RestrictSelfError),
21 #[error(transparent)]
22 Scope(#[from] ScopeError),
23}
24
25#[test]
26fn ruleset_error_breaking_change() {
27 use crate::*;
28
29 let _: RulesetError = RulesetError::HandleAccesses(HandleAccessesError::Fs(
31 HandleAccessError::Compat(CompatError::Access(AccessError::Empty)),
32 ));
33}
34
35#[derive(Debug, Error)]
37#[non_exhaustive]
38pub enum HandleAccessError<T>
39where
40 T: HandledAccess,
41{
42 #[error(transparent)]
43 Compat(#[from] CompatError<T>),
44}
45
46#[derive(Debug, Error)]
48#[non_exhaustive]
49pub enum ScopeError {
50 #[error(transparent)]
51 Compat(#[from] CompatError<Scope>),
52}
53
54#[derive(Debug, Error)]
55#[non_exhaustive]
56pub enum HandleAccessesError {
57 #[error(transparent)]
58 Fs(HandleAccessError<AccessFs>),
59 #[error(transparent)]
60 Net(HandleAccessError<AccessNet>),
61}
62
63impl<A> From<HandleAccessError<A>> for HandleAccessesError
66where
67 A: PrivateHandledAccess,
68{
69 fn from(error: HandleAccessError<A>) -> Self {
70 A::into_handle_accesses_error(error)
71 }
72}
73
74#[derive(Debug, Error)]
76#[non_exhaustive]
77pub enum CreateRulesetError {
78 #[error("failed to create a ruleset: {source}")]
80 #[non_exhaustive]
81 CreateRulesetCall { source: io::Error },
82 #[error("missing access")]
85 MissingHandledAccess,
86}
87
88#[derive(Debug, Error)]
90#[non_exhaustive]
91pub enum AddRuleError<T>
92where
93 T: HandledAccess,
94{
95 #[error("failed to add a rule: {source}")]
97 #[non_exhaustive]
98 AddRuleCall { source: io::Error },
99 #[error("access-rights not handled by the ruleset: {incompatible:?}")]
101 UnhandledAccess {
102 access: BitFlags<T>,
103 incompatible: BitFlags<T>,
104 },
105 #[error(transparent)]
106 Compat(#[from] CompatError<T>),
107}
108
109impl<A> From<AddRuleError<A>> for AddRulesError
112where
113 A: PrivateHandledAccess,
114{
115 fn from(error: AddRuleError<A>) -> Self {
116 A::into_add_rules_error(error)
117 }
118}
119
120#[derive(Debug, Error)]
123#[non_exhaustive]
124pub enum AddRulesError {
125 #[error(transparent)]
126 Fs(AddRuleError<AccessFs>),
127 #[error(transparent)]
128 Net(AddRuleError<AccessNet>),
129}
130
131#[derive(Debug, Error)]
132#[non_exhaustive]
133pub enum CompatError<T>
134where
135 T: Access,
136{
137 #[error(transparent)]
138 PathBeneath(#[from] PathBeneathError),
139 #[error(transparent)]
140 Access(#[from] AccessError<T>),
141}
142
143#[derive(Debug, Error)]
144#[non_exhaustive]
145pub enum PathBeneathError {
146 #[error("failed to check file descriptor type: {source}")]
150 #[non_exhaustive]
151 StatCall { source: io::Error },
152 #[error("incompatible directory-only access-rights: {incompatible:?}")]
158 DirectoryAccess {
159 access: BitFlags<AccessFs>,
160 incompatible: BitFlags<AccessFs>,
161 },
162}
163
164#[derive(Debug, Error)]
165pub enum AccessError<T>
167where
168 T: Access,
169{
170 #[error("empty access-right")]
173 Empty,
174 #[error("unknown access-rights (at build time): {unknown:?}")]
177 Unknown {
178 access: BitFlags<T>,
179 unknown: BitFlags<T>,
180 },
181 #[error("fully incompatible access-rights: {access:?}")]
184 Incompatible { access: BitFlags<T> },
185 #[error("partially incompatible access-rights: {incompatible:?}")]
188 PartiallyCompatible {
189 access: BitFlags<T>,
190 incompatible: BitFlags<T>,
191 },
192}
193
194#[derive(Debug, Error)]
195#[non_exhaustive]
196pub enum RestrictSelfError {
197 #[error("failed to set no_new_privs: {source}")]
199 #[non_exhaustive]
200 SetNoNewPrivsCall { source: io::Error },
201 #[error("failed to restrict the calling thread: {source}")]
203 #[non_exhaustive]
204 RestrictSelfCall { source: io::Error },
205}
206
207#[derive(Debug, Error)]
208#[non_exhaustive]
209pub enum PathFdError {
210 #[error("failed to open \"{path}\": {source}")]
212 #[non_exhaustive]
213 OpenCall { source: io::Error, path: PathBuf },
214}
215
216#[cfg(test)]
217#[derive(Debug, Error)]
218pub(crate) enum TestRulesetError {
219 #[error(transparent)]
220 Ruleset(#[from] RulesetError),
221 #[error(transparent)]
222 PathFd(#[from] PathFdError),
223 #[error(transparent)]
224 File(#[from] std::io::Error),
225}
226
227#[derive(Debug, PartialEq, Eq)]
232pub struct Errno(c_int);
233
234impl Errno {
235 pub fn new(value: c_int) -> Self {
236 Self(value)
237 }
238}
239
240impl<T> From<T> for Errno
241where
242 T: std::error::Error,
243{
244 fn from(error: T) -> Self {
245 let default = libc::EINVAL;
246 if let Some(e) = error.source() {
247 if let Some(e) = e.downcast_ref::<std::io::Error>() {
248 return Errno(e.raw_os_error().unwrap_or(default));
249 }
250 }
251 Errno(default)
252 }
253}
254
255impl std::ops::Deref for Errno {
256 type Target = c_int;
257
258 fn deref(&self) -> &Self::Target {
259 &self.0
260 }
261}
262
263#[cfg(test)]
264fn _test_ruleset_errno(expected_errno: c_int) {
265 use std::io::Error;
266
267 let handle_access_err = RulesetError::HandleAccesses(HandleAccessesError::Fs(
268 HandleAccessError::Compat(CompatError::Access(AccessError::Empty)),
269 ));
270 assert_eq!(*Errno::from(handle_access_err), libc::EINVAL);
271
272 let create_ruleset_err = RulesetError::CreateRuleset(CreateRulesetError::CreateRulesetCall {
273 source: Error::from_raw_os_error(expected_errno),
274 });
275 assert_eq!(*Errno::from(create_ruleset_err), expected_errno);
276
277 let add_rules_fs_err = RulesetError::AddRules(AddRulesError::Fs(AddRuleError::AddRuleCall {
278 source: Error::from_raw_os_error(expected_errno),
279 }));
280 assert_eq!(*Errno::from(add_rules_fs_err), expected_errno);
281
282 let add_rules_net_err = RulesetError::AddRules(AddRulesError::Net(AddRuleError::AddRuleCall {
283 source: Error::from_raw_os_error(expected_errno),
284 }));
285 assert_eq!(*Errno::from(add_rules_net_err), expected_errno);
286
287 let add_rules_other_err =
288 RulesetError::AddRules(AddRulesError::Fs(AddRuleError::UnhandledAccess {
289 access: AccessFs::Execute.into(),
290 incompatible: BitFlags::<AccessFs>::EMPTY,
291 }));
292 assert_eq!(*Errno::from(add_rules_other_err), libc::EINVAL);
293
294 let restrict_self_err = RulesetError::RestrictSelf(RestrictSelfError::RestrictSelfCall {
295 source: Error::from_raw_os_error(expected_errno),
296 });
297 assert_eq!(*Errno::from(restrict_self_err), expected_errno);
298
299 let set_no_new_privs_err = RulesetError::RestrictSelf(RestrictSelfError::SetNoNewPrivsCall {
300 source: Error::from_raw_os_error(expected_errno),
301 });
302 assert_eq!(*Errno::from(set_no_new_privs_err), expected_errno);
303
304 let create_ruleset_missing_err =
305 RulesetError::CreateRuleset(CreateRulesetError::MissingHandledAccess);
306 assert_eq!(*Errno::from(create_ruleset_missing_err), libc::EINVAL);
307}
308
309#[test]
310fn test_ruleset_errno() {
311 _test_ruleset_errno(libc::EACCES);
312 _test_ruleset_errno(libc::EIO);
313}