caps/
runtime.rs

1/*!
2Detect kernel features at runtime.
3
4This module exposes methods to perform detection of kernel
5features at runtime. This allows applications to auto-detect
6whether recent options are implemented by the currently
7running kernel.
8
9## Example
10
11```rust
12let ambient = caps::runtime::ambient_set_supported().is_ok();
13println!("Supported ambient set: {}", ambient);
14
15let all = caps::runtime::procfs_all_supported(None)
16    .unwrap_or_else(|_| caps::runtime::thread_all_supported());
17println!("Supported capabilities: {}", all.len());
18```
19!*/
20
21use super::{ambient, CapSet, Capability, CapsHashSet};
22use crate::errors::CapsError;
23use std::io::Read;
24use std::path::{Path, PathBuf};
25
26/// Check whether the running kernel supports the ambient set.
27///
28/// Ambient set was introduced in Linux kernel 4.3. On recent kernels
29/// where the ambient set is supported, this will return `Ok`.
30/// On a legacy kernel, an `Err` is returned instead.
31pub fn ambient_set_supported() -> Result<(), CapsError> {
32    ambient::has_cap(Capability::CAP_CHOWN)?;
33    Ok(())
34}
35
36/// Return the set of all capabilities supported by the running kernel.
37///
38/// This requires a mounted `procfs` and a kernel version >= 3.2. By default,
39/// it uses `/proc/` as the procfs mountpoint.
40pub fn procfs_all_supported(proc_mountpoint: Option<PathBuf>) -> Result<CapsHashSet, CapsError> {
41    /// See `man 2 capabilities`.
42    const LAST_CAP_FILEPATH: &str = "./sys/kernel/cap_last_cap";
43    let last_cap_path = proc_mountpoint
44        .unwrap_or_else(|| PathBuf::from("/proc/"))
45        .join(Path::new(LAST_CAP_FILEPATH));
46
47    let max_cap: u8 = {
48        let mut buf = String::with_capacity(4);
49        std::fs::File::open(last_cap_path.clone())
50            .and_then(|mut file| file.read_to_string(&mut buf))
51            .map_err(|e| format!("failed to read '{}': {}", last_cap_path.display(), e))?;
52        buf.trim_end()
53            .parse()
54            .map_err(|e| format!("failed to parse '{}': {}", last_cap_path.display(), e))?
55    };
56
57    let mut supported = super::all();
58    for c in super::all() {
59        if c.index() > max_cap {
60            supported.remove(&c);
61        }
62    }
63    Ok(supported)
64}
65
66/// Return the set of all capabilities supported on the current thread.
67///
68/// This does not require a mounted `procfs`, and it works with any
69/// kernel version >= 2.6.25.
70/// It internally uses `prctl(2)` and `PR_CAPBSET_READ`; if those are
71/// unavailable, this will result in an empty set.
72pub fn thread_all_supported() -> CapsHashSet {
73    let mut supported = super::all();
74    for c in super::all() {
75        if super::has_cap(None, CapSet::Bounding, c).is_err() {
76            supported.remove(&c);
77        }
78    }
79    supported
80}