121 lines
4.5 KiB
Rust
121 lines
4.5 KiB
Rust
//! The gatekeeper (see the vision's Gatekeeper note): authenticates the
|
|
//! owner before anything that touches user data starts, and hands the
|
|
//! master key over. `flow` is the logic; `run` wires the devices.
|
|
|
|
#![no_std]
|
|
#![forbid(unsafe_code)]
|
|
|
|
extern crate alloc;
|
|
|
|
pub mod devices;
|
|
pub mod flow;
|
|
|
|
#[cfg(test)]
|
|
#[path = "tests/mod.rs"]
|
|
mod tests;
|
|
|
|
use alloc::vec;
|
|
use keystore::{Block, KdfParams};
|
|
use libkos::syscall::{self, Received};
|
|
use types::bounded::CapSlot;
|
|
use types::gatekeeper::{KEY, NAME, REMOVED, SEED, UNLOCK, reply, unlocked};
|
|
|
|
/// Where init's manifest put them: its own endpoint (devices arrive on
|
|
/// it, then init's calls), then the terminal.
|
|
const EP: CapSlot = CapSlot::const_new(0);
|
|
const TERMINAL: CapSlot = CapSlot::const_new(1);
|
|
|
|
/// Answers init's next call, if it is `op`, with `words`.
|
|
fn answer(op: usize, words: [usize; 4]) -> bool {
|
|
loop {
|
|
match syscall::receive_msg(EP) {
|
|
Ok(Received::Call { badge: 0, words: [w, ..], .. }) if w == op => {
|
|
return syscall::reply(words, None).is_ok();
|
|
}
|
|
// Anyone else, or anything else: refused.
|
|
Ok(Received::Call { .. }) => {
|
|
let _ = syscall::reply([reply::FAILED, 0, 0, 0], None);
|
|
}
|
|
Ok(_) => {}
|
|
Err(_) => return false,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn run() -> ! {
|
|
// Init hands over the keystore disk, then the randomness device.
|
|
let keystore = libkos::device::receive_device(EP).mmio.and_then(driver_block_virtio::VirtioBlockDevice::new);
|
|
let random = libkos::device::receive_device(EP).mmio.and_then(driver_rng_virtio::VirtioRng::new);
|
|
let (Some(keystore), Some(random)) = (keystore, random) else {
|
|
libkos::println!("gatekeeper: no keystore or no randomness, nothing to unlock with");
|
|
let _ = answer(UNLOCK, [reply::FAILED, 0, 0, 0]);
|
|
syscall::exit();
|
|
};
|
|
let mut memory = vec![Block::default(); KdfParams::DEFAULT.memory_kib as usize];
|
|
libkos::println!("gatekeeper: waiting for the owner");
|
|
let mut random = devices::Hardware(random);
|
|
let unlocked = flow::run(
|
|
&mut devices::Disk(keystore),
|
|
&mut random,
|
|
&mut devices::Terminal(TERMINAL),
|
|
KdfParams::DEFAULT,
|
|
&mut memory,
|
|
);
|
|
// The guesses' working memory goes before anything else happens.
|
|
for b in memory.iter_mut() {
|
|
*b = Block::default();
|
|
}
|
|
drop(memory);
|
|
match unlocked {
|
|
Ok(flow::Unlocked { user, key, fresh, removed }) => {
|
|
libkos::println!("gatekeeper: unlocked");
|
|
libkos::terminal::clear(TERMINAL);
|
|
let words = |b: &[u8; 32]| core::array::from_fn::<usize, 4, _>(|i| usize::from_le_bytes(b[i * 8..i * 8 + 8].try_into().unwrap_or([0; 8])));
|
|
let mut seed = [0u8; 32];
|
|
if !keystore::Random::fill(&mut random, &mut seed) {
|
|
let _ = answer(UNLOCK, [reply::FAILED, 0, 0, 0]);
|
|
syscall::exit();
|
|
}
|
|
let flags = if fresh { unlocked::FRESH } else { 0 } | if user.is_none() { unlocked::GUEST } else { 0 };
|
|
let id = user.map_or(0, |u| usize::from_le_bytes(u.id.0));
|
|
let role = match user.map(|u| u.role) {
|
|
Some(keystore::Role::Owner) => unlocked::OWNER,
|
|
Some(keystore::Role::Standard) => unlocked::STANDARD,
|
|
None => 0,
|
|
};
|
|
let mut name_buf = [0u8; 16];
|
|
let len = match &user {
|
|
Some(u) => {
|
|
let n = u.name.as_bytes();
|
|
name_buf[..n.len()].copy_from_slice(n);
|
|
n.len()
|
|
}
|
|
None => {
|
|
name_buf[..5].copy_from_slice(b"guest");
|
|
5
|
|
}
|
|
};
|
|
let name = &name_buf[..len];
|
|
let handed = answer(UNLOCK, [reply::OK, flags, id, role])
|
|
&& answer(KEY, words(&key.0))
|
|
&& answer(SEED, words(&seed))
|
|
&& answer(NAME, types::log::text_words(name));
|
|
seed.fill(0);
|
|
if handed {
|
|
for gone in removed.iter().flatten() {
|
|
if !answer(REMOVED, [reply::OK, usize::from_le_bytes(gone.0), 0, 0]) {
|
|
break;
|
|
}
|
|
}
|
|
let _ = answer(REMOVED, [reply::DONE, 0, 0, 0]);
|
|
}
|
|
}
|
|
Err(why) => {
|
|
libkos::println!("gatekeeper: cannot go on: {:?}", why);
|
|
let _ = answer(UNLOCK, [reply::FAILED, 0, 0, 0]);
|
|
}
|
|
}
|
|
// Its capabilities go with it.
|
|
syscall::exit()
|
|
}
|