1use crate::{Access, AccessFs, AccessNet, BitFlags};
2use std::io;
3use std::path::PathBuf;
4use thiserror::Error;
5
6#[derive(Debug, Error)]
8#[non_exhaustive]
9pub enum RulesetError {
10 #[error(transparent)]
11 HandleAccesses(#[from] HandleAccessesError),
12 #[error(transparent)]
13 CreateRuleset(#[from] CreateRulesetError),
14 #[error(transparent)]
15 AddRules(#[from] AddRulesError),
16 #[error(transparent)]
17 RestrictSelf(#[from] RestrictSelfError),
18}
19
20#[test]
21fn ruleset_error_breaking_change() {
22 use crate::*;
23
24 let _: RulesetError = RulesetError::HandleAccesses(HandleAccessesError::Fs(
26 HandleAccessError::Compat(CompatError::Access(AccessError::Empty)),
27 ));
28}
29
30#[derive(Debug, Error)]
32#[non_exhaustive]
33pub enum HandleAccessError<T>
34where
35 T: Access,
36{
37 #[error(transparent)]
38 Compat(#[from] CompatError<T>),
39}
40
41#[derive(Debug, Error)]
42#[non_exhaustive]
43pub enum HandleAccessesError {
44 #[error(transparent)]
45 Fs(HandleAccessError<AccessFs>),
46 #[error(transparent)]
47 Net(HandleAccessError<AccessNet>),
48}
49
50impl<A> From<HandleAccessError<A>> for HandleAccessesError
53where
54 A: Access,
55{
56 fn from(error: HandleAccessError<A>) -> Self {
57 A::into_handle_accesses_error(error)
58 }
59}
60
61#[derive(Debug, Error)]
63#[non_exhaustive]
64pub enum CreateRulesetError {
65 #[error("failed to create a ruleset: {source}")]
67 #[non_exhaustive]
68 CreateRulesetCall { source: io::Error },
69 #[error("missing handled access")]
71 MissingHandledAccess,
72}
73
74#[derive(Debug, Error)]
76#[non_exhaustive]
77pub enum AddRuleError<T>
78where
79 T: Access,
80{
81 #[error("failed to add a rule: {source}")]
83 #[non_exhaustive]
84 AddRuleCall { source: io::Error },
85 #[error("access-rights not handled by the ruleset: {incompatible:?}")]
87 UnhandledAccess {
88 access: BitFlags<T>,
89 incompatible: BitFlags<T>,
90 },
91 #[error(transparent)]
92 Compat(#[from] CompatError<T>),
93}
94
95impl<A> From<AddRuleError<A>> for AddRulesError
98where
99 A: Access,
100{
101 fn from(error: AddRuleError<A>) -> Self {
102 A::into_add_rules_error(error)
103 }
104}
105
106#[derive(Debug, Error)]
109#[non_exhaustive]
110pub enum AddRulesError {
111 #[error(transparent)]
112 Fs(AddRuleError<AccessFs>),
113 #[error(transparent)]
114 Net(AddRuleError<AccessNet>),
115}
116
117#[derive(Debug, Error)]
118#[non_exhaustive]
119pub enum CompatError<T>
120where
121 T: Access,
122{
123 #[error(transparent)]
124 PathBeneath(#[from] PathBeneathError),
125 #[error(transparent)]
126 Access(#[from] AccessError<T>),
127}
128
129#[derive(Debug, Error)]
130#[non_exhaustive]
131pub enum PathBeneathError {
132 #[error("failed to check file descriptor type: {source}")]
136 #[non_exhaustive]
137 StatCall { source: io::Error },
138 #[error("incompatible directory-only access-rights: {incompatible:?}")]
144 DirectoryAccess {
145 access: BitFlags<AccessFs>,
146 incompatible: BitFlags<AccessFs>,
147 },
148}
149
150#[derive(Debug, Error)]
151pub enum AccessError<T>
153where
154 T: Access,
155{
156 #[error("empty access-right")]
159 Empty,
160 #[error("unknown access-rights (at build time): {unknown:?}")]
163 Unknown {
164 access: BitFlags<T>,
165 unknown: BitFlags<T>,
166 },
167 #[error("fully incompatible access-rights: {access:?}")]
170 Incompatible { access: BitFlags<T> },
171 #[error("partially incompatible access-rights: {incompatible:?}")]
174 PartiallyCompatible {
175 access: BitFlags<T>,
176 incompatible: BitFlags<T>,
177 },
178}
179
180#[derive(Debug, Error)]
181#[non_exhaustive]
182pub enum RestrictSelfError {
183 #[error("failed to set no_new_privs: {source}")]
185 #[non_exhaustive]
186 SetNoNewPrivsCall { source: io::Error },
187 #[error("failed to restrict the calling thread: {source}")]
189 #[non_exhaustive]
190 RestrictSelfCall { source: io::Error },
191}
192
193#[derive(Debug, Error)]
194#[non_exhaustive]
195pub enum PathFdError {
196 #[error("failed to open \"{path}\": {source}")]
198 #[non_exhaustive]
199 OpenCall { source: io::Error, path: PathBuf },
200}
201
202#[cfg(test)]
203#[derive(Debug, Error)]
204pub(crate) enum TestRulesetError {
205 #[error(transparent)]
206 Ruleset(#[from] RulesetError),
207 #[error(transparent)]
208 PathFd(#[from] PathFdError),
209 #[error(transparent)]
210 File(#[from] std::io::Error),
211}
212
213#[derive(Debug, PartialEq, Eq)]
218pub struct Errno(libc::c_int);
219
220impl Errno {
221 pub fn new(value: libc::c_int) -> Self {
222 Self(value)
223 }
224}
225
226impl<T> From<T> for Errno
227where
228 T: std::error::Error,
229{
230 fn from(error: T) -> Self {
231 let default = libc::EINVAL;
232 if let Some(e) = error.source() {
233 if let Some(e) = e.downcast_ref::<std::io::Error>() {
234 return Errno(e.raw_os_error().unwrap_or(default));
235 }
236 }
237 Errno(default)
238 }
239}
240
241impl AsRef<libc::c_int> for Errno {
242 fn as_ref(&self) -> &libc::c_int {
243 &self.0
244 }
245}
246
247#[cfg(test)]
248fn _test_ruleset_errno(expected_errno: libc::c_int) {
249 use std::io::Error;
250
251 let handle_access_err = RulesetError::HandleAccesses(HandleAccessesError::Fs(
252 HandleAccessError::Compat(CompatError::Access(AccessError::Empty)),
253 ));
254 assert_eq!(Errno::from(handle_access_err).0, libc::EINVAL);
255
256 let create_ruleset_err = RulesetError::CreateRuleset(CreateRulesetError::CreateRulesetCall {
257 source: Error::from_raw_os_error(expected_errno),
258 });
259 assert_eq!(Errno::from(create_ruleset_err).0, expected_errno);
260
261 let add_rules_fs_err = RulesetError::AddRules(AddRulesError::Fs(AddRuleError::AddRuleCall {
262 source: Error::from_raw_os_error(expected_errno),
263 }));
264 assert_eq!(Errno::from(add_rules_fs_err).0, expected_errno);
265
266 let add_rules_net_err = RulesetError::AddRules(AddRulesError::Net(AddRuleError::AddRuleCall {
267 source: Error::from_raw_os_error(expected_errno),
268 }));
269 assert_eq!(Errno::from(add_rules_net_err).0, expected_errno);
270
271 let add_rules_other_err =
272 RulesetError::AddRules(AddRulesError::Fs(AddRuleError::UnhandledAccess {
273 access: AccessFs::Execute.into(),
274 incompatible: BitFlags::<AccessFs>::EMPTY,
275 }));
276 assert_eq!(Errno::from(add_rules_other_err).0, libc::EINVAL);
277
278 let restrict_self_err = RulesetError::RestrictSelf(RestrictSelfError::RestrictSelfCall {
279 source: Error::from_raw_os_error(expected_errno),
280 });
281 assert_eq!(Errno::from(restrict_self_err).0, expected_errno);
282
283 let set_no_new_privs_err = RulesetError::RestrictSelf(RestrictSelfError::SetNoNewPrivsCall {
284 source: Error::from_raw_os_error(expected_errno),
285 });
286 assert_eq!(Errno::from(set_no_new_privs_err).0, expected_errno);
287
288 let create_ruleset_missing_err =
289 RulesetError::CreateRuleset(CreateRulesetError::MissingHandledAccess);
290 assert_eq!(Errno::from(create_ruleset_missing_err).0, libc::EINVAL);
291}
292
293#[test]
294fn test_ruleset_errno() {
295 _test_ruleset_errno(libc::EACCES);
296 _test_ruleset_errno(libc::EIO);
297}