capsules_extra/
kv_store_permissions.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2023.
4
5//! Tock Key-Value store capsule with permissions.
6//!
7//! This capsule provides a KV interface with permissions and access control.
8//!
9//! ```rust,ignore
10//! +-----------------------+
11//! |  Capsule using K-V    |
12//! +-----------------------+
13//!
14//!    hil::kv::KVPermissions
15//!
16//! +-----------------------+
17//! | K-V store (this file) |
18//! +-----------------------+
19//!
20//!    hil::kv::KV
21//!
22//! +-----------------------+
23//! |  K-V library          |
24//! +-----------------------+
25//!
26//!    hil::flash
27//! ```
28
29use core::mem;
30use kernel::hil::kv;
31use kernel::storage_permissions::StoragePermissions;
32use kernel::utilities::cells::{MapCell, OptionalCell, TakeCell};
33use kernel::utilities::leasable_buffer::SubSliceMut;
34use kernel::ErrorCode;
35
36#[derive(Clone, Copy, PartialEq, Debug)]
37enum Operation {
38    Get,
39    Set,
40    Add,
41    Update,
42    Delete,
43    GarbageCollect,
44}
45
46/// Current version of the Tock K-V header.
47const HEADER_VERSION: u8 = 0;
48pub const HEADER_LENGTH: usize = mem::size_of::<KeyHeader>();
49
50/// This is the header used for KV stores.
51#[repr(C, packed)]
52struct KeyHeader {
53    version: u8,
54    length: u32,
55    write_id: u32,
56}
57
58impl KeyHeader {
59    /// Create a new `KeyHeader` from a buffer
60    fn new_from_buf(buf: &[u8]) -> Self {
61        Self {
62            version: buf[0],
63            length: u32::from_le_bytes(buf[1..5].try_into().unwrap_or([0; 4])),
64            write_id: u32::from_le_bytes(buf[5..9].try_into().unwrap_or([0; 4])),
65        }
66    }
67
68    /// Copy the header to `buf`
69    fn copy_to_buf(&self, buf: &mut [u8]) {
70        buf[0] = self.version;
71        buf[1..5].copy_from_slice(&self.length.to_le_bytes());
72        buf[5..9].copy_from_slice(&self.write_id.to_le_bytes());
73    }
74}
75
76/// Key-Value store with Tock-specific extensions for permissions and access
77/// control.
78///
79/// Implements `KVPermissions` on top of `KV`.
80pub struct KVStorePermissions<'a, K: kv::KV<'a>> {
81    kv: &'a K,
82    header_value: TakeCell<'static, [u8]>,
83
84    client: OptionalCell<&'a dyn kv::KVClient>,
85    operation: OptionalCell<Operation>,
86
87    value: MapCell<SubSliceMut<'static, u8>>,
88    valid_ids: OptionalCell<StoragePermissions>,
89}
90
91impl<'a, K: kv::KV<'a>> KVStorePermissions<'a, K> {
92    pub fn new(
93        kv: &'a K,
94        header_value: &'static mut [u8; HEADER_LENGTH],
95    ) -> KVStorePermissions<'a, K> {
96        Self {
97            kv,
98            header_value: TakeCell::new(header_value),
99            client: OptionalCell::empty(),
100            operation: OptionalCell::empty(),
101            value: MapCell::empty(),
102            valid_ids: OptionalCell::empty(),
103        }
104    }
105
106    fn insert(
107        &self,
108        key: SubSliceMut<'static, u8>,
109        mut value: SubSliceMut<'static, u8>,
110        permissions: StoragePermissions,
111        operation: Operation,
112    ) -> Result<
113        (),
114        (
115            SubSliceMut<'static, u8>,
116            SubSliceMut<'static, u8>,
117            ErrorCode,
118        ),
119    > {
120        let write_id = match permissions.get_write_id() {
121            Some(write_id) => write_id,
122            None => return Err((key, value, ErrorCode::INVAL)),
123        };
124
125        if self.operation.is_some() {
126            return Err((key, value, ErrorCode::BUSY));
127        }
128
129        // The caller must ensure there is space for the header.
130        if value.len() < HEADER_LENGTH {
131            return Err((key, value, ErrorCode::SIZE));
132        }
133
134        // Create the Tock header.
135        let header = KeyHeader {
136            version: HEADER_VERSION,
137            length: (value.len() - HEADER_LENGTH) as u32,
138            write_id,
139        };
140
141        // Copy in the header to the buffer.
142        header.copy_to_buf(value.as_slice());
143
144        self.operation.set(operation);
145
146        match operation {
147            Operation::Set | Operation::Update => {
148                self.valid_ids.set(permissions);
149
150                // We first read the key to see if we are allowed to overwrite it.
151                match self.header_value.take() {
152                    Some(header_value) => match self.kv.get(key, SubSliceMut::new(header_value)) {
153                        Ok(()) => {
154                            self.value.replace(value);
155                            Ok(())
156                        }
157                        Err((key, hvalue, e)) => {
158                            self.header_value.replace(hvalue.take());
159                            self.operation.clear();
160                            Err((key, value, e))
161                        }
162                    },
163                    None => Err((key, value, ErrorCode::FAIL)),
164                }
165            }
166
167            Operation::Add => {
168                // Since add will only succeed if the key is not already there,
169                // we do not have to worry about overwriting and do not need to
170                // check permissions.
171                match self.kv.add(key, value) {
172                    Ok(()) => Ok(()),
173                    Err((key, val, e)) => {
174                        self.operation.clear();
175                        Err((key, val, e))
176                    }
177                }
178            }
179
180            _ => Err((key, value, ErrorCode::FAIL)),
181        }
182    }
183}
184
185impl<'a, K: kv::KV<'a>> kv::KVPermissions<'a> for KVStorePermissions<'a, K> {
186    fn set_client(&self, client: &'a dyn kv::KVClient) {
187        self.client.set(client);
188    }
189
190    fn get(
191        &self,
192        key: SubSliceMut<'static, u8>,
193        value: SubSliceMut<'static, u8>,
194        permissions: StoragePermissions,
195    ) -> Result<
196        (),
197        (
198            SubSliceMut<'static, u8>,
199            SubSliceMut<'static, u8>,
200            ErrorCode,
201        ),
202    > {
203        if self.operation.is_some() {
204            return Err((key, value, ErrorCode::BUSY));
205        }
206
207        self.operation.set(Operation::Get);
208        self.valid_ids.set(permissions);
209
210        match self.kv.get(key, value) {
211            Ok(()) => Ok(()),
212            Err((key, val, e)) => {
213                self.operation.clear();
214                Err((key, val, e))
215            }
216        }
217    }
218
219    fn set(
220        &self,
221        key: SubSliceMut<'static, u8>,
222        value: SubSliceMut<'static, u8>,
223        permissions: StoragePermissions,
224    ) -> Result<
225        (),
226        (
227            SubSliceMut<'static, u8>,
228            SubSliceMut<'static, u8>,
229            ErrorCode,
230        ),
231    > {
232        self.insert(key, value, permissions, Operation::Set)
233    }
234
235    fn add(
236        &self,
237        key: SubSliceMut<'static, u8>,
238        value: SubSliceMut<'static, u8>,
239        permissions: StoragePermissions,
240    ) -> Result<
241        (),
242        (
243            SubSliceMut<'static, u8>,
244            SubSliceMut<'static, u8>,
245            ErrorCode,
246        ),
247    > {
248        self.insert(key, value, permissions, Operation::Add)
249    }
250
251    fn update(
252        &self,
253        key: SubSliceMut<'static, u8>,
254        value: SubSliceMut<'static, u8>,
255        permissions: StoragePermissions,
256    ) -> Result<
257        (),
258        (
259            SubSliceMut<'static, u8>,
260            SubSliceMut<'static, u8>,
261            ErrorCode,
262        ),
263    > {
264        self.insert(key, value, permissions, Operation::Update)
265    }
266
267    fn delete(
268        &self,
269        key: SubSliceMut<'static, u8>,
270        permissions: StoragePermissions,
271    ) -> Result<(), (SubSliceMut<'static, u8>, ErrorCode)> {
272        if self.operation.is_some() {
273            return Err((key, ErrorCode::BUSY));
274        }
275
276        self.operation.set(Operation::Delete);
277        self.valid_ids.set(permissions);
278
279        match self.header_value.take() {
280            Some(header_value) => match self.kv.get(key, SubSliceMut::new(header_value)) {
281                Ok(()) => Ok(()),
282                Err((key, hvalue, e)) => {
283                    self.header_value.replace(hvalue.take());
284                    self.operation.clear();
285                    Err((key, e))
286                }
287            },
288            None => Err((key, ErrorCode::FAIL)),
289        }
290    }
291
292    fn garbage_collect(&self) -> Result<(), ErrorCode> {
293        if self.operation.is_some() {
294            return Err(ErrorCode::BUSY);
295        }
296
297        self.operation.set(Operation::GarbageCollect);
298
299        self.kv.garbage_collect()
300    }
301
302    fn header_size(&self) -> usize {
303        HEADER_LENGTH
304    }
305}
306
307impl<'a, K: kv::KV<'a>> kv::KVClient for KVStorePermissions<'a, K> {
308    fn get_complete(
309        &self,
310        result: Result<(), ErrorCode>,
311        key: SubSliceMut<'static, u8>,
312        mut value: SubSliceMut<'static, u8>,
313    ) {
314        self.operation.map(|op| {
315            match op {
316                Operation::Set => {
317                    // Need to determine if we have permission to set this key.
318                    let mut access_allowed = false;
319
320                    if result.is_ok() || result.err() == Some(ErrorCode::SIZE) {
321                        let header = KeyHeader::new_from_buf(value.as_slice());
322
323                        if header.version == HEADER_VERSION {
324                            self.valid_ids.map(|perms| {
325                                access_allowed = perms.check_modify_permission(header.write_id);
326                            });
327                        }
328                    } else if result.err() == Some(ErrorCode::NOSUPPORT) {
329                        // Key wasn't found, so we can create it fresh.
330                        access_allowed = true;
331                    }
332
333                    self.header_value.replace(value.take());
334
335                    if access_allowed {
336                        self.value
337                            .take()
338                            .map(|set_value| match self.kv.set(key, set_value) {
339                                Ok(()) => {}
340
341                                Err((key, set_value, e)) => {
342                                    self.operation.clear();
343                                    self.client.map(move |cb| {
344                                        cb.set_complete(Err(e), key, set_value);
345                                    });
346                                }
347                            });
348                    } else {
349                        self.operation.clear();
350                        self.value.take().map(|set_value| {
351                            self.client.map(move |cb| {
352                                cb.set_complete(Err(ErrorCode::NOSUPPORT), key, set_value);
353                            });
354                        });
355                    }
356                }
357                Operation::Update => {
358                    // Need to determine if we have permission to set this key.
359                    let mut access_allowed = false;
360
361                    if result.is_ok() || result.err() == Some(ErrorCode::SIZE) {
362                        let header = KeyHeader::new_from_buf(value.as_slice());
363
364                        if header.version == HEADER_VERSION {
365                            self.valid_ids.map(|perms| {
366                                access_allowed = perms.check_modify_permission(header.write_id);
367                            });
368                        }
369                    }
370
371                    self.header_value.replace(value.take());
372
373                    if access_allowed {
374                        self.value
375                            .take()
376                            .map(|set_value| match self.kv.update(key, set_value) {
377                                Ok(()) => {}
378
379                                Err((key, set_value, e)) => {
380                                    self.operation.clear();
381                                    self.client.map(move |cb| {
382                                        cb.update_complete(Err(e), key, set_value);
383                                    });
384                                }
385                            });
386                    } else {
387                        self.operation.clear();
388                        self.value.take().map(|set_value| {
389                            self.client.map(move |cb| {
390                                cb.update_complete(Err(ErrorCode::NOSUPPORT), key, set_value);
391                            });
392                        });
393                    }
394                }
395                Operation::Delete => {
396                    // Before we delete an object we retrieve the header to
397                    // ensure that we have permissions to access it. In that
398                    // case we don't need to supply a buffer long enough to
399                    // store the full value, so a `SIZE` error code is ok and we
400                    // can continue to remove the object.
401                    let mut access_allowed = false;
402
403                    if result.is_ok() || result.err() == Some(ErrorCode::SIZE) {
404                        let header = KeyHeader::new_from_buf(value.as_slice());
405
406                        if header.version == HEADER_VERSION {
407                            self.valid_ids.map(|perms| {
408                                access_allowed = perms.check_modify_permission(header.write_id);
409                            });
410                        }
411                    }
412
413                    self.header_value.replace(value.take());
414
415                    if access_allowed {
416                        match self.kv.delete(key) {
417                            Ok(()) => {}
418
419                            Err((key, e)) => {
420                                self.operation.clear();
421                                self.client.map(move |cb| {
422                                    cb.delete_complete(Err(e), key);
423                                });
424                            }
425                        }
426                    } else {
427                        self.operation.clear();
428                        self.client.map(move |cb| {
429                            cb.delete_complete(Err(ErrorCode::NOSUPPORT), key);
430                        });
431                    }
432                }
433                Operation::Get => {
434                    self.operation.clear();
435
436                    let mut read_allowed = false;
437
438                    if result.is_ok() || result.err() == Some(ErrorCode::SIZE) {
439                        let header = KeyHeader::new_from_buf(value.as_slice());
440
441                        if header.version == HEADER_VERSION {
442                            self.valid_ids.map(|perms| {
443                                read_allowed = perms.check_read_permission(header.write_id);
444                            });
445
446                            if read_allowed {
447                                // Remove the header from the accessible portion
448                                // of the buffer.
449                                value.slice(HEADER_LENGTH..);
450                            }
451                        }
452                    }
453
454                    if !read_allowed {
455                        // Access denied or the header is invalid, zero the buffer.
456                        value.as_slice().iter_mut().for_each(|m| *m = 0)
457                    }
458
459                    self.client.map(move |cb| {
460                        if read_allowed {
461                            cb.get_complete(result, key, value);
462                        } else {
463                            // The operation failed or the caller doesn't
464                            // have permission, just return the error for
465                            // key not found (and an empty buffer).
466                            cb.get_complete(Err(ErrorCode::NOSUPPORT), key, value);
467                        }
468                    });
469                }
470                _ => {}
471            }
472        });
473    }
474
475    fn set_complete(
476        &self,
477        result: Result<(), ErrorCode>,
478        key: SubSliceMut<'static, u8>,
479        value: SubSliceMut<'static, u8>,
480    ) {
481        self.operation.clear();
482        self.client.map(move |cb| {
483            cb.set_complete(result, key, value);
484        });
485    }
486
487    fn add_complete(
488        &self,
489        result: Result<(), ErrorCode>,
490        key: SubSliceMut<'static, u8>,
491        value: SubSliceMut<'static, u8>,
492    ) {
493        self.operation.clear();
494        self.client.map(move |cb| {
495            cb.add_complete(result, key, value);
496        });
497    }
498
499    fn update_complete(
500        &self,
501        result: Result<(), ErrorCode>,
502        key: SubSliceMut<'static, u8>,
503        value: SubSliceMut<'static, u8>,
504    ) {
505        self.operation.clear();
506        self.client.map(move |cb| {
507            cb.update_complete(result, key, value);
508        });
509    }
510
511    fn delete_complete(&self, result: Result<(), ErrorCode>, key: SubSliceMut<'static, u8>) {
512        self.operation.clear();
513        self.client.map(move |cb| {
514            cb.delete_complete(result, key);
515        });
516    }
517
518    fn garbage_collection_complete(&self, result: Result<(), ErrorCode>) {
519        self.operation.clear();
520        self.client.map(move |cb| {
521            cb.garbage_collection_complete(result);
522        });
523    }
524}