use crate::{Access, AccessFs, AccessNet, BitFlags, HandledAccess, PrivateHandledAccess, Scope};
use libc::c_int;
use std::io;
use std::path::PathBuf;
use thiserror::Error;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum RulesetError {
#[error(transparent)]
HandleAccesses(#[from] HandleAccessesError),
#[error(transparent)]
CreateRuleset(#[from] CreateRulesetError),
#[error(transparent)]
AddRules(#[from] AddRulesError),
#[error(transparent)]
RestrictSelf(#[from] RestrictSelfError),
#[error(transparent)]
Scope(#[from] ScopeError),
}
#[test]
fn ruleset_error_breaking_change() {
use crate::*;
let _: RulesetError = RulesetError::HandleAccesses(HandleAccessesError::Fs(
HandleAccessError::Compat(CompatError::Access(AccessError::Empty)),
));
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum HandleAccessError<T>
where
T: HandledAccess,
{
#[error(transparent)]
Compat(#[from] CompatError<T>),
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum ScopeError {
#[error(transparent)]
Compat(#[from] CompatError<Scope>),
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum HandleAccessesError {
#[error(transparent)]
Fs(HandleAccessError<AccessFs>),
#[error(transparent)]
Net(HandleAccessError<AccessNet>),
}
impl<A> From<HandleAccessError<A>> for HandleAccessesError
where
A: PrivateHandledAccess,
{
fn from(error: HandleAccessError<A>) -> Self {
A::into_handle_accesses_error(error)
}
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum CreateRulesetError {
#[error("failed to create a ruleset: {source}")]
#[non_exhaustive]
CreateRulesetCall { source: io::Error },
#[error("missing access")]
MissingHandledAccess,
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum AddRuleError<T>
where
T: HandledAccess,
{
#[error("failed to add a rule: {source}")]
#[non_exhaustive]
AddRuleCall { source: io::Error },
#[error("access-rights not handled by the ruleset: {incompatible:?}")]
UnhandledAccess {
access: BitFlags<T>,
incompatible: BitFlags<T>,
},
#[error(transparent)]
Compat(#[from] CompatError<T>),
}
impl<A> From<AddRuleError<A>> for AddRulesError
where
A: PrivateHandledAccess,
{
fn from(error: AddRuleError<A>) -> Self {
A::into_add_rules_error(error)
}
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum AddRulesError {
#[error(transparent)]
Fs(AddRuleError<AccessFs>),
#[error(transparent)]
Net(AddRuleError<AccessNet>),
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum CompatError<T>
where
T: Access,
{
#[error(transparent)]
PathBeneath(#[from] PathBeneathError),
#[error(transparent)]
Access(#[from] AccessError<T>),
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum PathBeneathError {
#[error("failed to check file descriptor type: {source}")]
#[non_exhaustive]
StatCall { source: io::Error },
#[error("incompatible directory-only access-rights: {incompatible:?}")]
DirectoryAccess {
access: BitFlags<AccessFs>,
incompatible: BitFlags<AccessFs>,
},
}
#[derive(Debug, Error)]
pub enum AccessError<T>
where
T: Access,
{
#[error("empty access-right")]
Empty,
#[error("unknown access-rights (at build time): {unknown:?}")]
Unknown {
access: BitFlags<T>,
unknown: BitFlags<T>,
},
#[error("fully incompatible access-rights: {access:?}")]
Incompatible { access: BitFlags<T> },
#[error("partially incompatible access-rights: {incompatible:?}")]
PartiallyCompatible {
access: BitFlags<T>,
incompatible: BitFlags<T>,
},
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum RestrictSelfError {
#[error("failed to set no_new_privs: {source}")]
#[non_exhaustive]
SetNoNewPrivsCall { source: io::Error },
#[error("failed to restrict the calling thread: {source}")]
#[non_exhaustive]
RestrictSelfCall { source: io::Error },
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum PathFdError {
#[error("failed to open \"{path}\": {source}")]
#[non_exhaustive]
OpenCall { source: io::Error, path: PathBuf },
}
#[cfg(test)]
#[derive(Debug, Error)]
pub(crate) enum TestRulesetError {
#[error(transparent)]
Ruleset(#[from] RulesetError),
#[error(transparent)]
PathFd(#[from] PathFdError),
#[error(transparent)]
File(#[from] std::io::Error),
}
#[derive(Debug, PartialEq, Eq)]
pub struct Errno(c_int);
impl Errno {
pub fn new(value: c_int) -> Self {
Self(value)
}
}
impl<T> From<T> for Errno
where
T: std::error::Error,
{
fn from(error: T) -> Self {
let default = libc::EINVAL;
if let Some(e) = error.source() {
if let Some(e) = e.downcast_ref::<std::io::Error>() {
return Errno(e.raw_os_error().unwrap_or(default));
}
}
Errno(default)
}
}
impl std::ops::Deref for Errno {
type Target = c_int;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(test)]
fn _test_ruleset_errno(expected_errno: c_int) {
use std::io::Error;
let handle_access_err = RulesetError::HandleAccesses(HandleAccessesError::Fs(
HandleAccessError::Compat(CompatError::Access(AccessError::Empty)),
));
assert_eq!(*Errno::from(handle_access_err), libc::EINVAL);
let create_ruleset_err = RulesetError::CreateRuleset(CreateRulesetError::CreateRulesetCall {
source: Error::from_raw_os_error(expected_errno),
});
assert_eq!(*Errno::from(create_ruleset_err), expected_errno);
let add_rules_fs_err = RulesetError::AddRules(AddRulesError::Fs(AddRuleError::AddRuleCall {
source: Error::from_raw_os_error(expected_errno),
}));
assert_eq!(*Errno::from(add_rules_fs_err), expected_errno);
let add_rules_net_err = RulesetError::AddRules(AddRulesError::Net(AddRuleError::AddRuleCall {
source: Error::from_raw_os_error(expected_errno),
}));
assert_eq!(*Errno::from(add_rules_net_err), expected_errno);
let add_rules_other_err =
RulesetError::AddRules(AddRulesError::Fs(AddRuleError::UnhandledAccess {
access: AccessFs::Execute.into(),
incompatible: BitFlags::<AccessFs>::EMPTY,
}));
assert_eq!(*Errno::from(add_rules_other_err), libc::EINVAL);
let restrict_self_err = RulesetError::RestrictSelf(RestrictSelfError::RestrictSelfCall {
source: Error::from_raw_os_error(expected_errno),
});
assert_eq!(*Errno::from(restrict_self_err), expected_errno);
let set_no_new_privs_err = RulesetError::RestrictSelf(RestrictSelfError::SetNoNewPrivsCall {
source: Error::from_raw_os_error(expected_errno),
});
assert_eq!(*Errno::from(set_no_new_privs_err), expected_errno);
let create_ruleset_missing_err =
RulesetError::CreateRuleset(CreateRulesetError::MissingHandledAccess);
assert_eq!(*Errno::from(create_ruleset_missing_err), libc::EINVAL);
}
#[test]
fn test_ruleset_errno() {
_test_ruleset_errno(libc::EACCES);
_test_ruleset_errno(libc::EIO);
}