capsec_core/permission.rs
1//! The [`Permission`] trait and all built-in permission types.
2//!
3//! Permissions are zero-sized marker types that encode what kind of I/O a
4//! capability token grants. Built-in permissions cover filesystem, network,
5//! environment, and process operations. Library authors can define custom
6//! permissions using `#[capsec::permission]`.
7//!
8//! # Built-in permissions
9//!
10//! | Type | Category | What it grants |
11//! |------|----------|----------------|
12//! | [`FsRead`] | Filesystem | Read files, list directories, check metadata |
13//! | [`FsWrite`] | Filesystem | Write, create, delete files and directories |
14//! | [`FsAll`] | Filesystem | All filesystem operations (subsumes `FsRead` + `FsWrite`) |
15//! | [`NetConnect`] | Network | Open outbound TCP/UDP connections |
16//! | [`NetBind`] | Network | Bind listeners and sockets to local ports |
17//! | [`NetAll`] | Network | All network operations (subsumes `NetConnect` + `NetBind`) |
18//! | [`EnvRead`] | Environment | Read environment variables |
19//! | [`EnvWrite`] | Environment | Modify or remove environment variables |
20//! | [`Spawn`] | Process | Execute subprocesses |
21//! | [`Ambient`] | Everything | Full ambient authority — the "god token" |
22//!
23//! # Custom permissions
24//!
25//! Use `#[capsec::permission]` to define domain-specific permissions:
26//!
27//! ```rust,ignore
28//! #[capsec::permission]
29//! pub struct DbRead;
30//!
31//! #[capsec::permission(subsumes = [DbRead])]
32//! pub struct DbAll;
33//! ```
34//!
35//! # Tuples
36//!
37//! Two permissions can be bundled via a tuple: `(FsRead, NetConnect)` is itself
38//! a `Permission`, and `Cap<(FsRead, NetConnect)>` satisfies both `Has<FsRead>`
39//! and `Has<NetConnect>`. All 2-tuple combinations of built-in permissions are
40//! supported.
41//!
42//! # Subsumption
43//!
44//! Some permissions imply others. [`FsAll`] subsumes both [`FsRead`] and [`FsWrite`],
45//! meaning a `Cap<FsAll>` can be used anywhere a `Cap<FsRead>` is required.
46//! [`Ambient`] subsumes everything.
47
48/// Marker trait for all capability permissions.
49///
50/// Every permission type is a zero-sized struct that implements this trait.
51/// Built-in permissions are defined in this module. Custom permissions can be
52/// defined using the `#[capsec::permission]` derive macro, which generates the
53/// required seal token.
54///
55/// # Direct implementation
56///
57/// Do not implement this trait manually. Use `#[capsec::permission]` instead.
58/// The `__CapsecSeal` associated type is `#[doc(hidden)]` and may change
59/// without notice.
60pub trait Permission: 'static {
61 /// Seal token preventing manual implementation. Do not use directly.
62 #[doc(hidden)]
63 type __CapsecSeal: __private::SealToken;
64}
65
66// Filesystem
67
68/// Permission to read files, list directories, and check metadata.
69pub struct FsRead;
70
71/// Permission to write, create, rename, and delete files and directories.
72pub struct FsWrite;
73
74/// Permission for all filesystem operations. Subsumes [`FsRead`] and [`FsWrite`].
75pub struct FsAll;
76
77// Network
78
79/// Permission to open outbound TCP and UDP connections.
80pub struct NetConnect;
81
82/// Permission to bind TCP listeners and UDP sockets to local ports.
83pub struct NetBind;
84
85/// Permission for all network operations. Subsumes [`NetConnect`] and [`NetBind`].
86pub struct NetAll;
87
88// Environment
89
90/// Permission to read environment variables.
91pub struct EnvRead;
92
93/// Permission to modify or remove environment variables.
94pub struct EnvWrite;
95
96// Process
97
98/// Permission to spawn and execute subprocesses via `std::process::Command`.
99pub struct Spawn;
100
101// Ambient
102
103/// Full ambient authority — grants every permission.
104///
105/// This is the "god token." A `Cap<Ambient>` satisfies any `Has<P>` bound.
106/// Use sparingly and only at the capability root.
107pub struct Ambient;
108
109// Permission impls
110
111impl Permission for FsRead {
112 type __CapsecSeal = __private::SealProof;
113}
114impl Permission for FsWrite {
115 type __CapsecSeal = __private::SealProof;
116}
117impl Permission for FsAll {
118 type __CapsecSeal = __private::SealProof;
119}
120impl Permission for NetConnect {
121 type __CapsecSeal = __private::SealProof;
122}
123impl Permission for NetBind {
124 type __CapsecSeal = __private::SealProof;
125}
126impl Permission for NetAll {
127 type __CapsecSeal = __private::SealProof;
128}
129impl Permission for EnvRead {
130 type __CapsecSeal = __private::SealProof;
131}
132impl Permission for EnvWrite {
133 type __CapsecSeal = __private::SealProof;
134}
135impl Permission for Spawn {
136 type __CapsecSeal = __private::SealProof;
137}
138impl Permission for Ambient {
139 type __CapsecSeal = __private::SealProof;
140}
141
142// Tuple permissions
143
144impl<A: Permission, B: Permission> Permission for (A, B) {
145 type __CapsecSeal = __private::SealProof;
146}
147
148// Subsumption
149
150/// Indicates that `Self` implies permission `P`.
151///
152/// When `Super: Subsumes<Sub>`, a `Cap<Super>` can satisfy `Has<Sub>`.
153/// For example, `FsAll: Subsumes<FsRead>` means `Cap<FsAll>` works
154/// anywhere `Has<FsRead>` is required.
155pub trait Subsumes<P: Permission>: Permission {}
156
157impl Subsumes<FsRead> for FsAll {}
158impl Subsumes<FsWrite> for FsAll {}
159impl Subsumes<NetConnect> for NetAll {}
160impl Subsumes<NetBind> for NetAll {}
161impl<P: Permission> Subsumes<P> for Ambient {}
162
163// Seal token — prevents manual Permission implementation.
164// The #[capsec::permission] macro generates the correct seal.
165// Users could write this by hand (it's #[doc(hidden)], not private),
166// but that's equivalent to writing `unsafe` — they own the consequences.
167
168#[doc(hidden)]
169pub mod __private {
170 /// Proof token that a permission was registered via the capsec derive macro.
171 pub struct SealProof;
172
173 /// Trait bound for the seal associated type.
174 pub trait SealToken {}
175 impl SealToken for SealProof {}
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181 use std::mem::size_of;
182
183 #[test]
184 fn all_permissions_are_zst() {
185 assert_eq!(size_of::<FsRead>(), 0);
186 assert_eq!(size_of::<FsWrite>(), 0);
187 assert_eq!(size_of::<FsAll>(), 0);
188 assert_eq!(size_of::<NetConnect>(), 0);
189 assert_eq!(size_of::<NetBind>(), 0);
190 assert_eq!(size_of::<NetAll>(), 0);
191 assert_eq!(size_of::<EnvRead>(), 0);
192 assert_eq!(size_of::<EnvWrite>(), 0);
193 assert_eq!(size_of::<Spawn>(), 0);
194 assert_eq!(size_of::<Ambient>(), 0);
195 }
196
197 // Compile-time proof that subsumption relationships hold:
198 fn _assert_subsumes<Super: Subsumes<Sub>, Sub: Permission>() {}
199
200 #[test]
201 fn tuple_permission_is_zst() {
202 assert_eq!(size_of::<(FsRead, NetConnect)>(), 0);
203 }
204
205 #[test]
206 fn subsumption_relationships() {
207 _assert_subsumes::<FsAll, FsRead>();
208 _assert_subsumes::<FsAll, FsWrite>();
209 _assert_subsumes::<NetAll, NetConnect>();
210 _assert_subsumes::<NetAll, NetBind>();
211 _assert_subsumes::<Ambient, FsRead>();
212 _assert_subsumes::<Ambient, NetConnect>();
213 _assert_subsumes::<Ambient, Spawn>();
214 }
215}