twizzler_security/sec_ctx/
user.rs

1use alloc::collections::btree_map::BTreeMap;
2use core::fmt::Display;
3
4use heapless::Vec;
5use log::debug;
6use twizzler::{
7    marker::BaseType,
8    object::{Object, ObjectBuilder, RawObject, TypedObject},
9};
10use twizzler_abi::{
11    object::{ObjID, Protections},
12    syscall::ObjectCreate,
13};
14use twizzler_rt_abi::{
15    error::{ResourceError, TwzError},
16    object::MapFlags,
17};
18
19use super::{CtxMapItem, CtxMapItemType, PermsInfo, SecCtxBase, SecCtxFlags};
20use crate::{
21    sec_ctx::{MAP_ITEMS_PER_OBJ, OBJECT_ROOT_OFFSET},
22    Cap, Del, VerifyingKey,
23};
24
25pub struct SecCtx {
26    uobj: Object<SecCtxBase>,
27    cache: BTreeMap<ObjID, PermsInfo>,
28}
29
30impl Default for SecCtx {
31    fn default() -> Self {
32        let obj = ObjectBuilder::default()
33            .build(SecCtxBase::default())
34            .unwrap();
35
36        Self {
37            uobj: obj,
38            cache: BTreeMap::new(),
39        }
40    }
41}
42
43impl Display for SecCtx {
44    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
45        let binding = self.uobj.clone();
46        let base = binding.base();
47
48        write!(f, "Sec Ctx ObjID: {} {{\n", self.uobj.id())?;
49        write!(f, "base: {:?}", base)?;
50        Ok(())
51    }
52}
53
54impl SecCtx {
55    pub fn attached_ctx() -> SecCtx {
56        todo!("unsure how to get attached sec_ctx as of rn")
57    }
58
59    pub fn new(
60        object_create_spec: ObjectCreate,
61        global_mask: Protections,
62        flags: SecCtxFlags,
63    ) -> Result<Self, TwzError> {
64        let new_obj =
65            ObjectBuilder::new(object_create_spec).build(SecCtxBase::new(global_mask, flags))?;
66
67        Ok(Self {
68            uobj: new_obj,
69            cache: BTreeMap::new(),
70        })
71    }
72
73    pub fn insert_cap(&self, cap: Cap) -> Result<(), TwzError> {
74        let mut tx = self.uobj.clone().into_tx()?;
75        let mut base = tx.base_mut();
76
77        let mut map_item = {
78            base.offset += size_of::<Cap>();
79
80            CtxMapItem {
81                item_type: CtxMapItemType::Cap,
82                offset: base.offset + OBJECT_ROOT_OFFSET,
83            }
84        };
85
86        let alignment = 0x10 - (map_item.offset % 0x10);
87        map_item.offset += alignment;
88        // also have to fix the length in the offset
89        base.offset += alignment;
90
91        #[cfg(feature = "log")]
92        debug!("write offset into object for entry: {:#X}", map_item.offset);
93
94        // seeing if a vec already exists for target obj, else create new
95        if let Some(vec) = base.map.get_mut(&cap.target) {
96            vec.push(map_item).map_err(|_| {
97                // only possible error case is it being full
98                TwzError::Resource(ResourceError::OutOfResources)
99            })?;
100        } else {
101            let mut new_vec = Vec::<CtxMapItem, MAP_ITEMS_PER_OBJ>::new();
102            let _ = new_vec.push(map_item);
103            let _ = base.map.insert(cap.target, new_vec).map_err(|_| {
104                // only possible error case is it being full
105                TwzError::Resource(ResourceError::OutOfResources)
106            })?;
107        };
108
109        let ptr = tx
110            .lea_mut(map_item.offset, size_of::<Cap>())
111            .expect("Write offset should not result in a pointer outside of the object")
112            .cast::<Cap>();
113
114        // SAFETY: copies the capability into the object, we check that the pointer is valid above /
115        // fixing its alignment
116        unsafe {
117            *ptr = cap;
118        }
119
120        tx.commit()?;
121
122        #[cfg(feature = "log")]
123        debug!("Added capability at ptr: {:#?}", ptr);
124        Ok(())
125    }
126
127    pub fn insert_del(&self, _del: Del) -> Result<(), TwzError> {
128        todo!("implement later")
129    }
130
131    pub fn id(&self) -> ObjID {
132        self.uobj.id()
133    }
134
135    pub fn remove_cap(&mut self) {
136        todo!("implement later")
137    }
138
139    pub fn remove_del(&mut self) {
140        todo!("implement later")
141    }
142
143    /// looks up permission info for requested object
144    pub fn lookup<T: BaseType>(&mut self, target_id: ObjID) -> PermsInfo {
145        // first just check cache
146        if let Some(cache_entry) = self.cache.get(&target_id) {
147            return *cache_entry;
148        };
149
150        let base = self.uobj.base();
151
152        // fetch default protections
153        let target_object = Object::<T>::map(target_id, MapFlags::READ)
154            .expect("target object should exist!")
155            .meta_ptr();
156
157        let target_obj_default_prot;
158        let v_key_obj_id;
159
160        unsafe {
161            let metadata = *target_object;
162            v_key_obj_id = metadata.kuid;
163
164            target_obj_default_prot = metadata.default_prot;
165        }
166
167        let v_obj = Object::<VerifyingKey>::map(v_key_obj_id, MapFlags::READ | MapFlags::WRITE)
168            .expect("failed to open verifying key for this object");
169        let v_key = v_obj.base();
170
171        // step 1, add up all the permissions granted by VERIFIED capabilities and delegations
172        let mut granted_perms =
173            PermsInfo::new(self.id(), target_obj_default_prot, Protections::empty());
174
175        // check for possible items
176        let Some(results) = base.map.get(&target_id) else {
177            // only default permissions granted, there are no entries in this security context
178            // not even worth adding to cache
179            return granted_perms;
180        };
181
182        for entry in results {
183            match entry.item_type {
184                CtxMapItemType::Del => {
185                    //TODO: skip over for now!! finish up later
186                    todo!("Delegations not supported yet for lookup")
187                }
188
189                CtxMapItemType::Cap => {
190                    // pull capability out of the object
191
192                    let ptr = self
193                        .uobj
194                        .lea(entry.offset, size_of::<Cap>())
195                        .expect("address should be inside of object!")
196                        .cast::<Cap>();
197
198                    unsafe {
199                        let cap = *ptr;
200
201                        if cap.verify_sig(v_key).is_ok() {
202                            granted_perms.provide |= cap.protections;
203                        }
204                    }
205                }
206            }
207        }
208
209        let Some(mask) = base.masks.get(&target_id) else {
210            // no mask inside
211            // final perms are granted_perms (intersection) global_mask
212
213            granted_perms.provide &= base.global_mask;
214
215            self.cache.insert(target_id, granted_perms.clone());
216            return granted_perms;
217        };
218
219        // mask exists, final perms are
220        // granted_perms & permmask & (global_mask | override_mask)
221        granted_perms.provide =
222            granted_perms.provide & mask.permmask & (base.global_mask | mask.ovrmask);
223
224        self.cache.insert(target_id, granted_perms.clone());
225        granted_perms
226    }
227}
228
229impl TryFrom<ObjID> for SecCtx {
230    type Error = TwzError;
231    fn try_from(value: ObjID) -> Result<Self, Self::Error> {
232        let uobj = Object::<SecCtxBase>::map(value, MapFlags::READ | MapFlags::WRITE)?;
233
234        Ok(Self {
235            uobj,
236            cache: BTreeMap::new(),
237        })
238    }
239}
240
241mod tests {
242    use super::*;
243    use crate::sec_ctx::SecCtxFlags;
244
245    extern crate test;
246
247    fn test_security_context_creation() {
248        let _default_sec_ctx = SecCtx::default();
249        let _new_sec_ctx =
250            SecCtx::new(Default::default(), Protections::all(), SecCtxFlags::empty())
251                .expect("new context should have been created!");
252    }
253}