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
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.
//! Traits and types for application credentials checkers, used to
//! decide whether an application can be loaded. See
//| the [AppID TRD](../../doc/reference/trd-appid.md).
pub mod basic;
use crate::config;
use crate::debug;
use crate::process::{Process, ShortID, State};
use crate::ErrorCode;
use tock_tbf::types::TbfFooterV2Credentials;
/// What a AppCredentialsChecker decided a particular application's credential
/// indicates about the runnability of an application binary.
#[derive(Debug)]
pub enum CheckResult {
/// Accept the credential and run the binary.
Accept,
/// Go to the next credential or in the case of the last one fall
/// back to the default policy.
Pass,
/// Reject the credential and do not run the binary.
Reject,
}
/// Receives callbacks on whether a credential was accepted or not.
pub trait Client<'a> {
fn check_done(
&self,
result: Result<CheckResult, ErrorCode>,
credentials: TbfFooterV2Credentials,
binary: &'a [u8],
);
}
/// Implements a Credentials Checking Policy.
pub trait AppCredentialsChecker<'a> {
fn set_client(&self, _client: &'a dyn Client<'a>);
fn require_credentials(&self) -> bool;
fn check_credentials(
&self,
credentials: TbfFooterV2Credentials,
binary: &'a [u8],
) -> Result<(), (ErrorCode, TbfFooterV2Credentials, &'a [u8])>;
}
/// Default implementation.
impl<'a> AppCredentialsChecker<'a> for () {
fn set_client(&self, _client: &'a dyn Client<'a>) {}
fn require_credentials(&self) -> bool {
false
}
fn check_credentials(
&self,
credentials: TbfFooterV2Credentials,
binary: &'a [u8],
) -> Result<(), (ErrorCode, TbfFooterV2Credentials, &'a [u8])> {
Err((ErrorCode::NOSUPPORT, credentials, binary))
}
}
/// Return whether `process` can run given the identifiers, version
/// numbers, and execution state of other processes. A process is
/// runnable if its credentials have been approved, it is in the
/// Terminated state, and one of the following conditions hold:
///
/// 1. Its Application Identifier and Short ID are different from
/// all other processes, or
/// 2. For every other process that shares an Application Identifier
/// or Short ID:
/// 2A. If it has a lower or equal version number, it is not running
/// 2B. If it has a higher version number, it is in the Terminated,
/// CredentialsUnchecked, or CredentialsFailed state.
///
/// Case 2A is because if a lower version number is currently running, it
/// must be stopped before the higher version number can run. Case 2B is
/// so that a lower or equal version number can be run if the higher or equal
/// has been explicitly stopped (Terminated) or cannot run (Unchecked/Failed).
/// This second case is designed so that at boot the highest version number
/// will run (it will be in the CredentialsApproved state when this test
/// runs at boot), but it can be stopped to let a lower version number run.
pub fn is_runnable<AU: AppUniqueness>(
process: &dyn Process,
processes: &[Option<&dyn Process>],
id_differ: &AU,
) -> bool {
let len = processes.len();
// A process is only runnable if it has approved credentials and
// is not currently running.
if process.get_state() != State::CredentialsApproved && process.get_state() != State::Terminated
{
return false;
}
// Note that this causes `process` to compare against itself;
// however, since `process` is not running and its version number
// is the same, it will not block itself from running.
for i in 0..len {
let other_process = processes[i];
let other_name = other_process.map_or("None", |c| c.get_process_name());
let blocks = other_process.map_or(false, |other| {
let state = other.get_state();
let creds_approve =
state != State::CredentialsUnchecked && state != State::CredentialsFailed;
let different = id_differ.different_identifier(process, other)
&& other.short_app_id() != process.short_app_id();
let newer = other.binary_version() > process.binary_version();
let running = other.is_running();
let runnable = state != State::CredentialsUnchecked
&& state != State::CredentialsFailed
&& state != State::Terminated;
// Other will block process from running if
// 1) Other has approved credentials, and
// 2) Other has the same ShortID or Application Identifier, and
// 3) Other has a higher version number *or* the same version number and is running
if config::CONFIG.debug_process_credentials {
debug!(
"[{}]: creds_approve: {}, different: {}, newer: {}, runnable: {}, running: {}",
other.get_process_name(),
creds_approve,
different,
newer,
runnable,
running
);
}
creds_approve && !different && ((newer && runnable) || running)
});
if blocks {
if config::CONFIG.debug_process_credentials {
debug!(
"Process {} blocks {}",
other_name,
process.get_process_name()
);
}
return false;
}
}
if config::CONFIG.debug_process_credentials {
debug!(
"No process blocks {}: it is runnable",
process.get_process_name()
);
}
// No process blocks this one from running -- it's runnable
true
}
/// Whether two processes have the same Application Identifier; two
/// processes with the same Application Identifier cannot run concurrently.
pub trait AppUniqueness {
/// Returns whether `process_a` and `process_b` have a different identifier,
/// and so can run concurrently. If this returns `false`, the kernel
/// will not run `process_a` and `process_b` at the same time.
fn different_identifier(&self, _process_a: &dyn Process, _process_b: &dyn Process) -> bool;
}
/// Default implementation.
impl AppUniqueness for () {
fn different_identifier(&self, _process_a: &dyn Process, _process_b: &dyn Process) -> bool {
true
}
}
/// Transforms Application Credentials into a corresponding ShortID.
pub trait Compress {
fn to_short_id(&self, _credentials: &TbfFooterV2Credentials) -> ShortID;
}
impl Compress for () {
fn to_short_id(&self, _credentials: &TbfFooterV2Credentials) -> ShortID {
ShortID::LocallyUnique
}
}
pub trait CredentialsCheckingPolicy<'a>:
AppCredentialsChecker<'a> + Compress + AppUniqueness
{
}
impl<'a, T: AppCredentialsChecker<'a> + Compress + AppUniqueness> CredentialsCheckingPolicy<'a>
for T
{
}