syd/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```ignore
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 std::{
22 io::Read,
23 path::{Path, PathBuf},
24};
25
26use nix::errno::Errno;
27
28use super::{ambient, CapSet, Capability, CapsHashSet};
29use crate::{caps::errors::CapsError, err::err2no};
30
31/// Check whether the running kernel supports the ambient set.
32///
33/// Ambient set was introduced in Linux kernel 4.3. On recent kernels
34/// where the ambient set is supported, this will return `Ok`.
35/// On a legacy kernel, an `Err` is returned instead.
36pub fn ambient_set_supported() -> Result<(), CapsError> {
37 ambient::has_cap(Capability::CAP_CHOWN)?;
38 Ok(())
39}
40
41/// Return the set of all capabilities supported by the running kernel.
42///
43/// This requires a mounted `procfs` and a kernel version >= 3.2. By default,
44/// it uses `/proc/` as the procfs mountpoint.
45pub fn procfs_all_supported(proc_mountpoint: Option<PathBuf>) -> Result<CapsHashSet, CapsError> {
46 /// See `man 2 capabilities`.
47 const LAST_CAP_FILEPATH: &str = "./sys/kernel/cap_last_cap";
48 let last_cap_path = proc_mountpoint
49 .unwrap_or_else(|| PathBuf::from("/proc/"))
50 .join(Path::new(LAST_CAP_FILEPATH));
51
52 #[allow(clippy::disallowed_methods)]
53 let max_cap: u8 = {
54 let mut buf = String::with_capacity(4);
55 std::fs::File::open(last_cap_path.clone())
56 .and_then(|mut file| file.read_to_string(&mut buf))
57 .map_err(|e| CapsError(err2no(&e)))?;
58 buf.trim_end().parse().or(Err(CapsError(Errno::EINVAL)))?
59 };
60
61 let mut supported = super::all();
62 for c in super::all() {
63 if c.index() > max_cap {
64 supported.remove(&c);
65 }
66 }
67 Ok(supported)
68}
69
70/// Return the set of all capabilities supported on the current thread.
71///
72/// This does not require a mounted `procfs`, and it works with any
73/// kernel version >= 2.6.25.
74/// It internally uses `prctl(2)` and `PR_CAPBSET_READ`; if those are
75/// unavailable, this will result in an empty set.
76pub fn thread_all_supported() -> CapsHashSet {
77 let mut supported = super::all();
78 for c in super::all() {
79 if super::has_cap(None, CapSet::Bounding, c).is_err() {
80 supported.remove(&c);
81 }
82 }
83 supported
84}