seccomp/
filter.rs

1#![cfg(feature = "notify")]
2//! A wrapper around a SECCOMP context.
3
4use super::{action::Action, attribute::Attribute, raw, syscall::Syscall};
5use crate::notify::Notifier;
6use nix::errno::Errno;
7use std::{
8    fs::File,
9    io,
10    os::fd::{IntoRawFd, OwnedFd},
11    path::Path,
12};
13use thiserror::Error;
14
15#[cfg(feature = "notify")]
16use std::os::fd::FromRawFd;
17
18/// Errors related to filter generation.
19#[derive(Debug, Error)]
20pub enum Error {
21    /// Failure to initialize the context
22    #[error("Failed to initialization the Filter context")]
23    Initialization,
24
25    /// Failed to set attribute.
26    #[error("Failed to set attribute {0}: {1}")]
27    SetAttribute(Attribute, Errno),
28
29    /// Failed to add rule.
30    #[error("Failed to add rule {0} for {1}: {2}")]
31    AddRule(Action, Syscall, Errno),
32
33    /// Failed to write out as BPF
34    #[error("Failed to write BPF: {0}")]
35    Io(#[from] io::Error),
36
37    /// Failed to export the SECCOMP to BPF.
38    #[error("Failed to export to BPF: {0}")]
39    Export(Errno),
40
41    /// Failed to load the policy into the process.
42    #[error("Failed to load policy: {0}")]
43    Load(Errno),
44
45    /// Failed to send the SECCOMP FD to the monitor.
46    #[cfg(feature = "notify")]
47    #[error("Failed to send FD to notifier")]
48    Send,
49
50    /// Failed to prepare notifier
51    #[cfg(feature = "notify")]
52    #[error("Failed to prepare notifier: {0}")]
53    Prepare(String),
54}
55
56/// The Filter is a wrapper around a SECCOMP Context.
57///
58/// This implementation has first-class support for the SECCOMP Notify
59/// API, but a lot of the logic needs to be implemented in the
60/// application. Firstly, implement the `Notifier` trait for
61/// the calling process (The one that loads the filter). Then,
62/// use a `notify::Pair` on the monitoring process. A working
63/// implementation of both exist in Antimony.
64///
65/// ## Examples
66///
67/// Load a basic rule that logs everything but `read`.
68/// ```rust
69/// use seccomp::{filter::Filter, action::Action, attribute::Attribute, syscall::Syscall};
70/// let mut filter = Filter::new(Action::Log).unwrap();
71/// filter.set_attribute(Attribute::NoNewPrivileges(true)).unwrap();
72/// filter.add_rule(Action::Allow, Syscall::from_name("read").unwrap()).unwrap();
73/// filter.load();
74/// ```
75///
76pub struct Filter {
77    ctx: raw::scmp_filter_ctx,
78
79    #[cfg(feature = "notify")]
80    notifier: Option<Box<dyn Notifier>>,
81}
82impl Filter {
83    /// Construct a new filter with a default action.
84    pub fn new(def_action: Action) -> Result<Self, Error> {
85        let ctx = unsafe { raw::seccomp_init(def_action.into()) };
86        if ctx.is_null() {
87            Err(Error::Initialization)
88        } else {
89            #[cfg(feature = "notify")]
90            return Ok(Self {
91                ctx,
92                notifier: None,
93            });
94
95            #[cfg(not(feature = "notify"))]
96            return Ok(Self { ctx });
97        }
98    }
99
100    #[cfg(feature = "notify")]
101    /// Set a notifier monitor process. See the Notifier trait for more information.
102    pub fn set_notifier(&mut self, f: impl Notifier) {
103        self.notifier = Some(Box::new(f))
104    }
105
106    /// Set an attribute.
107    pub fn set_attribute(&mut self, attr: Attribute) -> Result<(), Error> {
108        match unsafe { raw::seccomp_attr_set(self.ctx, attr.name(), attr.value()) } {
109            0 => Ok(()),
110            e => Err(Error::SetAttribute(attr, Errno::from_raw(e))),
111        }
112    }
113
114    /// Add a rule. Complex rules are not supported.
115    pub fn add_rule(&mut self, action: Action, syscall: Syscall) -> Result<(), Error> {
116        match unsafe { raw::seccomp_rule_add(self.ctx, action.into(), syscall.into(), 0) } {
117            0 => Ok(()),
118            e => Err(Error::AddRule(action, syscall, Errno::from_raw(e))),
119        }
120    }
121
122    /// Write the filter to a new file with the BPF format of the filter.
123    pub fn write(&self, path: &Path) -> Result<OwnedFd, Error> {
124        let file = File::create(path)?;
125        match unsafe { raw::seccomp_export_bpf(self.ctx, file.into_raw_fd()) } {
126            0 => Ok(File::open(path)?.into()),
127            e => Err(Error::Export(Errno::from_raw(e))),
128        }
129    }
130
131    /// Execute the notifier's setup functions. This is necessary
132    /// to call before calling load().
133    #[cfg(feature = "notify")]
134    pub fn setup(&mut self) -> Result<(), Error> {
135        if let Some(notifier) = &mut self.notifier {
136            for (action, call) in notifier.exempt() {
137                self.add_rule(action, call)?
138            }
139        }
140
141        if let Some(notifier) = &mut self.notifier {
142            notifier.prepare().map_err(Error::Prepare)?;
143        }
144        Ok(())
145    }
146
147    #[cfg(feature = "notify")]
148    /// Loads the policy, optionally executing a Notifier function.
149    ///
150    /// Note that this function treats failure as fatal. It will panic
151    /// the program if the policy cannot be loaded.
152    pub fn load(mut self) {
153        if let Some(mut notifier) = self.notifier.take() {
154            match unsafe { raw::seccomp_load(self.ctx) } {
155                0 => {
156                    let fd = unsafe { OwnedFd::from_raw_fd(raw::seccomp_notify_fd(self.ctx)) };
157                    notifier.handle(fd);
158                }
159                errno => panic!("Failed to set filter: {errno}"),
160            };
161        }
162    }
163
164    #[cfg(not(feature = "notify"))]
165    /// Loads the policy.
166    ///
167    /// Note that this function treats failure as fatal. It will panic
168    /// the program if the policy cannot be loaded.
169    pub fn load(self) {
170        let errno = unsafe { raw::seccomp_load(self.ctx) };
171        if errno != 0 {
172            panic!("Failed to set filter: {errno}");
173        }
174    }
175}
176impl Drop for Filter {
177    fn drop(&mut self) {
178        unsafe { raw::seccomp_release(self.ctx) }
179    }
180}
181
182// The filter can be shared across threads, but it
183// cannot be modified simultaneously
184unsafe impl Sync for Filter {}
185unsafe impl Send for Filter {}