1use core::sync::atomic::{AtomicU64, AtomicU8, Ordering};
6
7use super::{
8 object::ObjectRef,
9 space::CapabilitySpace,
10 token::{CapabilityToken, Rights},
11};
12
13#[cfg(feature = "alloc")]
14extern crate alloc;
15
16#[cfg(feature = "alloc")]
17use alloc::collections::{BTreeMap, BTreeSet};
18
19use spin::RwLock;
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum CapError {
24 InvalidCapability,
25 InsufficientRights,
26 CapabilityRevoked,
27 OutOfMemory,
28 InvalidObject,
29 PermissionDenied,
30 AlreadyExists,
31 NotFound,
32 IdExhausted,
33 QuotaExceeded,
34}
35
36struct IdAllocator {
38 next_id: AtomicU64,
39 #[cfg(feature = "alloc")]
40 recycled: RwLock<BTreeSet<u64>>,
41}
42
43impl IdAllocator {
44 fn new() -> Self {
45 Self {
46 next_id: AtomicU64::new(1),
47 #[cfg(feature = "alloc")]
48 recycled: RwLock::new(BTreeSet::new()),
49 }
50 }
51
52 fn allocate(&self) -> Result<u64, CapError> {
53 #[cfg(feature = "alloc")]
55 {
56 let mut recycled = self.recycled.write();
57 if let Some(&id) = recycled.iter().next() {
58 recycled.remove(&id);
59 return Ok(id);
60 }
61 }
62
63 const MAX_CAP_ID: u64 = (1 << 48) - 1;
65
66 loop {
67 let current = self.next_id.load(Ordering::Relaxed);
68
69 if current > MAX_CAP_ID {
71 return Err(CapError::IdExhausted);
72 }
73
74 let next = current + 1;
75
76 match self.next_id.compare_exchange_weak(
78 current,
79 next,
80 Ordering::Release,
81 Ordering::Relaxed,
82 ) {
83 Ok(_) => return Ok(current),
84 Err(_) => {
85 continue;
87 }
88 }
89 }
90 }
91
92 #[cfg(feature = "alloc")]
93 fn recycle(&self, id: u64) {
94 self.recycled.write().insert(id);
95 }
96}
97
98struct RegistryEntry {
100 object: ObjectRef,
101 generation: AtomicU8,
102 revoked: bool,
103}
104
105pub struct CapabilityManager {
107 #[cfg(feature = "alloc")]
109 registry: RwLock<BTreeMap<u64, RegistryEntry>>,
110
111 id_allocator: IdAllocator,
113
114 global_generation: AtomicU8,
116
117 stats: CapManagerStats,
119}
120
121pub struct CapManagerStats {
123 pub capabilities_created: AtomicU64,
124 pub capabilities_delegated: AtomicU64,
125 pub capabilities_revoked: AtomicU64,
126 pub capabilities_deleted: AtomicU64,
127}
128
129impl Default for CapManagerStats {
130 fn default() -> Self {
131 Self {
132 capabilities_created: AtomicU64::new(0),
133 capabilities_delegated: AtomicU64::new(0),
134 capabilities_revoked: AtomicU64::new(0),
135 capabilities_deleted: AtomicU64::new(0),
136 }
137 }
138}
139
140static CAP_MANAGER: CapabilityManager = CapabilityManager::new();
142
143impl CapabilityManager {
144 const fn new() -> Self {
145 Self {
146 #[cfg(feature = "alloc")]
147 registry: RwLock::new(BTreeMap::new()),
148 id_allocator: IdAllocator {
149 next_id: AtomicU64::new(1),
150 #[cfg(feature = "alloc")]
151 recycled: RwLock::new(BTreeSet::new()),
152 },
153 global_generation: AtomicU8::new(0),
154 stats: CapManagerStats {
155 capabilities_created: AtomicU64::new(0),
156 capabilities_delegated: AtomicU64::new(0),
157 capabilities_revoked: AtomicU64::new(0),
158 capabilities_deleted: AtomicU64::new(0),
159 },
160 }
161 }
162
163 pub fn create_capability(
165 &self,
166 object: ObjectRef,
167 rights: Rights,
168 cap_space: &CapabilitySpace,
169 ) -> Result<CapabilityToken, CapError> {
170 if !object.is_valid() {
172 return Err(CapError::InvalidObject);
173 }
174
175 let id = self.id_allocator.allocate()?;
177
178 let cap = CapabilityToken::new(
180 id,
181 self.global_generation.load(Ordering::Relaxed),
182 object.type_code(),
183 rights.to_flags(),
184 );
185
186 #[cfg(feature = "alloc")]
188 {
189 let entry = RegistryEntry {
190 object: object.clone(),
191 generation: AtomicU8::new(0),
192 revoked: false,
193 };
194 self.registry.write().insert(id, entry);
195 }
196
197 cap_space
199 .insert(cap, object, rights)
200 .map_err(|_| CapError::OutOfMemory)?;
201
202 self.stats
203 .capabilities_created
204 .fetch_add(1, Ordering::Relaxed);
205
206 crate::security::audit::log_capability_op(0, id, 0);
208
209 Ok(cap)
210 }
211
212 pub fn delegate(
214 &self,
215 cap: CapabilityToken,
216 source: &CapabilitySpace,
217 target: &CapabilitySpace,
218 new_rights: Rights,
219 ) -> Result<CapabilityToken, CapError> {
220 let source_rights = source.lookup(cap).ok_or(CapError::InvalidCapability)?;
222
223 if !source_rights.contains(Rights::GRANT) {
225 return Err(CapError::PermissionDenied);
226 }
227
228 #[cfg(feature = "alloc")]
230 let object = {
231 let registry = self.registry.read();
232 let entry = registry.get(&cap.id()).ok_or(CapError::InvalidCapability)?;
233
234 if entry.revoked {
235 return Err(CapError::CapabilityRevoked);
236 }
237
238 entry.object.clone()
239 };
240
241 #[cfg(not(feature = "alloc"))]
242 return Err(CapError::OutOfMemory);
243
244 let derived_rights = source_rights.intersection(new_rights);
246
247 let new_cap = CapabilityToken::new(
249 cap.id(),
250 cap.generation(),
251 cap.cap_type(),
252 derived_rights.to_flags(),
253 );
254
255 target
257 .insert(new_cap, object, derived_rights)
258 .map_err(|_| CapError::OutOfMemory)?;
259
260 self.stats
261 .capabilities_delegated
262 .fetch_add(1, Ordering::Relaxed);
263
264 crate::security::audit::log_capability_op(0, cap.id(), 0);
266
267 Ok(new_cap)
268 }
269
270 pub fn revoke(&self, cap: CapabilityToken) -> Result<(), CapError> {
272 #[cfg(feature = "alloc")]
273 {
274 let mut registry = self.registry.write();
275 let entry = registry
276 .get_mut(&cap.id())
277 .ok_or(CapError::InvalidCapability)?;
278
279 if entry.revoked {
280 return Ok(()); }
282
283 entry.revoked = true;
284 entry.generation.fetch_add(1, Ordering::SeqCst);
285 }
286
287 self.stats
288 .capabilities_revoked
289 .fetch_add(1, Ordering::Relaxed);
290
291 crate::security::audit::log_capability_op(0, cap.id(), 0);
293
294 super::revocation::broadcast_capability_revoked(cap);
296
297 Ok(())
298 }
299
300 pub fn delete(&self, cap: CapabilityToken) -> Result<(), CapError> {
302 #[cfg(feature = "alloc")]
303 {
304 self.registry
305 .write()
306 .remove(&cap.id())
307 .ok_or(CapError::NotFound)?;
308
309 self.id_allocator.recycle(cap.id());
311 }
312
313 self.stats
314 .capabilities_deleted
315 .fetch_add(1, Ordering::Relaxed);
316
317 Ok(())
318 }
319
320 pub fn is_valid(&self, cap: CapabilityToken) -> bool {
322 #[cfg(feature = "alloc")]
323 {
324 let registry = self.registry.read();
325 if let Some(entry) = registry.get(&cap.id()) {
326 !entry.revoked && entry.generation.load(Ordering::Relaxed) == cap.generation()
327 } else {
328 false
329 }
330 }
331
332 #[cfg(not(feature = "alloc"))]
333 true }
335
336 pub fn stats(&self) -> &CapManagerStats {
338 &self.stats
339 }
340}
341
342pub fn cap_manager() -> &'static CapabilityManager {
344 &CAP_MANAGER
345}
346
347#[inline(always)]
357pub fn check_capability(
358 cap: CapabilityToken,
359 required_rights: Rights,
360 cap_space: &CapabilitySpace,
361) -> Result<(), CapError> {
362 if !cap_space.check_rights(cap, required_rights) {
364 return Err(CapError::InsufficientRights);
365 }
366
367 if !cap_manager().is_valid(cap) {
369 return Err(CapError::CapabilityRevoked);
370 }
371
372 Ok(())
373}
374
375#[macro_export]
377macro_rules! require_capability {
378 ($cap:expr, $rights:expr, $cap_space:expr) => {
379 $crate::cap::manager::check_capability($cap, $rights, $cap_space)?
380 };
381}