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:
@@ -44,9 +44,9 @@ impl<'a> Launcher<'a> {
|
||||
) -> Result<Self, &'static str> {
|
||||
let spawn_ep = syscall::create_endpoint().ok_or("no spawn endpoint")?;
|
||||
let deaths = syscall::create_notification().map_err(|_| "no death notification")?;
|
||||
// Storage was started by boot, before init; programs may use it too.
|
||||
let mut served = [None; MAX_SERVED];
|
||||
served[0] = Some(("storage", crate::STORAGE_EP));
|
||||
// Storage, started by boot, is not among them: it is reached
|
||||
// through grants (a program's `storage` scope).
|
||||
let served = [None; MAX_SERVED];
|
||||
Ok(Self { storage, scratch, devices, served, next_badge: 1, spawn_ep, deaths, spawner: Spawner::new() })
|
||||
}
|
||||
|
||||
@@ -78,20 +78,21 @@ impl<'a> Launcher<'a> {
|
||||
// and take other clients' requests.
|
||||
for name in p.uses {
|
||||
let ep = self.endpoint(name).ok_or("needs a server that is not running")?;
|
||||
let copy = match *name {
|
||||
// Storage issues its own badges, each naming a scope.
|
||||
"storage" => self.storage.grant(b"", true).map_err(|_| "storage refused a grant")?,
|
||||
_ => {
|
||||
let badge = Badge::new(self.next_badge);
|
||||
self.next_badge += 1;
|
||||
syscall::mint(ep, badge, perms(&[Permission::Execute, Permission::Delegate]))
|
||||
.map_err(|_| "cannot mint an endpoint")?
|
||||
}
|
||||
};
|
||||
let badge = Badge::new(self.next_badge);
|
||||
self.next_badge += 1;
|
||||
let copy = syscall::mint(ep, badge, perms(&[Permission::Execute, Permission::Delegate]))
|
||||
.map_err(|_| "cannot mint an endpoint")?;
|
||||
let given = syscall::delegate(copy, child, perms(&[Permission::Execute, Permission::Delegate]));
|
||||
syscall::drop_cap(copy);
|
||||
given.ok_or("cannot give an endpoint")?;
|
||||
}
|
||||
// Storage issues its own badges, each naming a scope.
|
||||
if let Some(scope) = p.storage {
|
||||
let grant = self.storage.grant(scope.path, scope.write).map_err(|_| "storage refused a grant")?;
|
||||
let given = syscall::delegate(grant, child, perms(&[Permission::Execute, Permission::Delegate]));
|
||||
syscall::drop_cap(grant);
|
||||
given.ok_or("cannot give storage")?;
|
||||
}
|
||||
if let Some(allowance) = p.spawns {
|
||||
self.give_spawn(child, allowance)?;
|
||||
}
|
||||
@@ -126,7 +127,8 @@ impl<'a> Launcher<'a> {
|
||||
let badge: Badge = self.spawner.add_client(allowance).ok_or("too many spawn clients")?;
|
||||
let minted = perms(&[Permission::Execute, Permission::Delegate]);
|
||||
let copy = syscall::mint(self.spawn_ep, badge, minted).map_err(|_| "cannot mint")?;
|
||||
let given = syscall::delegate(copy, child, perms(&[Permission::Execute]));
|
||||
// Delegate, to send the program file's grant with each request.
|
||||
let given = syscall::delegate(copy, child, perms(&[Permission::Execute, Permission::Delegate]));
|
||||
syscall::drop_cap(copy);
|
||||
given.map(|_| ()).ok_or("cannot give the spawn endpoint")
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ pub fn run() -> ! {
|
||||
selftest::run(storage, scratch);
|
||||
launch_all(&mut launcher, manifest::TESTS);
|
||||
} else {
|
||||
launch_all(&mut launcher, manifest::APPS);
|
||||
launch_all(&mut launcher, manifest::DESKTOP);
|
||||
}
|
||||
|
||||
libkos::println!("init: all loaded, serving spawns");
|
||||
|
||||
@@ -9,6 +9,14 @@ pub enum Device {
|
||||
Input,
|
||||
}
|
||||
|
||||
/// A storage grant: a folder (or the whole disk, `b""`) and whether it may
|
||||
/// be changed.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Scope {
|
||||
pub path: &'static [u8],
|
||||
pub write: bool,
|
||||
}
|
||||
|
||||
pub struct Program {
|
||||
pub name: &'static str,
|
||||
pub file: &'static [u8],
|
||||
@@ -19,6 +27,8 @@ pub struct Program {
|
||||
/// Earlier programs' endpoints it may call, in its next slots; each
|
||||
/// program's copy carries its own badge, so a server can tell them apart.
|
||||
pub uses: &'static [&'static str],
|
||||
/// Storage it may reach, in the slot after `uses`.
|
||||
pub storage: Option<Scope>,
|
||||
/// Takes the console over from init; everyone else prints through it.
|
||||
pub console: bool,
|
||||
/// May have init spawn programs, whose limits together stay within this.
|
||||
@@ -43,6 +53,7 @@ const fn server(name: &'static str, file: &'static [u8], limits: Limits) -> Prog
|
||||
serves: true,
|
||||
device: None,
|
||||
uses: &[],
|
||||
storage: None,
|
||||
console: false,
|
||||
spawns: None,
|
||||
system_control: false,
|
||||
@@ -62,7 +73,15 @@ pub const SERVERS: &[Program] = &[
|
||||
},
|
||||
// Text on the display, lines from the keyboard (it takes input focus).
|
||||
Program { uses: &["display"], ..server("terminal", b"bin/terminal", SMALL) },
|
||||
server("permission", b"bin/permission", SMALL),
|
||||
// The only one that equips apps (docs/apps.md): it reaches every app's
|
||||
// files and the owner's decisions, and runs apps through init.
|
||||
Program {
|
||||
uses: &["terminal", "display"],
|
||||
storage: Some(Scope { path: b"", write: true }),
|
||||
// Room for a few apps at a time.
|
||||
spawns: Some(Limits { pages: 512, endpoints: 8, notifications: 8, regions: 8 }),
|
||||
..server("permission", b"bin/permission", Limits { pages: 96, ..SMALL })
|
||||
},
|
||||
server("session", b"bin/session", SMALL),
|
||||
];
|
||||
|
||||
@@ -70,18 +89,20 @@ pub const SERVERS: &[Program] = &[
|
||||
/// spawns through init, then reports and halts.
|
||||
pub const TESTS: &[Program] = &[Program {
|
||||
serves: false,
|
||||
uses: &["storage"],
|
||||
// The permission manager, to run an app the way the shell does.
|
||||
uses: &["permission"],
|
||||
storage: Some(Scope { path: b"", write: true }),
|
||||
spawns: Some(Limits { pages: 128, endpoints: 2, notifications: 2, regions: 2 }),
|
||||
system_control: true,
|
||||
..server("spawntest", b"bin/spawntest", SMALL)
|
||||
}];
|
||||
|
||||
/// The desktop; the test image runs the SMP self-test instead.
|
||||
pub const APPS: &[Program] = &[Program {
|
||||
/// The desktop; the test image runs the SMP self-test instead. The shell
|
||||
/// holds the owner's files and asks the permission manager to run apps;
|
||||
/// it holds nothing to give them beyond that.
|
||||
pub const DESKTOP: &[Program] = &[Program {
|
||||
serves: false,
|
||||
// The display only to hand on, to programs whose manifest asks for it.
|
||||
uses: &["terminal", "storage", "display"],
|
||||
// Room for one program at a time, the way the shell runs them.
|
||||
spawns: Some(Limits { pages: 256, endpoints: 2, notifications: 2, regions: 2 }),
|
||||
uses: &["terminal", "permission"],
|
||||
storage: Some(Scope { path: b"home", write: true }),
|
||||
..server("shell", b"bin/shell", SMALL)
|
||||
}];
|
||||
|
||||
@@ -8,7 +8,8 @@ use types::badge::Badge;
|
||||
use types::bounded::CapSlot;
|
||||
use types::budget::Limits;
|
||||
use types::permission::{Permission, PermissionSet};
|
||||
use types::spawn::{MAX_NAME, NONE, reply};
|
||||
use types::address::VirtualAddress;
|
||||
use types::spawn::{NONE, reply};
|
||||
|
||||
use crate::launch::Launcher;
|
||||
use crate::load;
|
||||
@@ -67,15 +68,16 @@ impl Spawner {
|
||||
}
|
||||
}
|
||||
|
||||
/// Where init maps a spawn request's file grant while it reads it.
|
||||
const PROGRAM_BUF_VA: VirtualAddress = VirtualAddress::from_raw(0x01A0_0000);
|
||||
|
||||
pub(crate) struct Request {
|
||||
name: [u8; MAX_NAME],
|
||||
len: usize,
|
||||
limits: Limits,
|
||||
}
|
||||
|
||||
impl Decode for Request {
|
||||
fn decode(words: [usize; 4]) -> Option<Self> {
|
||||
types::spawn::parse(words).map(|(name, len, limits)| Self { name, len, limits })
|
||||
types::spawn::parse(words).map(|limits| Self { limits })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +94,9 @@ fn refused(tag: usize) -> ([usize; 4], Option<CapSlot>) {
|
||||
}
|
||||
|
||||
impl Launcher<'_> {
|
||||
fn spawn(&mut self, badge: usize, r: &Request) -> ([usize; 4], Option<CapSlot>) {
|
||||
/// Loads the file `binary` (a storage grant scoped to it) for `badge`'s
|
||||
/// client.
|
||||
fn spawn(&mut self, badge: usize, r: &Request, binary: CapSlot) -> ([usize; 4], Option<CapSlot>) {
|
||||
let Some(client) = badge.checked_sub(1).filter(|c| *c < MAX_CLIENTS) else {
|
||||
return refused(reply::ERROR);
|
||||
};
|
||||
@@ -105,16 +109,16 @@ impl Launcher<'_> {
|
||||
let Some(index) = self.spawner.children.iter().position(Option::is_none) else {
|
||||
return refused(reply::ERROR);
|
||||
};
|
||||
let name = &r.name[..r.len];
|
||||
let (storage, scratch) = self.storage();
|
||||
match storage.open(name) {
|
||||
Ok(h) => {
|
||||
let _ = storage.close(h);
|
||||
}
|
||||
Err(_) => return refused(reply::NOT_FOUND),
|
||||
}
|
||||
let Some(loaded) = load::load_binary(storage, scratch, name, r.limits) else {
|
||||
return refused(reply::ERROR);
|
||||
let Ok(mut file) = libkos::fs::Storage::attach(binary, PROGRAM_BUF_VA) else {
|
||||
return refused(reply::NOT_FOUND);
|
||||
};
|
||||
// The grant is the file: its path inside it is empty.
|
||||
let found = file.open(b"").and_then(|h| file.close(h)).is_ok();
|
||||
let (_, scratch) = self.storage();
|
||||
let loaded = found.then(|| load::load_binary(&mut file, scratch, b"", r.limits)).flatten();
|
||||
file.detach();
|
||||
let Some(loaded) = loaded else {
|
||||
return refused(if found { reply::ERROR } else { reply::NOT_FOUND });
|
||||
};
|
||||
if !self.watch(loaded.process_cap, index) {
|
||||
let _ = syscall::kill(loaded.process_cap);
|
||||
@@ -165,13 +169,18 @@ impl Server for Launcher<'_> {
|
||||
Reply(refused(reply::ERROR).0)
|
||||
}
|
||||
|
||||
fn handle_call(&mut self, badge: usize, words: [usize; 4], _cap: Option<CapSlot>) -> ([usize; 4], Option<libkos::syscall::Give>) {
|
||||
match Request::decode(words) {
|
||||
Some(r) => {
|
||||
let (reply, process) = self.spawn(badge, &r);
|
||||
(reply, process.map(Into::into))
|
||||
fn handle_call(&mut self, badge: usize, words: [usize; 4], cap: Option<CapSlot>) -> ([usize; 4], Option<libkos::syscall::Give>) {
|
||||
match (Request::decode(words), cap) {
|
||||
(Some(r), Some(binary)) => {
|
||||
let (reply, process) = self.spawn(badge, &r, binary);
|
||||
// Init's copy of the grant is done with.
|
||||
syscall::drop_cap(binary);
|
||||
// The client equips the child, and may pass it on to whoever
|
||||
// it runs it for.
|
||||
(reply, process.map(libkos::syscall::Give::keeping_delegate))
|
||||
}
|
||||
None => ([MALFORMED, 0, 0, 0], None),
|
||||
(Some(_), None) => (refused(reply::NOT_FOUND).0, None),
|
||||
(None, _) => ([MALFORMED, 0, 0, 0], None),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user