nucleus/security/
capabilities.rs1use crate::error::{NucleusError, Result};
2use caps::{CapSet, Capability, CapsHashSet};
3use tracing::{debug, info};
4
5pub struct CapabilityManager {
7 dropped: bool,
8}
9
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct CapabilitySets {
12 pub bounding: Vec<Capability>,
13 pub permitted: Vec<Capability>,
14 pub effective: Vec<Capability>,
15 pub inheritable: Vec<Capability>,
16 pub ambient: Vec<Capability>,
17}
18
19impl CapabilityManager {
20 pub fn new() -> Self {
21 Self { dropped: false }
22 }
23
24 pub fn drop_all(&mut self) -> Result<()> {
29 if self.dropped {
30 debug!("Capabilities already dropped, skipping");
31 return Ok(());
32 }
33
34 info!("Dropping all capabilities");
35
36 caps::clear(None, CapSet::Permitted).map_err(|e| {
38 NucleusError::CapabilityError(format!("Failed to clear permitted caps: {}", e))
39 })?;
40
41 caps::clear(None, CapSet::Effective).map_err(|e| {
42 NucleusError::CapabilityError(format!("Failed to clear effective caps: {}", e))
43 })?;
44
45 caps::clear(None, CapSet::Inheritable).map_err(|e| {
46 NucleusError::CapabilityError(format!("Failed to clear inheritable caps: {}", e))
47 })?;
48
49 caps::clear(None, CapSet::Ambient).map_err(|e| {
50 NucleusError::CapabilityError(format!("Failed to clear ambient caps: {}", e))
51 })?;
52
53 for cap in caps::all() {
55 if let Err(e) = caps::drop(None, CapSet::Bounding, cap) {
56 debug!(
58 "Failed to drop bounding cap {:?}: {} (may not be present)",
59 cap, e
60 );
61 }
62 }
63
64 self.dropped = true;
65 info!("Successfully dropped all capabilities (including bounding set)");
66
67 Ok(())
68 }
69
70 pub fn drop_except(&mut self, keep: &[Capability]) -> Result<()> {
75 if self.dropped {
76 debug!("Capabilities already dropped, skipping");
77 return Ok(());
78 }
79
80 info!("Dropping capabilities except: {:?}", keep);
81
82 let all_caps = caps::all();
84
85 for cap in all_caps {
87 if !keep.contains(&cap) {
88 caps::drop(None, CapSet::Permitted, cap).map_err(|e| {
89 NucleusError::CapabilityError(format!("Failed to drop {cap:?}: {e}"))
90 })?;
91
92 caps::drop(None, CapSet::Effective, cap).map_err(|e| {
93 NucleusError::CapabilityError(format!("Failed to drop {cap:?}: {e}"))
94 })?;
95
96 caps::drop(None, CapSet::Inheritable, cap).map_err(|e| {
97 NucleusError::CapabilityError(format!("Failed to drop {cap:?}: {e}"))
98 })?;
99
100 if let Err(e) = caps::drop(None, CapSet::Bounding, cap) {
101 debug!(
102 "Failed to drop bounding cap {:?}: {} (may not be present)",
103 cap, e
104 );
105 }
106 }
107 }
108
109 caps::clear(None, CapSet::Ambient).map_err(|e| {
111 NucleusError::CapabilityError(format!("Failed to clear ambient caps: {}", e))
112 })?;
113
114 self.dropped = true;
115 info!("Successfully dropped capabilities");
116
117 Ok(())
118 }
119
120 pub fn apply_sets(&mut self, sets: &CapabilitySets) -> Result<()> {
125 if self.dropped {
126 debug!("Capabilities already dropped, skipping");
127 return Ok(());
128 }
129
130 info!("Applying explicit capability sets");
131
132 for cap in caps::all() {
133 if !sets.bounding.contains(&cap) {
134 if let Err(e) = caps::drop(None, CapSet::Bounding, cap) {
135 debug!(
136 "Failed to drop bounding cap {:?}: {} (may not be present)",
137 cap, e
138 );
139 }
140 }
141 }
142
143 caps::set(None, CapSet::Permitted, &to_caps_hash_set(&sets.permitted)).map_err(|e| {
144 NucleusError::CapabilityError(format!("Failed to set permitted caps: {}", e))
145 })?;
146 caps::set(
147 None,
148 CapSet::Inheritable,
149 &to_caps_hash_set(&sets.inheritable),
150 )
151 .map_err(|e| {
152 NucleusError::CapabilityError(format!("Failed to set inheritable caps: {}", e))
153 })?;
154 caps::set(None, CapSet::Ambient, &to_caps_hash_set(&sets.ambient)).map_err(|e| {
155 NucleusError::CapabilityError(format!("Failed to set ambient caps: {}", e))
156 })?;
157 caps::set(None, CapSet::Effective, &to_caps_hash_set(&sets.effective)).map_err(|e| {
158 NucleusError::CapabilityError(format!("Failed to set effective caps: {}", e))
159 })?;
160
161 self.dropped = true;
162 info!("Successfully applied capability sets");
163 Ok(())
164 }
165
166 pub fn is_dropped(&self) -> bool {
168 self.dropped
169 }
170}
171
172impl Default for CapabilityManager {
173 fn default() -> Self {
174 Self::new()
175 }
176}
177
178fn to_caps_hash_set(caps_list: &[Capability]) -> CapsHashSet {
179 caps_list.iter().copied().collect()
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185
186 #[test]
187 fn test_capability_manager_initial_state() {
188 let mgr = CapabilityManager::new();
189 assert!(!mgr.is_dropped());
190 }
191
192 #[test]
193 fn test_drop_idempotent() {
194 let mut mgr = CapabilityManager::new();
195 let _ = mgr.drop_all();
197 assert!(mgr.is_dropped());
198
199 let result = mgr.drop_all();
201 assert!(result.is_ok());
202 assert!(mgr.is_dropped());
203 }
204}