mod bpf;
mod condition;
mod filter;
mod rule;
pub use condition::SeccompCondition;
pub use filter::SeccompFilter;
pub use rule::SeccompRule;
#[cfg(feature = "json")]
use serde::Deserialize;
use core::fmt::Formatter;
use std::fmt::Display;
use libc::{
SECCOMP_RET_ALLOW, SECCOMP_RET_DATA, SECCOMP_RET_ERRNO, SECCOMP_RET_KILL_PROCESS,
SECCOMP_RET_KILL_THREAD, SECCOMP_RET_LOG, SECCOMP_RET_TRACE, SECCOMP_RET_TRAP,
};
use bpf::{ARG_NUMBER_MAX, AUDIT_ARCH_AARCH64, AUDIT_ARCH_RISCV64, AUDIT_ARCH_X86_64, BPF_MAX_LEN};
pub use bpf::{sock_filter, BpfProgram, BpfProgramRef};
type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
EmptyRule,
FilterTooLarge(usize),
IdenticalActions,
InvalidArgumentNumber,
InvalidTargetArch(String),
}
impl std::error::Error for Error {}
impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
use self::Error::*;
match self {
EmptyRule => {
write!(f, "The condition vector of a rule cannot be empty.")
}
FilterTooLarge(len) => write!(
f,
"The seccomp filter contains too many BPF instructions: {}. Max length is {}.",
len, BPF_MAX_LEN
),
IdenticalActions => write!(f, "`match_action` and `mismatch_action` are equal."),
InvalidArgumentNumber => {
write!(
f,
"The seccomp rule contains an invalid argument index. Maximum index value: {}",
ARG_NUMBER_MAX
)
}
InvalidTargetArch(arch) => write!(f, "Invalid target arch: {}.", arch),
}
}
}
#[allow(non_camel_case_types)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum TargetArch {
x86_64,
aarch64,
riscv64,
}
impl TargetArch {
fn get_audit_value(self) -> u32 {
match self {
TargetArch::x86_64 => AUDIT_ARCH_X86_64,
TargetArch::aarch64 => AUDIT_ARCH_AARCH64,
TargetArch::riscv64 => AUDIT_ARCH_RISCV64,
}
}
}
impl TryFrom<&str> for TargetArch {
type Error = Error;
fn try_from(input: &str) -> Result<Self> {
match input.to_lowercase().as_str() {
"x86_64" => Ok(TargetArch::x86_64),
"aarch64" => Ok(TargetArch::aarch64),
"riscv64" => Ok(TargetArch::riscv64),
_ => Err(Error::InvalidTargetArch(input.to_string())),
}
}
}
#[cfg_attr(
feature = "json",
derive(Deserialize),
serde(rename_all = "snake_case")
)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SeccompCmpOp {
Eq,
Ge,
Gt,
Le,
Lt,
MaskedEq(u64),
Ne,
}
#[cfg_attr(feature = "json", derive(Deserialize), serde(rename_all = "lowercase"))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SeccompCmpArgLen {
Dword,
Qword,
}
#[cfg_attr(
feature = "json",
derive(Deserialize),
serde(rename_all = "snake_case")
)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SeccompAction {
Allow,
Errno(u32),
KillThread,
KillProcess,
Log,
Trace(u32),
Trap,
}
impl From<SeccompAction> for u32 {
fn from(action: SeccompAction) -> Self {
match action {
SeccompAction::Allow => SECCOMP_RET_ALLOW,
SeccompAction::Errno(x) => SECCOMP_RET_ERRNO | (x & SECCOMP_RET_DATA),
SeccompAction::KillThread => SECCOMP_RET_KILL_THREAD,
SeccompAction::KillProcess => SECCOMP_RET_KILL_PROCESS,
SeccompAction::Log => SECCOMP_RET_LOG,
SeccompAction::Trace(x) => SECCOMP_RET_TRACE | (x & SECCOMP_RET_DATA),
SeccompAction::Trap => SECCOMP_RET_TRAP,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_target_arch() {
assert!(TargetArch::try_from("invalid").is_err());
assert!(TargetArch::try_from("x8664").is_err());
assert_eq!(TargetArch::try_from("x86_64").unwrap(), TargetArch::x86_64);
assert_eq!(TargetArch::try_from("X86_64").unwrap(), TargetArch::x86_64);
assert_eq!(
TargetArch::try_from("aarch64").unwrap(),
TargetArch::aarch64
);
assert_eq!(
TargetArch::try_from("aARch64").unwrap(),
TargetArch::aarch64
);
assert_eq!(
TargetArch::try_from("riscv64").unwrap(),
TargetArch::riscv64
);
assert_eq!(
TargetArch::try_from("RiScV64").unwrap(),
TargetArch::riscv64
);
}
}