1use crate::{
4 Access, AccessFs, AccessNet, BitFlags, HandledAccess, PrivateHandledAccess, RestrictSelfFlag,
5 Scope, SyscallFlag,
6};
7use libc::c_int;
8use std::io;
9use std::path::PathBuf;
10use thiserror::Error;
11
12#[derive(Debug, Error)]
14#[non_exhaustive]
15pub enum RulesetError {
16 #[error(transparent)]
17 HandleAccesses(#[from] HandleAccessesError),
18 #[error(transparent)]
19 CreateRuleset(#[from] CreateRulesetError),
20 #[error(transparent)]
21 AddRules(#[from] AddRulesError),
22 #[error(transparent)]
23 RestrictSelf(#[from] RestrictSelfError),
24 #[error(transparent)]
25 Scope(#[from] ScopeError),
26 #[error(transparent)]
30 RestrictSelfFlags(#[from] SyscallFlagError<RestrictSelfFlag>),
31}
32
33#[test]
34fn ruleset_error_breaking_change() {
35 use crate::*;
36
37 let _: RulesetError = RulesetError::HandleAccesses(HandleAccessesError::Fs(
39 HandleAccessError::Compat(CompatError::Access(AccessError::Empty)),
40 ));
41}
42
43#[derive(Debug, Error)]
45#[non_exhaustive]
46pub enum HandleAccessError<T>
47where
48 T: HandledAccess,
49{
50 #[error(transparent)]
51 Compat(#[from] CompatError<T>),
52}
53
54#[derive(Debug, Error)]
56#[non_exhaustive]
57pub enum ScopeError {
58 #[error(transparent)]
59 Compat(#[from] CompatError<Scope>),
60}
61
62#[derive(Debug, Error)]
67#[non_exhaustive]
68pub enum SyscallFlagError<F: SyscallFlag> {
69 #[error("unsupported syscall flag: {flag:?} set to {set}")]
71 #[non_exhaustive]
72 NotSupported { flag: F, set: bool },
73}
74
75#[derive(Debug, Error)]
76#[non_exhaustive]
77pub enum HandleAccessesError {
78 #[error(transparent)]
79 Fs(HandleAccessError<AccessFs>),
80 #[error(transparent)]
81 Net(HandleAccessError<AccessNet>),
82}
83
84impl<A> From<HandleAccessError<A>> for HandleAccessesError
87where
88 A: PrivateHandledAccess,
89{
90 fn from(error: HandleAccessError<A>) -> Self {
91 A::into_handle_accesses_error(error)
92 }
93}
94
95#[derive(Debug, Error)]
97#[non_exhaustive]
98pub enum CreateRulesetError {
99 #[error("failed to create a ruleset: {source}")]
101 #[non_exhaustive]
102 CreateRulesetCall { source: io::Error },
103 #[error("missing access")]
106 MissingHandledAccess,
107}
108
109#[derive(Debug, Error)]
111#[non_exhaustive]
112pub enum AddRuleError<T>
113where
114 T: HandledAccess,
115{
116 #[error("failed to add a rule: {source}")]
118 #[non_exhaustive]
119 AddRuleCall { source: io::Error },
120 #[error("access-rights not handled by the ruleset: {incompatible:?}")]
122 UnhandledAccess {
123 access: BitFlags<T>,
124 incompatible: BitFlags<T>,
125 },
126 #[error(transparent)]
127 Compat(#[from] CompatError<T>),
128}
129
130impl<A> From<AddRuleError<A>> for AddRulesError
133where
134 A: PrivateHandledAccess,
135{
136 fn from(error: AddRuleError<A>) -> Self {
137 A::into_add_rules_error(error)
138 }
139}
140
141#[derive(Debug, Error)]
144#[non_exhaustive]
145pub enum AddRulesError {
146 #[error(transparent)]
147 Fs(AddRuleError<AccessFs>),
148 #[error(transparent)]
149 Net(AddRuleError<AccessNet>),
150}
151
152#[derive(Debug, Error)]
153#[non_exhaustive]
154pub enum CompatError<T>
155where
156 T: Access,
157{
158 #[error(transparent)]
159 PathBeneath(#[from] PathBeneathError),
160 #[error(transparent)]
161 Access(#[from] AccessError<T>),
162}
163
164#[derive(Debug, Error)]
165#[non_exhaustive]
166pub enum PathBeneathError {
167 #[error("failed to check file descriptor type: {source}")]
171 #[non_exhaustive]
172 StatCall { source: io::Error },
173 #[error("incompatible directory-only access-rights: {incompatible:?}")]
179 DirectoryAccess {
180 access: BitFlags<AccessFs>,
181 incompatible: BitFlags<AccessFs>,
182 },
183}
184
185#[derive(Debug, Error)]
186pub enum AccessError<T>
188where
189 T: Access,
190{
191 #[error("empty access-right")]
194 Empty,
195 #[error("unknown access-rights (at build time): {unknown:?}")]
198 Unknown {
199 access: BitFlags<T>,
200 unknown: BitFlags<T>,
201 },
202 #[error("fully incompatible access-rights: {access:?}")]
205 Incompatible { access: BitFlags<T> },
206 #[error("partially incompatible access-rights: {incompatible:?}")]
209 PartiallyCompatible {
210 access: BitFlags<T>,
211 incompatible: BitFlags<T>,
212 },
213}
214
215#[derive(Debug, Error)]
216#[non_exhaustive]
217pub enum RestrictSelfError {
218 #[error("failed to set no_new_privs: {source}")]
220 #[non_exhaustive]
221 SetNoNewPrivsCall { source: io::Error },
222 #[error("failed to restrict the calling thread: {source}")]
224 #[non_exhaustive]
225 RestrictSelfCall { source: io::Error },
226}
227
228#[derive(Debug, Error)]
229#[non_exhaustive]
230pub enum PathFdError {
231 #[error("failed to open \"{path}\": {source}")]
233 #[non_exhaustive]
234 OpenCall { source: io::Error, path: PathBuf },
235}
236
237#[cfg(test)]
238#[derive(Debug, Error)]
239pub(crate) enum TestRulesetError {
240 #[error(transparent)]
241 Ruleset(#[from] RulesetError),
242 #[error(transparent)]
243 PathFd(#[from] PathFdError),
244 #[error(transparent)]
245 File(#[from] std::io::Error),
246}
247
248#[derive(Debug, PartialEq, Eq)]
253pub struct Errno(c_int);
254
255impl Errno {
256 pub fn new(value: c_int) -> Self {
257 Self(value)
258 }
259}
260
261impl<T> From<T> for Errno
262where
263 T: std::error::Error,
264{
265 fn from(error: T) -> Self {
266 let default = libc::EINVAL;
267 if let Some(e) = error.source() {
268 if let Some(e) = e.downcast_ref::<std::io::Error>() {
269 return Errno(e.raw_os_error().unwrap_or(default));
270 }
271 }
272 Errno(default)
273 }
274}
275
276impl std::ops::Deref for Errno {
277 type Target = c_int;
278
279 fn deref(&self) -> &Self::Target {
280 &self.0
281 }
282}
283
284#[cfg(test)]
285fn _test_ruleset_errno(expected_errno: c_int) {
286 use std::io::Error;
287
288 let handle_access_err = RulesetError::HandleAccesses(HandleAccessesError::Fs(
289 HandleAccessError::Compat(CompatError::Access(AccessError::Empty)),
290 ));
291 assert_eq!(*Errno::from(handle_access_err), libc::EINVAL);
292
293 let create_ruleset_err = RulesetError::CreateRuleset(CreateRulesetError::CreateRulesetCall {
294 source: Error::from_raw_os_error(expected_errno),
295 });
296 assert_eq!(*Errno::from(create_ruleset_err), expected_errno);
297
298 let add_rules_fs_err = RulesetError::AddRules(AddRulesError::Fs(AddRuleError::AddRuleCall {
299 source: Error::from_raw_os_error(expected_errno),
300 }));
301 assert_eq!(*Errno::from(add_rules_fs_err), expected_errno);
302
303 let add_rules_net_err = RulesetError::AddRules(AddRulesError::Net(AddRuleError::AddRuleCall {
304 source: Error::from_raw_os_error(expected_errno),
305 }));
306 assert_eq!(*Errno::from(add_rules_net_err), expected_errno);
307
308 let add_rules_other_err =
309 RulesetError::AddRules(AddRulesError::Fs(AddRuleError::UnhandledAccess {
310 access: AccessFs::Execute.into(),
311 incompatible: BitFlags::<AccessFs>::EMPTY,
312 }));
313 assert_eq!(*Errno::from(add_rules_other_err), libc::EINVAL);
314
315 let restrict_self_err = RulesetError::RestrictSelf(RestrictSelfError::RestrictSelfCall {
316 source: Error::from_raw_os_error(expected_errno),
317 });
318 assert_eq!(*Errno::from(restrict_self_err), expected_errno);
319
320 let set_no_new_privs_err = RulesetError::RestrictSelf(RestrictSelfError::SetNoNewPrivsCall {
321 source: Error::from_raw_os_error(expected_errno),
322 });
323 assert_eq!(*Errno::from(set_no_new_privs_err), expected_errno);
324
325 let create_ruleset_missing_err =
326 RulesetError::CreateRuleset(CreateRulesetError::MissingHandledAccess);
327 assert_eq!(*Errno::from(create_ruleset_missing_err), libc::EINVAL);
328
329 let restrict_self_flags_err = RulesetError::RestrictSelfFlags(SyscallFlagError::NotSupported {
330 flag: RestrictSelfFlag::LogSameExec,
331 set: false,
332 });
333 assert_eq!(*Errno::from(restrict_self_flags_err), libc::EINVAL);
334}
335
336#[test]
337fn test_ruleset_errno() {
338 _test_ruleset_errno(libc::EACCES);
339 _test_ruleset_errno(libc::EIO);
340}