feat(apps): permission manager equips apps (manifest+consent+decisions in /system), launches via init w/ file grant; shell unprivileged (/home); install/apps/allow/revoke; revoke stops running app; app-image tool; counter app
This commit is contained in:
@@ -10,4 +10,5 @@ bench = false
|
||||
|
||||
[dependencies]
|
||||
types = { workspace = true }
|
||||
manifest = { workspace = true }
|
||||
libkos = { workspace = true }
|
||||
|
||||
@@ -9,19 +9,21 @@
|
||||
extern crate libkos;
|
||||
|
||||
use libkos::fs::{FsError, Storage};
|
||||
use libkos::spawn::{SpawnError, spawn};
|
||||
use libkos::spawn::{SpawnError, Spawned, spawn};
|
||||
use libkos::syscall;
|
||||
use types::bounded::CapSlot;
|
||||
use types::budget::Limits;
|
||||
|
||||
/// Where init put them (see init's manifest: `uses`, the spawn endpoint,
|
||||
/// then control).
|
||||
const STORAGE: CapSlot = CapSlot::const_new(0);
|
||||
const INIT: CapSlot = CapSlot::const_new(1);
|
||||
const CONTROL: CapSlot = CapSlot::const_new(2);
|
||||
const PERMISSION: CapSlot = CapSlot::const_new(0);
|
||||
const STORAGE: CapSlot = CapSlot::const_new(1);
|
||||
const INIT: CapSlot = CapSlot::const_new(2);
|
||||
const CONTROL: CapSlot = CapSlot::const_new(3);
|
||||
/// Where storage's buffers are mapped: the whole disk's, a narrowed one's.
|
||||
const STORAGE_BUF_VA: types::address::VirtualAddress = types::address::VirtualAddress::from_raw(0x0180_0000);
|
||||
const NARROW_BUF_VA: types::address::VirtualAddress = types::address::VirtualAddress::from_raw(0x0190_0000);
|
||||
const REQUEST_PAGE_VA: types::address::VirtualAddress = types::address::VirtualAddress::from_raw(0x01C0_0000);
|
||||
/// Read back from the disk image by scripts/test-qemu.sh.
|
||||
const NOTE: &[u8] = b"written by k-os on ext4\n";
|
||||
|
||||
@@ -29,10 +31,18 @@ const NOTE: &[u8] = b"written by k-os on ext4\n";
|
||||
/// the first has died and its limits came back.
|
||||
const HELLO: Limits = Limits { pages: 80, endpoints: 0, notifications: 0, regions: 0 };
|
||||
|
||||
/// Spawns the program at `path`, read through a grant made just for it.
|
||||
fn spawn_file(fs: &mut Storage, path: &[u8], limits: Limits) -> Result<Spawned, SpawnError> {
|
||||
let file = fs.grant(path, false).map_err(|_| SpawnError::Failed)?;
|
||||
let child = spawn(INIT, file, limits);
|
||||
let _ = libkos::fs::release(file);
|
||||
child
|
||||
}
|
||||
|
||||
/// Spawns hello, runs it, and waits for it to die; hands it `grant`
|
||||
/// first, if there is one.
|
||||
fn run_hello(died: CapSlot, grant: Option<CapSlot>) -> Result<(), &'static str> {
|
||||
let child = spawn(INIT, b"bin/hello", HELLO).map_err(|_| "init would not spawn hello")?;
|
||||
fn run_hello(fs: &mut Storage, died: CapSlot, grant: Option<CapSlot>) -> Result<(), &'static str> {
|
||||
let child = spawn_file(fs, b"bin/hello", HELLO).map_err(|_| "init would not spawn hello")?;
|
||||
let call = types::permission::PermissionSet::empty().with(types::permission::Permission::Execute);
|
||||
if grant.is_some_and(|g| child.give(g, call).is_none()) {
|
||||
return Err("a storage grant could not be passed on");
|
||||
@@ -78,7 +88,7 @@ fn narrow_storage(fs: &mut Storage, died: CapSlot) -> Result<(), &'static str> {
|
||||
if widened.and_then(|mut w| w.create(b"x")).is_ok() {
|
||||
return Err("a grant was widened to write");
|
||||
}
|
||||
run_hello(died, Some(grant))?;
|
||||
run_hello(fs, died, Some(grant))?;
|
||||
libkos::fs::release(grant).map_err(|_| "release failed")?;
|
||||
if inner.list_dir(b"", |_| {}).is_ok() {
|
||||
return Err("a released grant still works");
|
||||
@@ -86,6 +96,33 @@ fn narrow_storage(fs: &mut Storage, died: CapSlot) -> Result<(), &'static str> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// An app runs through the permission manager with what it was granted;
|
||||
/// revoking that stops it, and it cannot run again until allowed.
|
||||
fn revoke_stops_a_running_app(died: CapSlot) -> Result<(), &'static str> {
|
||||
use manifest::Capability;
|
||||
let apps = libkos::apps::Apps::connect(PERMISSION, REQUEST_PAGE_VA).ok_or("no permission manager")?;
|
||||
let display = Capability::Display.index();
|
||||
let (snake, granted) = apps.launch(&["snake"], None, None).map_err(|_| "snake would not launch")?;
|
||||
if granted & manifest::Capabilities::NONE.with(Capability::Display).bits() == 0 {
|
||||
return Err("snake runs without the display it was granted");
|
||||
}
|
||||
syscall::watch_death(snake, died).map_err(|_| "cannot watch snake")?;
|
||||
apps.revoke("kos.snake", display).map_err(|_| "revoke failed")?;
|
||||
syscall::wait(died).map_err(|_| "wait failed")?;
|
||||
syscall::drop_cap(snake);
|
||||
let refused = apps.launch(&["snake"], None, None);
|
||||
if !matches!(refused, Err(r) if r.code == types::apps::reply::MISSING) {
|
||||
return Err("snake ran without the display it requires");
|
||||
}
|
||||
apps.allow("kos.snake", display).map_err(|_| "allow failed")?;
|
||||
let (snake, _) = apps.launch(&["snake"], None, None).map_err(|_| "snake did not run once allowed again")?;
|
||||
syscall::watch_death(snake, died).map_err(|_| "cannot watch snake")?;
|
||||
syscall::kill(snake);
|
||||
syscall::wait(died).map_err(|_| "wait failed")?;
|
||||
syscall::drop_cap(snake);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check() -> Result<(), &'static str> {
|
||||
let mut fs = Storage::attach(STORAGE, STORAGE_BUF_VA).map_err(|_| "cannot attach to storage")?;
|
||||
write_a_file(&mut fs)?;
|
||||
@@ -95,21 +132,24 @@ fn check() -> Result<(), &'static str> {
|
||||
narrow_storage(&mut fs, died)?;
|
||||
libkos::println!("spawntest: storage grants stay in scope, pass on, release: ok");
|
||||
|
||||
run_hello(died, None)?;
|
||||
run_hello(&mut fs, died, None)?;
|
||||
libkos::println!("spawntest: spawned program runs and its death is heard: ok");
|
||||
|
||||
let greedy = Limits { pages: 200, ..HELLO };
|
||||
if spawn(INIT, b"bin/hello", greedy).err() != Some(SpawnError::OverAllowance) {
|
||||
if spawn_file(&mut fs, b"bin/hello", greedy).err() != Some(SpawnError::OverAllowance) {
|
||||
return Err("init spawned past the allowance");
|
||||
}
|
||||
let tiny = Limits { pages: 1, ..HELLO };
|
||||
if spawn(INIT, b"bin/nothere", tiny).err() != Some(SpawnError::NotFound) {
|
||||
if spawn_file(&mut fs, b"bin/nothere", tiny).err() != Some(SpawnError::NotFound) {
|
||||
return Err("a missing program was not reported");
|
||||
}
|
||||
libkos::println!("spawntest: allowance and missing files refused: ok");
|
||||
|
||||
run_hello(died, None)?;
|
||||
run_hello(&mut fs, died, None)?;
|
||||
libkos::println!("spawntest: allowance comes back when a child dies: ok");
|
||||
|
||||
revoke_stops_a_running_app(died)?;
|
||||
libkos::println!("spawntest: revoking stops a running app, allowing lets it run: ok");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user