caps/
lib.rs

1/*!
2A pure-Rust library to work with Linux capabilities.
3
4It provides support for manipulating capabilities available on modern Linux
5kernels. It supports traditional POSIX sets (Effective, Inheritable, Permitted)
6as well as Linux-specific Ambient and Bounding capabilities sets.
7
8```rust
9type ExResult<T> = Result<T, Box<dyn std::error::Error + 'static>>;
10
11fn manipulate_caps() -> ExResult<()> {
12    use caps::{Capability, CapSet};
13
14    if caps::has_cap(None, CapSet::Permitted, Capability::CAP_SYS_NICE)? {
15        caps::drop(None, CapSet::Effective, Capability::CAP_SYS_NICE)?;
16        let effective = caps::read(None, CapSet::Effective)?;
17        assert_eq!(effective.contains(&Capability::CAP_SYS_NICE), false);
18
19        caps::clear(None, CapSet::Effective)?;
20        let cleared = caps::read(None, CapSet::Effective)?;
21        assert_eq!(cleared.is_empty(), true);
22    };
23
24    Ok(())
25}
26```
27!*/
28
29pub mod errors;
30pub mod runtime;
31pub mod securebits;
32
33// Implementation of Bounding set.
34mod ambient;
35// Implementation of POSIX sets.
36mod base;
37// Implementation of Bounding set.
38mod bounding;
39// All kernel-related constants.
40mod nr;
41
42use crate::errors::CapsError;
43use std::iter::FromIterator;
44
45/// Linux capabilities sets.
46///
47/// All capabilities sets supported by Linux, including standard
48/// POSIX and custom ones. See `capabilities(7)`.
49#[derive(Debug, Clone, Copy)]
50pub enum CapSet {
51    /// Ambient capabilities set (from Linux 4.3).
52    Ambient,
53    /// Bounding capabilities set (from Linux 2.6.25)
54    Bounding,
55    /// Effective capabilities set (from POSIX)
56    Effective,
57    /// Inheritable capabilities set (from POSIX)
58    Inheritable,
59    /// Permitted capabilities set (from POSIX)
60    Permitted,
61}
62
63/// Linux capabilities.
64///
65/// All capabilities supported by Linux, including standard
66/// POSIX and custom ones. See `capabilities(7)`.
67#[allow(clippy::manual_non_exhaustive)]
68#[allow(non_camel_case_types)]
69#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
70#[repr(u8)]
71#[cfg_attr(
72    feature = "serde_support",
73    derive(serde::Serialize, serde::Deserialize)
74)]
75pub enum Capability {
76    /// `CAP_CHOWN` (from POSIX)
77    CAP_CHOWN = nr::CAP_CHOWN,
78    /// `CAP_DAC_OVERRIDE` (from POSIX)
79    CAP_DAC_OVERRIDE = nr::CAP_DAC_OVERRIDE,
80    /// `CAP_DAC_READ_SEARCH` (from POSIX)
81    CAP_DAC_READ_SEARCH = nr::CAP_DAC_READ_SEARCH,
82    /// `CAP_FOWNER` (from POSIX)
83    CAP_FOWNER = nr::CAP_FOWNER,
84    /// `CAP_FSETID` (from POSIX)
85    CAP_FSETID = nr::CAP_FSETID,
86    /// `CAP_KILL` (from POSIX)
87    CAP_KILL = nr::CAP_KILL,
88    /// `CAP_SETGID` (from POSIX)
89    CAP_SETGID = nr::CAP_SETGID,
90    /// `CAP_SETUID` (from POSIX)
91    CAP_SETUID = nr::CAP_SETUID,
92    /// `CAP_SETPCAP` (from Linux)
93    CAP_SETPCAP = nr::CAP_SETPCAP,
94    CAP_LINUX_IMMUTABLE = nr::CAP_LINUX_IMMUTABLE,
95    CAP_NET_BIND_SERVICE = nr::CAP_NET_BIND_SERVICE,
96    CAP_NET_BROADCAST = nr::CAP_NET_BROADCAST,
97    CAP_NET_ADMIN = nr::CAP_NET_ADMIN,
98    CAP_NET_RAW = nr::CAP_NET_RAW,
99    CAP_IPC_LOCK = nr::CAP_IPC_LOCK,
100    CAP_IPC_OWNER = nr::CAP_IPC_OWNER,
101    /// `CAP_SYS_MODULE` (from Linux)
102    CAP_SYS_MODULE = nr::CAP_SYS_MODULE,
103    /// `CAP_SYS_RAWIO` (from Linux)
104    CAP_SYS_RAWIO = nr::CAP_SYS_RAWIO,
105    /// `CAP_SYS_CHROOT` (from Linux)
106    CAP_SYS_CHROOT = nr::CAP_SYS_CHROOT,
107    /// `CAP_SYS_PTRACE` (from Linux)
108    CAP_SYS_PTRACE = nr::CAP_SYS_PTRACE,
109    /// `CAP_SYS_PACCT` (from Linux)
110    CAP_SYS_PACCT = nr::CAP_SYS_PACCT,
111    /// `CAP_SYS_ADMIN` (from Linux)
112    CAP_SYS_ADMIN = nr::CAP_SYS_ADMIN,
113    /// `CAP_SYS_BOOT` (from Linux)
114    CAP_SYS_BOOT = nr::CAP_SYS_BOOT,
115    /// `CAP_SYS_NICE` (from Linux)
116    CAP_SYS_NICE = nr::CAP_SYS_NICE,
117    /// `CAP_SYS_RESOURCE` (from Linux)
118    CAP_SYS_RESOURCE = nr::CAP_SYS_RESOURCE,
119    /// `CAP_SYS_TIME` (from Linux)
120    CAP_SYS_TIME = nr::CAP_SYS_TIME,
121    /// `CAP_SYS_TTY_CONFIG` (from Linux)
122    CAP_SYS_TTY_CONFIG = nr::CAP_SYS_TTY_CONFIG,
123    /// `CAP_SYS_MKNOD` (from Linux, >= 2.4)
124    CAP_MKNOD = nr::CAP_MKNOD,
125    /// `CAP_LEASE` (from Linux, >= 2.4)
126    CAP_LEASE = nr::CAP_LEASE,
127    CAP_AUDIT_WRITE = nr::CAP_AUDIT_WRITE,
128    /// `CAP_AUDIT_CONTROL` (from Linux, >= 2.6.11)
129    CAP_AUDIT_CONTROL = nr::CAP_AUDIT_CONTROL,
130    CAP_SETFCAP = nr::CAP_SETFCAP,
131    CAP_MAC_OVERRIDE = nr::CAP_MAC_OVERRIDE,
132    CAP_MAC_ADMIN = nr::CAP_MAC_ADMIN,
133    /// `CAP_SYSLOG` (from Linux, >= 2.6.37)
134    CAP_SYSLOG = nr::CAP_SYSLOG,
135    /// `CAP_WAKE_ALARM` (from Linux, >= 3.0)
136    CAP_WAKE_ALARM = nr::CAP_WAKE_ALARM,
137    CAP_BLOCK_SUSPEND = nr::CAP_BLOCK_SUSPEND,
138    /// `CAP_AUDIT_READ` (from Linux, >= 3.16).
139    CAP_AUDIT_READ = nr::CAP_AUDIT_READ,
140    /// `CAP_PERFMON` (from Linux, >= 5.8).
141    CAP_PERFMON = nr::CAP_PERFMON,
142    /// `CAP_BPF` (from Linux, >= 5.8).
143    CAP_BPF = nr::CAP_BPF,
144    /// `CAP_CHECKPOINT_RESTORE` (from Linux, >= 5.9).
145    CAP_CHECKPOINT_RESTORE = nr::CAP_CHECKPOINT_RESTORE,
146    #[doc(hidden)]
147    __Nonexhaustive,
148}
149
150impl std::fmt::Display for Capability {
151    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
152        let name = match *self {
153            Capability::CAP_CHOWN => "CAP_CHOWN",
154            Capability::CAP_DAC_OVERRIDE => "CAP_DAC_OVERRIDE",
155            Capability::CAP_DAC_READ_SEARCH => "CAP_DAC_READ_SEARCH",
156            Capability::CAP_FOWNER => "CAP_FOWNER",
157            Capability::CAP_FSETID => "CAP_FSETID",
158            Capability::CAP_KILL => "CAP_KILL",
159            Capability::CAP_SETGID => "CAP_SETGID",
160            Capability::CAP_SETUID => "CAP_SETUID",
161            Capability::CAP_SETPCAP => "CAP_SETPCAP",
162            Capability::CAP_LINUX_IMMUTABLE => "CAP_LINUX_IMMUTABLE",
163            Capability::CAP_NET_BIND_SERVICE => "CAP_NET_BIND_SERVICE",
164            Capability::CAP_NET_BROADCAST => "CAP_NET_BROADCAST",
165            Capability::CAP_NET_ADMIN => "CAP_NET_ADMIN",
166            Capability::CAP_NET_RAW => "CAP_NET_RAW",
167            Capability::CAP_IPC_LOCK => "CAP_IPC_LOCK",
168            Capability::CAP_IPC_OWNER => "CAP_IPC_OWNER",
169            Capability::CAP_SYS_MODULE => "CAP_SYS_MODULE",
170            Capability::CAP_SYS_RAWIO => "CAP_SYS_RAWIO",
171            Capability::CAP_SYS_CHROOT => "CAP_SYS_CHROOT",
172            Capability::CAP_SYS_PTRACE => "CAP_SYS_PTRACE",
173            Capability::CAP_SYS_PACCT => "CAP_SYS_PACCT",
174            Capability::CAP_SYS_ADMIN => "CAP_SYS_ADMIN",
175            Capability::CAP_SYS_BOOT => "CAP_SYS_BOOT",
176            Capability::CAP_SYS_NICE => "CAP_SYS_NICE",
177            Capability::CAP_SYS_RESOURCE => "CAP_SYS_RESOURCE",
178            Capability::CAP_SYS_TIME => "CAP_SYS_TIME",
179            Capability::CAP_SYS_TTY_CONFIG => "CAP_SYS_TTY_CONFIG",
180            Capability::CAP_MKNOD => "CAP_MKNOD",
181            Capability::CAP_LEASE => "CAP_LEASE",
182            Capability::CAP_AUDIT_WRITE => "CAP_AUDIT_WRITE",
183            Capability::CAP_AUDIT_CONTROL => "CAP_AUDIT_CONTROL",
184            Capability::CAP_SETFCAP => "CAP_SETFCAP",
185            Capability::CAP_MAC_OVERRIDE => "CAP_MAC_OVERRIDE",
186            Capability::CAP_MAC_ADMIN => "CAP_MAC_ADMIN",
187            Capability::CAP_SYSLOG => "CAP_SYSLOG",
188            Capability::CAP_WAKE_ALARM => "CAP_WAKE_ALARM",
189            Capability::CAP_BLOCK_SUSPEND => "CAP_BLOCK_SUSPEND",
190            Capability::CAP_AUDIT_READ => "CAP_AUDIT_READ",
191            Capability::CAP_PERFMON => "CAP_PERFMON",
192            Capability::CAP_BPF => "CAP_BPF",
193            Capability::CAP_CHECKPOINT_RESTORE => "CAP_CHECKPOINT_RESTORE",
194            Capability::__Nonexhaustive => unreachable!("invalid capability"),
195        };
196        write!(f, "{}", name)
197    }
198}
199
200impl std::str::FromStr for Capability {
201    type Err = CapsError;
202
203    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
204        match s {
205            "CAP_CHOWN" => Ok(Capability::CAP_CHOWN),
206            "CAP_DAC_OVERRIDE" => Ok(Capability::CAP_DAC_OVERRIDE),
207            "CAP_DAC_READ_SEARCH" => Ok(Capability::CAP_DAC_READ_SEARCH),
208            "CAP_FOWNER" => Ok(Capability::CAP_FOWNER),
209            "CAP_FSETID" => Ok(Capability::CAP_FSETID),
210            "CAP_KILL" => Ok(Capability::CAP_KILL),
211            "CAP_SETGID" => Ok(Capability::CAP_SETGID),
212            "CAP_SETUID" => Ok(Capability::CAP_SETUID),
213            "CAP_SETPCAP" => Ok(Capability::CAP_SETPCAP),
214            "CAP_LINUX_IMMUTABLE" => Ok(Capability::CAP_LINUX_IMMUTABLE),
215            "CAP_NET_BIND_SERVICE" => Ok(Capability::CAP_NET_BIND_SERVICE),
216            "CAP_NET_BROADCAST" => Ok(Capability::CAP_NET_BROADCAST),
217            "CAP_NET_ADMIN" => Ok(Capability::CAP_NET_ADMIN),
218            "CAP_NET_RAW" => Ok(Capability::CAP_NET_RAW),
219            "CAP_IPC_LOCK" => Ok(Capability::CAP_IPC_LOCK),
220            "CAP_IPC_OWNER" => Ok(Capability::CAP_IPC_OWNER),
221            "CAP_SYS_MODULE" => Ok(Capability::CAP_SYS_MODULE),
222            "CAP_SYS_RAWIO" => Ok(Capability::CAP_SYS_RAWIO),
223            "CAP_SYS_CHROOT" => Ok(Capability::CAP_SYS_CHROOT),
224            "CAP_SYS_PTRACE" => Ok(Capability::CAP_SYS_PTRACE),
225            "CAP_SYS_PACCT" => Ok(Capability::CAP_SYS_PACCT),
226            "CAP_SYS_ADMIN" => Ok(Capability::CAP_SYS_ADMIN),
227            "CAP_SYS_BOOT" => Ok(Capability::CAP_SYS_BOOT),
228            "CAP_SYS_NICE" => Ok(Capability::CAP_SYS_NICE),
229            "CAP_SYS_RESOURCE" => Ok(Capability::CAP_SYS_RESOURCE),
230            "CAP_SYS_TIME" => Ok(Capability::CAP_SYS_TIME),
231            "CAP_SYS_TTY_CONFIG" => Ok(Capability::CAP_SYS_TTY_CONFIG),
232            "CAP_MKNOD" => Ok(Capability::CAP_MKNOD),
233            "CAP_LEASE" => Ok(Capability::CAP_LEASE),
234            "CAP_AUDIT_WRITE" => Ok(Capability::CAP_AUDIT_WRITE),
235            "CAP_AUDIT_CONTROL" => Ok(Capability::CAP_AUDIT_CONTROL),
236            "CAP_SETFCAP" => Ok(Capability::CAP_SETFCAP),
237            "CAP_MAC_OVERRIDE" => Ok(Capability::CAP_MAC_OVERRIDE),
238            "CAP_MAC_ADMIN" => Ok(Capability::CAP_MAC_ADMIN),
239            "CAP_SYSLOG" => Ok(Capability::CAP_SYSLOG),
240            "CAP_WAKE_ALARM" => Ok(Capability::CAP_WAKE_ALARM),
241            "CAP_BLOCK_SUSPEND" => Ok(Capability::CAP_BLOCK_SUSPEND),
242            "CAP_AUDIT_READ" => Ok(Capability::CAP_AUDIT_READ),
243            "CAP_PERFMON" => Ok(Capability::CAP_PERFMON),
244            "CAP_BPF" => Ok(Capability::CAP_BPF),
245            "CAP_CHECKPOINT_RESTORE" => Ok(Capability::CAP_CHECKPOINT_RESTORE),
246            _ => Err(format!("invalid capability: {}", s).into()),
247        }
248    }
249}
250
251impl Capability {
252    /// Returns the bitmask corresponding to this capability value.
253    #[allow(clippy::trivially_copy_pass_by_ref)]
254    pub fn bitmask(&self) -> u64 {
255        1u64 << (*self as u8)
256    }
257
258    /// Returns the index of this capability, i.e. its kernel-defined value.
259    #[allow(clippy::trivially_copy_pass_by_ref)]
260    pub fn index(&self) -> u8 {
261        *self as u8
262    }
263}
264
265/// An `HashSet` specialized on `Capability`.
266pub type CapsHashSet = std::collections::HashSet<Capability>;
267
268/// Check if a thread contains a capability in a set.
269///
270/// Check if set `cset` for thread `tid` contains capability `cap`.
271/// If `tid` is `None`, this operates on current thread (tid=0).
272/// It cannot check Ambient or Bounding capabilities of other processes.
273pub fn has_cap(tid: Option<i32>, cset: CapSet, cap: Capability) -> Result<bool, CapsError> {
274    let t = tid.unwrap_or(0);
275    match cset {
276        CapSet::Ambient if t == 0 => ambient::has_cap(cap),
277        CapSet::Bounding if t == 0 => bounding::has_cap(cap),
278        CapSet::Effective | CapSet::Inheritable | CapSet::Permitted => base::has_cap(t, cset, cap),
279        _ => Err("operation not supported".into()),
280    }
281}
282
283/// Return all capabilities in a set for a thread.
284///
285/// Return current content of set `cset` for thread `tid`.
286/// If `tid` is `None`, this operates on current thread (tid=0).
287/// It cannot read Ambient or Bounding capabilities of other processes.
288pub fn read(tid: Option<i32>, cset: CapSet) -> Result<CapsHashSet, CapsError> {
289    let t = tid.unwrap_or(0);
290    match cset {
291        CapSet::Ambient if t == 0 => ambient::read(),
292        CapSet::Bounding if t == 0 => bounding::read(),
293        CapSet::Effective | CapSet::Inheritable | CapSet::Permitted => base::read(t, cset),
294        _ => Err("operation not supported".into()),
295    }
296}
297
298/// Set a capability set for a thread to a new value.
299///
300/// All and only capabilities in `value` will be set for set `cset` for thread `tid`.
301/// If `tid` is `None`, this operates on current thread (tid=0).
302/// It cannot manipulate Ambient set of other processes.
303/// Capabilities cannot be set in Bounding set.
304pub fn set(tid: Option<i32>, cset: CapSet, value: &CapsHashSet) -> Result<(), CapsError> {
305    let t = tid.unwrap_or(0);
306    match cset {
307        CapSet::Ambient if t == 0 => ambient::set(value),
308        CapSet::Effective | CapSet::Inheritable | CapSet::Permitted => base::set(t, cset, value),
309        _ => Err("operation not supported".into()),
310    }
311}
312
313/// Clear all capabilities in a set for a thread.
314///
315/// All capabilities will be cleared from set `cset` for thread `tid`.
316/// If `tid` is `None`, this operates on current thread (tid=0).
317/// It cannot manipulate Ambient or Bounding set of other processes.
318pub fn clear(tid: Option<i32>, cset: CapSet) -> Result<(), CapsError> {
319    let t = tid.unwrap_or(0);
320    match cset {
321        CapSet::Ambient if t == 0 => ambient::clear(),
322        CapSet::Bounding if t == 0 => bounding::clear(),
323        CapSet::Effective | CapSet::Permitted | CapSet::Inheritable => base::clear(t, cset),
324        _ => Err("operation not supported".into()),
325    }
326}
327
328/// Raise a single capability in a set for a thread.
329///
330/// Capabilities `cap` will be raised from set `cset` of thread `tid`.
331/// If `tid` is `None`, this operates on current thread (tid=0).
332/// It cannot manipulate Ambient set of other processes.
333/// Capabilities cannot be raised in Bounding set.
334pub fn raise(tid: Option<i32>, cset: CapSet, cap: Capability) -> Result<(), CapsError> {
335    let t = tid.unwrap_or(0);
336    match cset {
337        CapSet::Ambient if t == 0 => ambient::raise(cap),
338        CapSet::Effective | CapSet::Permitted | CapSet::Inheritable => base::raise(t, cset, cap),
339        _ => Err("operation not supported".into()),
340    }
341}
342
343/// Drop a single capability from a set for a thread.
344///
345/// Capabilities `cap` will be dropped from set `cset` of thread `tid`.
346/// If `tid` is `None`, this operates on current thread (tid=0).
347/// It cannot manipulate Ambient and Bounding sets of other processes.
348pub fn drop(tid: Option<i32>, cset: CapSet, cap: Capability) -> Result<(), CapsError> {
349    let t = tid.unwrap_or(0);
350    match cset {
351        CapSet::Ambient if t == 0 => ambient::drop(cap),
352        CapSet::Bounding if t == 0 => bounding::drop(cap),
353        CapSet::Effective | CapSet::Permitted | CapSet::Inheritable => base::drop(t, cset, cap),
354        _ => Err("operation not supported".into()),
355    }
356}
357
358/// Return the set of all capabilities supported by this library.
359pub fn all() -> CapsHashSet {
360    let slice = vec![
361        Capability::CAP_CHOWN,
362        Capability::CAP_DAC_OVERRIDE,
363        Capability::CAP_DAC_READ_SEARCH,
364        Capability::CAP_FOWNER,
365        Capability::CAP_FSETID,
366        Capability::CAP_KILL,
367        Capability::CAP_SETGID,
368        Capability::CAP_SETUID,
369        Capability::CAP_SETPCAP,
370        Capability::CAP_LINUX_IMMUTABLE,
371        Capability::CAP_NET_BIND_SERVICE,
372        Capability::CAP_NET_BROADCAST,
373        Capability::CAP_NET_ADMIN,
374        Capability::CAP_NET_RAW,
375        Capability::CAP_IPC_LOCK,
376        Capability::CAP_IPC_OWNER,
377        Capability::CAP_SYS_MODULE,
378        Capability::CAP_SYS_RAWIO,
379        Capability::CAP_SYS_CHROOT,
380        Capability::CAP_SYS_PTRACE,
381        Capability::CAP_SYS_PACCT,
382        Capability::CAP_SYS_ADMIN,
383        Capability::CAP_SYS_BOOT,
384        Capability::CAP_SYS_NICE,
385        Capability::CAP_SYS_RESOURCE,
386        Capability::CAP_SYS_TIME,
387        Capability::CAP_SYS_TTY_CONFIG,
388        Capability::CAP_MKNOD,
389        Capability::CAP_LEASE,
390        Capability::CAP_AUDIT_WRITE,
391        Capability::CAP_AUDIT_CONTROL,
392        Capability::CAP_SETFCAP,
393        Capability::CAP_MAC_OVERRIDE,
394        Capability::CAP_MAC_ADMIN,
395        Capability::CAP_SYSLOG,
396        Capability::CAP_WAKE_ALARM,
397        Capability::CAP_BLOCK_SUSPEND,
398        Capability::CAP_AUDIT_READ,
399        Capability::CAP_PERFMON,
400        Capability::CAP_BPF,
401        Capability::CAP_CHECKPOINT_RESTORE,
402    ];
403    CapsHashSet::from_iter(slice)
404}
405
406/// Convert an informal capability name into a canonical form.
407///
408/// This converts the input string to uppercase and ensures that it starts with
409/// `CAP_`, prepending it if necessary. It performs no validity checks so the
410/// output may not represent an actual capability. To check if it is, pass it
411/// to [`from_str`].
412///
413/// [`from_str`]: enum.Capability.html#method.from_str
414pub fn to_canonical(name: &str) -> String {
415    let uppername = name.to_uppercase();
416    if uppername.starts_with("CAP_") {
417        uppername
418    } else {
419        ["CAP_", &uppername].concat()
420    }
421}
422
423#[cfg(test)]
424mod tests {
425    use super::*;
426    use std::str::FromStr;
427
428    #[test]
429    fn test_all_roundtrip() {
430        let all = all();
431        assert!(all.len() > 0);
432        for c in all {
433            let name = c.to_string();
434            let parsed: Capability = name.parse().unwrap();
435            assert_eq!(c, parsed);
436        }
437    }
438
439    #[test]
440    fn test_parse_invalid() {
441        let p1 = Capability::from_str("CAP_FOO");
442        let p1_err = p1.unwrap_err();
443        assert!(p1_err.to_string().contains("invalid"));
444        assert!(format!("{}", p1_err).contains("CAP_FOO"));
445        let p2: Result<Capability, CapsError> = "CAP_BAR".parse();
446        assert!(p2.is_err());
447    }
448
449    #[test]
450    fn test_to_canonical() {
451        let p1 = "foo";
452        assert!(Capability::from_str(&to_canonical(p1)).is_err());
453        let p2 = "sys_admin";
454        assert!(Capability::from_str(&to_canonical(p2)).is_ok());
455        let p3 = "CAP_SYS_CHROOT";
456        assert!(Capability::from_str(&to_canonical(p3)).is_ok());
457    }
458
459    #[test]
460    #[cfg(feature = "serde_support")]
461    fn test_serde() {
462        let p1 = Capability::from_str("CAP_CHOWN").unwrap();
463        let ser = serde_json::to_value(&p1).unwrap();
464        let deser: Capability = serde_json::from_value(ser).unwrap();
465        assert_eq!(deser, p1);
466    }
467}