⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/cap/
manager.rs

1//! Global capability manager
2//!
3//! Manages capability creation, delegation, and revocation across the system.
4
5use 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/// Error types for capability operations
22#[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
36/// ID allocator for capability IDs
37struct 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        // Try to reuse a recycled ID first
54        #[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        // Allocate new ID using atomic compare-exchange
64        const MAX_CAP_ID: u64 = (1 << 48) - 1;
65
66        loop {
67            let current = self.next_id.load(Ordering::Relaxed);
68
69            // Check if we've exhausted the ID space
70            if current > MAX_CAP_ID {
71                return Err(CapError::IdExhausted);
72            }
73
74            let next = current + 1;
75
76            // Use compare_exchange_weak for better performance
77            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                    // Another thread updated the counter, retry
86                    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
98/// Registry entry for a capability
99struct RegistryEntry {
100    object: ObjectRef,
101    generation: AtomicU8,
102    revoked: bool,
103}
104
105/// Global capability manager
106pub struct CapabilityManager {
107    /// Global capability registry
108    #[cfg(feature = "alloc")]
109    registry: RwLock<BTreeMap<u64, RegistryEntry>>,
110
111    /// ID allocator
112    id_allocator: IdAllocator,
113
114    /// Global generation counter
115    global_generation: AtomicU8,
116
117    /// Statistics
118    stats: CapManagerStats,
119}
120
121/// Statistics for capability manager
122pub 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
140/// Global capability manager instance
141static 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    /// Create a new capability
164    pub fn create_capability(
165        &self,
166        object: ObjectRef,
167        rights: Rights,
168        cap_space: &CapabilitySpace,
169    ) -> Result<CapabilityToken, CapError> {
170        // Validate object
171        if !object.is_valid() {
172            return Err(CapError::InvalidObject);
173        }
174
175        // Allocate ID
176        let id = self.id_allocator.allocate()?;
177
178        // Create capability token
179        let cap = CapabilityToken::new(
180            id,
181            self.global_generation.load(Ordering::Relaxed),
182            object.type_code(),
183            rights.to_flags(),
184        );
185
186        // Register globally
187        #[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        // Insert into capability space
198        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        // Audit log: capability creation
207        crate::security::audit::log_capability_op(0, id, 0);
208
209        Ok(cap)
210    }
211
212    /// Delegate capability to another capability space
213    pub fn delegate(
214        &self,
215        cap: CapabilityToken,
216        source: &CapabilitySpace,
217        target: &CapabilitySpace,
218        new_rights: Rights,
219    ) -> Result<CapabilityToken, CapError> {
220        // Verify source has the capability
221        let source_rights = source.lookup(cap).ok_or(CapError::InvalidCapability)?;
222
223        // Check grant permission
224        if !source_rights.contains(Rights::GRANT) {
225            return Err(CapError::PermissionDenied);
226        }
227
228        // Get object reference
229        #[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        // Ensure new rights are subset of source rights
245        let derived_rights = source_rights.intersection(new_rights);
246
247        // Create new capability with same ID but potentially different rights
248        let new_cap = CapabilityToken::new(
249            cap.id(),
250            cap.generation(),
251            cap.cap_type(),
252            derived_rights.to_flags(),
253        );
254
255        // Insert into target space
256        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        // Audit log: capability delegation
265        crate::security::audit::log_capability_op(0, cap.id(), 0);
266
267        Ok(new_cap)
268    }
269
270    /// Revoke a capability globally
271    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(()); // Already revoked
281            }
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        // Audit log: capability revocation
292        crate::security::audit::log_capability_op(0, cap.id(), 0);
293
294        // Notify all processes of revocation via the revocation subsystem
295        super::revocation::broadcast_capability_revoked(cap);
296
297        Ok(())
298    }
299
300    /// Delete a capability completely
301    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            // Recycle the ID
310            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    /// Check if a capability is valid (not revoked)
321    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 // Without alloc, we can't track revocation
334    }
335
336    /// Get statistics
337    pub fn stats(&self) -> &CapManagerStats {
338        &self.stats
339    }
340}
341
342/// Get the global capability manager
343pub fn cap_manager() -> &'static CapabilityManager {
344    &CAP_MANAGER
345}
346
347/// Fast inline capability check.
348///
349/// NOTE: There is a theoretical TOCTOU window between the rights check and
350/// the revocation check -- a capability could be revoked between the two
351/// lookups. In practice this window is extremely narrow: the capability
352/// space is RwLock-protected and revocation is a rare administrative
353/// operation. A single atomic check would eliminate this entirely but
354/// requires restructuring the capability space lookup. Documented as a
355/// known limitation.
356#[inline(always)]
357pub fn check_capability(
358    cap: CapabilityToken,
359    required_rights: Rights,
360    cap_space: &CapabilitySpace,
361) -> Result<(), CapError> {
362    // Check if capability exists and has required rights
363    if !cap_space.check_rights(cap, required_rights) {
364        return Err(CapError::InsufficientRights);
365    }
366
367    // Check if not revoked
368    if !cap_manager().is_valid(cap) {
369        return Err(CapError::CapabilityRevoked);
370    }
371
372    Ok(())
373}
374
375/// Capability check macro for system calls
376#[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}