1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.
//! Mechanism for managing storage read & write permissions.
//!
//! These permissions are intended for userspace applications so the kernel can
//! restrict which stored elements the apps have access to.
use crate::capabilities::ApplicationStorageCapability;
use crate::capabilities::KerneluserStorageCapability;
/// Permissions for accessing persistent storage.
///
/// This is a general type capable of representing permissions in different
/// ways. Users of storage permissions do not need to understand the different
/// ways permissions are stored internally. Instead, layers that need to enforce
/// permissions only use the following API:
///
/// ```rust,ignore
/// fn StoragePermissions::check_read_permission(&self, stored_id: u32) -> bool;
/// fn StoragePermissions::check_modify_permission(&self, stored_id: u32) -> bool;
/// fn StoragePermissions::get_write_id(&self) -> Option<u32>;
/// ```
#[derive(Clone, Copy)]
pub struct StoragePermissions(StoragePermissionsPrivate);
/// Inner enum type for types of permissions.
///
/// Private so permissions can only be created with capability-restricted
/// constructors.
#[derive(Clone, Copy)]
enum StoragePermissionsPrivate {
/// This permission grants an application full access to its own stored
/// state. The application may write state, and read and modify anything it
/// has written.
///
/// The `NonZeroU32` is the `ShortId::Fixed` of the application.
SelfOnly(core::num::NonZeroU32),
/// This permission supports setting whether an application can write and
/// supports setting up to eight storage identifiers the application can
/// read and eight storage identifiers the application can modify. This
/// permission also includes a flag allowing an application to read and
/// modify its own state.
FixedSize(FixedSizePermissions),
/// This permission supports setting whether an application can write and
/// supports storing references to static buffers that contain an arbitrary
/// list of storage identifiers the application can read and modify. This
/// permission also includes a flag allowing an application to read and
/// modify its own state.
Listed(ListedPermissions),
/// This permission is designed for only the kernel use, and allows the
/// kernel to store and read/modify its own state. Note, this permission
/// does not give the kernel access to application state.
Kernel,
/// This permission grants an application no access to any persistent
/// storage.
Null,
}
/// `StoragePermissions` with a fixed size number of read and modify
/// permissions.
///
/// For simplicity, a we store to eight read and eight write permissions. The
/// first `X_count` `u32` values in `X_permissions` are valid.
#[derive(Clone, Copy)]
pub struct FixedSizePermissions {
/// The `ShortId::Fixed` of the application these permissions belong to.
app_id: core::num::NonZeroU32,
/// Whether this permission grants write access.
write_permission: bool,
/// If true, these permissions grant read and modify access to any stored
/// state where this AppId matches the storage identifier.
read_modify_self: bool,
/// How many entries in the `read_permissions` slice are valid, starting at
/// index 0.
read_count: usize,
/// Up to eight 32 bit identifiers of storage items the process has read
/// access to.
read_permissions: [u32; 8],
/// How many entries in the `modify_permissions` slice are valid, starting
/// at index 0.
modify_count: usize,
/// Up to eight 32 bit identifiers of storage items the process has modify
/// (update) access to.
modify_permissions: [u32; 8],
}
/// `StoragePermissions` with arbitrary static arrays holding read and modify
/// permissions.
#[derive(Clone, Copy)]
pub struct ListedPermissions {
/// The `ShortId::Fixed` of the application these permissions belong to.
app_id: core::num::NonZeroU32,
/// Whether this permission grants write access.
write_permission: bool,
/// If true, these permissions grant read and modify access to any stored
/// state where this AppId matches the storage identifier.
read_modify_self: bool,
/// The 32 bit identifiers of storage items the process can read.
read_permissions: &'static [u32],
/// The 32 bit identifiers of storage items the process can modify
modify_permissions: &'static [u32],
}
impl StoragePermissions {
pub fn new_self_only(
short_id_fixed: core::num::NonZeroU32,
_cap: &dyn ApplicationStorageCapability,
) -> Self {
Self(StoragePermissionsPrivate::SelfOnly(short_id_fixed))
}
pub fn new_fixed_size(
app_id: core::num::NonZeroU32,
write_permission: bool,
read_modify_self: bool,
read_count: usize,
read_permissions: [u32; 8],
modify_count: usize,
modify_permissions: [u32; 8],
_cap: &dyn ApplicationStorageCapability,
) -> Self {
Self(StoragePermissionsPrivate::FixedSize(FixedSizePermissions {
app_id,
write_permission,
read_modify_self,
read_count,
read_permissions,
modify_count,
modify_permissions,
}))
}
pub fn new_listed(
app_id: core::num::NonZeroU32,
write_permission: bool,
read_modify_self: bool,
read_permissions: &'static [u32],
modify_permissions: &'static [u32],
_cap: &dyn ApplicationStorageCapability,
) -> Self {
Self(StoragePermissionsPrivate::Listed(ListedPermissions {
app_id,
write_permission,
read_modify_self,
read_permissions,
modify_permissions,
}))
}
pub fn new_kernel(_cap: &dyn KerneluserStorageCapability) -> Self {
Self(StoragePermissionsPrivate::Kernel)
}
pub fn new_null() -> Self {
Self(StoragePermissionsPrivate::Null)
}
/// Check if these storage permissions grant read access to the stored state
/// marked with identifier `stored_id`.
pub fn check_read_permission(&self, stored_id: u32) -> bool {
match self.0 {
StoragePermissionsPrivate::SelfOnly(id) => stored_id == id.into(),
StoragePermissionsPrivate::FixedSize(p) => {
(stored_id == p.app_id.into() && p.read_modify_self)
|| (stored_id != 0
&& p.read_permissions
.get(0..p.read_count)
.unwrap_or(&[])
.contains(&stored_id))
}
StoragePermissionsPrivate::Listed(p) => {
(stored_id == p.app_id.into() && p.read_modify_self)
|| (stored_id != 0 && p.read_permissions.contains(&stored_id))
}
StoragePermissionsPrivate::Kernel => stored_id == 0,
StoragePermissionsPrivate::Null => false,
}
}
/// Check if these storage permissions grant modify access to the stored
/// state marked with identifier `stored_id`.
pub fn check_modify_permission(&self, stored_id: u32) -> bool {
match self.0 {
StoragePermissionsPrivate::SelfOnly(id) => stored_id == id.into(),
StoragePermissionsPrivate::FixedSize(p) => {
(stored_id == p.app_id.into() && p.read_modify_self)
|| (stored_id != 0
&& p.modify_permissions
.get(0..p.modify_count)
.unwrap_or(&[])
.contains(&stored_id))
}
StoragePermissionsPrivate::Listed(p) => {
(stored_id == p.app_id.into() && p.read_modify_self)
|| (stored_id != 0 && p.modify_permissions.contains(&stored_id))
}
StoragePermissionsPrivate::Kernel => stored_id == 0,
StoragePermissionsPrivate::Null => false,
}
}
/// Retrieve the identifier to use when storing state, if the application
/// has permission to write. Returns `None` if the application cannot write.
pub fn get_write_id(&self) -> Option<u32> {
match self.0 {
StoragePermissionsPrivate::SelfOnly(id) => Some(id.into()),
StoragePermissionsPrivate::FixedSize(p) => {
p.write_permission.then_some(p.app_id.into())
}
StoragePermissionsPrivate::Listed(p) => p.write_permission.then_some(p.app_id.into()),
StoragePermissionsPrivate::Kernel => Some(0),
StoragePermissionsPrivate::Null => None,
}
}
}