Skip to main content

capsec_core/
cap_provider.rs

1//! The [`CapProvider<P>`] trait — unified capability access with optional scope enforcement.
2//!
3//! `CapProvider<P>` generalizes [`Has<P>`](crate::has::Has) to support both unscoped
4//! capabilities (which always succeed) and scoped capabilities
5//! ([`Attenuated<P, S>`](crate::attenuate::Attenuated)) which check the target against
6//! a scope before granting access.
7
8use crate::cap::Cap;
9use crate::error::CapSecError;
10use crate::permission::Permission;
11
12/// A type that can provide a capability token for permission `P`, possibly
13/// after performing a scope check against the target.
14pub trait CapProvider<P: Permission> {
15    /// Provides a `Cap<P>` for the given target, or returns an error if the
16    /// target is outside the capability's scope.
17    #[must_use = "ignoring a capability check silently discards scope violations"]
18    fn provide_cap(&self, target: &str) -> Result<Cap<P>, CapSecError>;
19}
20
21// We cannot use a blanket `impl<T: Has<P>> CapProvider<P> for T` because
22// Rust's coherence rules can't prove Attenuated won't impl Has in the future.
23// Instead, we require types to impl CapProvider explicitly.
24// The #[capsec::context] macro and built-in types all get impls.
25// For user-defined Has<P> impls, provide a convenience macro.
26
27/// Implement `CapProvider<P>` for a type that already implements `Has<P>`.
28///
29/// Since `Has<P>` is infallible, the implementation always returns `Ok`
30/// and ignores the target string.
31///
32/// # Example
33///
34/// ```rust,ignore
35/// struct MyCtx { cap: Cap<FsRead> }
36/// impl Has<FsRead> for MyCtx { fn cap_ref(&self) -> Cap<FsRead> { self.cap.clone() } }
37/// capsec_core::impl_cap_provider_for_has!(MyCtx, FsRead);
38/// ```
39#[macro_export]
40macro_rules! impl_cap_provider_for_has {
41    ($ty:ty, $perm:ty) => {
42        impl $crate::cap_provider::CapProvider<$perm> for $ty {
43            fn provide_cap(
44                &self,
45                _target: &str,
46            ) -> Result<$crate::cap::Cap<$perm>, $crate::error::CapSecError> {
47                Ok(<Self as $crate::has::Has<$perm>>::cap_ref(self))
48            }
49        }
50    };
51}
52
53// ── Built-in impls: Cap<P>, SendCap<P> ─────────────────────────────
54
55impl<P: Permission> CapProvider<P> for Cap<P> {
56    fn provide_cap(&self, _target: &str) -> Result<Cap<P>, CapSecError> {
57        Ok(Cap::new())
58    }
59}
60
61impl<P: Permission> CapProvider<P> for crate::cap::SendCap<P> {
62    fn provide_cap(&self, _target: &str) -> Result<Cap<P>, CapSecError> {
63        Ok(Cap::new())
64    }
65}
66
67// ── Subsumption (Cap) ────────────────────────────────────────────────
68
69macro_rules! impl_cap_provider_subsumes {
70    ($super:ty => $($sub:ty),+) => {
71        $(
72            impl CapProvider<$sub> for Cap<$super> {
73                fn provide_cap(&self, _target: &str) -> Result<Cap<$sub>, CapSecError> {
74                    Ok(Cap::new())
75                }
76            }
77        )+
78    }
79}
80
81use crate::permission::*;
82
83impl_cap_provider_subsumes!(FsAll => FsRead, FsWrite);
84impl_cap_provider_subsumes!(NetAll => NetConnect, NetBind);
85
86// ── Subsumption (SendCap) ───────────────────────────────────────────
87
88macro_rules! impl_cap_provider_sendcap_subsumes {
89    ($super:ty => $($sub:ty),+) => {
90        $(
91            impl CapProvider<$sub> for crate::cap::SendCap<$super> {
92                fn provide_cap(&self, _target: &str) -> Result<Cap<$sub>, CapSecError> {
93                    Ok(Cap::new())
94                }
95            }
96        )+
97    }
98}
99
100impl_cap_provider_sendcap_subsumes!(FsAll => FsRead, FsWrite);
101impl_cap_provider_sendcap_subsumes!(NetAll => NetConnect, NetBind);
102
103// ── Ambient (Cap) ───────────────────────────────────────────────────
104
105macro_rules! impl_cap_provider_ambient {
106    ($($perm:ty),+) => {
107        $(
108            impl CapProvider<$perm> for Cap<Ambient> {
109                fn provide_cap(&self, _target: &str) -> Result<Cap<$perm>, CapSecError> {
110                    Ok(Cap::new())
111                }
112            }
113        )+
114    }
115}
116
117impl_cap_provider_ambient!(
118    FsRead, FsWrite, FsAll, NetConnect, NetBind, NetAll, EnvRead, EnvWrite, Spawn
119);
120
121// ── Ambient (SendCap) ───────────────────────────────────────────────
122
123macro_rules! impl_cap_provider_sendcap_ambient {
124    ($($perm:ty),+) => {
125        $(
126            impl CapProvider<$perm> for crate::cap::SendCap<Ambient> {
127                fn provide_cap(&self, _target: &str) -> Result<Cap<$perm>, CapSecError> {
128                    Ok(Cap::new())
129                }
130            }
131        )+
132    }
133}
134
135impl_cap_provider_sendcap_ambient!(
136    FsRead, FsWrite, FsAll, NetConnect, NetBind, NetAll, EnvRead, EnvWrite, Spawn
137);
138
139// ── Tuples (Cap) ────────────────────────────────────────────────────
140
141macro_rules! impl_cap_provider_tuple_first {
142    ([$($a:ident),+]; $all:tt) => {
143        $( impl_cap_provider_tuple_first!(@inner $a; $all); )+
144    };
145    (@inner $a:ident; [$($b:ident),+]) => {
146        $(
147            impl CapProvider<$a> for Cap<($a, $b)> {
148                fn provide_cap(&self, _target: &str) -> Result<Cap<$a>, CapSecError> {
149                    Ok(Cap::new())
150                }
151            }
152        )+
153    };
154}
155
156macro_rules! impl_cap_provider_tuple_second {
157    ($first:ident $(, $rest:ident)+) => {
158        $(
159            impl CapProvider<$first> for Cap<($rest, $first)> {
160                fn provide_cap(&self, _target: &str) -> Result<Cap<$first>, CapSecError> {
161                    Ok(Cap::new())
162                }
163            }
164            impl CapProvider<$rest> for Cap<($first, $rest)> {
165                fn provide_cap(&self, _target: &str) -> Result<Cap<$rest>, CapSecError> {
166                    Ok(Cap::new())
167                }
168            }
169        )+
170        impl_cap_provider_tuple_second!($($rest),+);
171    };
172    ($single:ident) => {};
173}
174
175impl_cap_provider_tuple_first!(
176    [FsRead, FsWrite, FsAll, NetConnect, NetBind, NetAll, EnvRead, EnvWrite, Spawn, Ambient];
177    [FsRead, FsWrite, FsAll, NetConnect, NetBind, NetAll, EnvRead, EnvWrite, Spawn, Ambient]
178);
179
180impl_cap_provider_tuple_second!(
181    FsRead, FsWrite, FsAll, NetConnect, NetBind, NetAll, EnvRead, EnvWrite, Spawn, Ambient
182);
183
184// ── Tuples (SendCap) ────────────────────────────────────────────────
185
186macro_rules! impl_cap_provider_sendcap_tuple_first {
187    ([$($a:ident),+]; $all:tt) => {
188        $( impl_cap_provider_sendcap_tuple_first!(@inner $a; $all); )+
189    };
190    (@inner $a:ident; [$($b:ident),+]) => {
191        $(
192            impl CapProvider<$a> for crate::cap::SendCap<($a, $b)> {
193                fn provide_cap(&self, _target: &str) -> Result<Cap<$a>, CapSecError> {
194                    Ok(Cap::new())
195                }
196            }
197        )+
198    };
199}
200
201macro_rules! impl_cap_provider_sendcap_tuple_second {
202    ($first:ident $(, $rest:ident)+) => {
203        $(
204            impl CapProvider<$first> for crate::cap::SendCap<($rest, $first)> {
205                fn provide_cap(&self, _target: &str) -> Result<Cap<$first>, CapSecError> {
206                    Ok(Cap::new())
207                }
208            }
209            impl CapProvider<$rest> for crate::cap::SendCap<($first, $rest)> {
210                fn provide_cap(&self, _target: &str) -> Result<Cap<$rest>, CapSecError> {
211                    Ok(Cap::new())
212                }
213            }
214        )+
215        impl_cap_provider_sendcap_tuple_second!($($rest),+);
216    };
217    ($single:ident) => {};
218}
219
220impl_cap_provider_sendcap_tuple_first!(
221    [FsRead, FsWrite, FsAll, NetConnect, NetBind, NetAll, EnvRead, EnvWrite, Spawn, Ambient];
222    [FsRead, FsWrite, FsAll, NetConnect, NetBind, NetAll, EnvRead, EnvWrite, Spawn, Ambient]
223);
224
225impl_cap_provider_sendcap_tuple_second!(
226    FsRead, FsWrite, FsAll, NetConnect, NetBind, NetAll, EnvRead, EnvWrite, Spawn, Ambient
227);
228
229// ── Runtime / Prescript types ───────────────────────────────────────
230
231impl<P: Permission> CapProvider<P> for crate::runtime::RuntimeCap<P> {
232    fn provide_cap(&self, _target: &str) -> Result<Cap<P>, CapSecError> {
233        self.try_cap()
234    }
235}
236
237impl<P: Permission> CapProvider<P> for crate::runtime::RuntimeSendCap<P> {
238    fn provide_cap(&self, _target: &str) -> Result<Cap<P>, CapSecError> {
239        self.try_cap()
240    }
241}
242
243impl<P: Permission> CapProvider<P> for crate::runtime::TimedCap<P> {
244    fn provide_cap(&self, _target: &str) -> Result<Cap<P>, CapSecError> {
245        self.try_cap()
246    }
247}
248
249impl<P: Permission> CapProvider<P> for crate::runtime::TimedSendCap<P> {
250    fn provide_cap(&self, _target: &str) -> Result<Cap<P>, CapSecError> {
251        self.try_cap()
252    }
253}
254
255impl<P: Permission> CapProvider<P> for crate::prescript::LoggedCap<P> {
256    fn provide_cap(&self, _target: &str) -> Result<Cap<P>, CapSecError> {
257        self.try_cap()
258    }
259}
260
261impl<P: Permission> CapProvider<P> for crate::prescript::LoggedSendCap<P> {
262    fn provide_cap(&self, _target: &str) -> Result<Cap<P>, CapSecError> {
263        self.try_cap()
264    }
265}
266
267impl<P: Permission> CapProvider<P> for crate::prescript::DualKeyCap<P> {
268    fn provide_cap(&self, _target: &str) -> Result<Cap<P>, CapSecError> {
269        self.try_cap()
270    }
271}
272
273impl<P: Permission> CapProvider<P> for crate::prescript::DualKeySendCap<P> {
274    fn provide_cap(&self, _target: &str) -> Result<Cap<P>, CapSecError> {
275        self.try_cap()
276    }
277}
278
279#[cfg(test)]
280mod tests {
281    use super::*;
282
283    #[test]
284    fn cap_provides() {
285        let root = crate::root::test_root();
286        let cap = root.grant::<FsRead>();
287        assert!(cap.provide_cap("/any").is_ok());
288    }
289
290    #[test]
291    fn sendcap_provides() {
292        let root = crate::root::test_root();
293        let cap = root.grant::<FsRead>().make_send();
294        assert!(cap.provide_cap("/any").is_ok());
295    }
296
297    #[test]
298    fn subsumption_provides() {
299        let root = crate::root::test_root();
300        let cap = root.grant::<FsAll>();
301        let result: Result<Cap<FsRead>, _> = cap.provide_cap("/any");
302        assert!(result.is_ok());
303    }
304
305    #[test]
306    fn ambient_provides() {
307        let root = crate::root::test_root();
308        let cap = root.grant::<Ambient>();
309        let result: Result<Cap<FsRead>, _> = cap.provide_cap("/any");
310        assert!(result.is_ok());
311    }
312
313    #[test]
314    fn tuple_provides() {
315        let root = crate::root::test_root();
316        let cap = root.grant::<(FsRead, NetConnect)>();
317        assert!(CapProvider::<FsRead>::provide_cap(&cap, "/any").is_ok());
318        assert!(CapProvider::<NetConnect>::provide_cap(&cap, "host").is_ok());
319    }
320
321    #[test]
322    fn sendcap_subsumption_provides() {
323        let root = crate::root::test_root();
324        let cap = root.grant::<FsAll>().make_send();
325        let result: Result<Cap<FsRead>, _> = cap.provide_cap("/any");
326        assert!(result.is_ok());
327    }
328
329    #[test]
330    fn sendcap_ambient_provides() {
331        let root = crate::root::test_root();
332        let cap = root.grant::<Ambient>().make_send();
333        let result: Result<Cap<FsRead>, _> = cap.provide_cap("/any");
334        assert!(result.is_ok());
335    }
336
337    #[test]
338    fn sendcap_tuple_provides() {
339        let root = crate::root::test_root();
340        let cap = root.grant::<(FsRead, NetConnect)>().make_send();
341        assert!(CapProvider::<FsRead>::provide_cap(&cap, "/any").is_ok());
342        assert!(CapProvider::<NetConnect>::provide_cap(&cap, "host").is_ok());
343    }
344
345    #[test]
346    fn runtime_cap_provides() {
347        let root = crate::root::test_root();
348        let cap = root.grant::<FsRead>();
349        let (rcap, _revoker) = crate::runtime::RuntimeCap::new(cap);
350        assert!(rcap.provide_cap("/any").is_ok());
351    }
352
353    #[test]
354    fn runtime_cap_provides_fails_after_revocation() {
355        let root = crate::root::test_root();
356        let cap = root.grant::<FsRead>();
357        let (rcap, revoker) = crate::runtime::RuntimeCap::new(cap);
358        revoker.revoke();
359        assert!(rcap.provide_cap("/any").is_err());
360    }
361
362    #[test]
363    fn timed_cap_provides() {
364        let root = crate::root::test_root();
365        let cap = root.grant::<FsRead>();
366        let tcap = crate::runtime::TimedCap::new(cap, std::time::Duration::from_secs(60));
367        assert!(tcap.provide_cap("/any").is_ok());
368    }
369
370    #[test]
371    fn logged_cap_provides() {
372        let root = crate::root::test_root();
373        let cap = root.grant::<FsRead>();
374        let lcap = crate::prescript::LoggedCap::new(cap);
375        assert!(lcap.provide_cap("/any").is_ok());
376    }
377
378    #[test]
379    fn dual_key_cap_provides() {
380        let root = crate::root::test_root();
381        let cap = root.grant::<FsRead>();
382        let (dcap, a, b) = crate::prescript::DualKeyCap::new(cap);
383        a.approve();
384        b.approve();
385        assert!(dcap.provide_cap("/any").is_ok());
386    }
387
388    #[test]
389    fn dual_key_cap_provides_fails_without_approvals() {
390        let root = crate::root::test_root();
391        let cap = root.grant::<FsRead>();
392        let (dcap, _a, _b) = crate::prescript::DualKeyCap::new(cap);
393        assert!(dcap.provide_cap("/any").is_err());
394    }
395}