caps revoked on process kill

This commit is contained in:
Drew Tada 2024-02-08 10:22:03 -05:00
parent f0c8c2308c
commit 4d97def768
3 changed files with 55 additions and 37 deletions

View File

@ -23,7 +23,6 @@ enum TerminalAction {
alias: String,
process: Option<ProcessId>,
},
ProcessEnded(Vec<(ProcessId, Capability)>),
}
#[derive(Serialize, Deserialize)]
@ -145,12 +144,6 @@ impl Guest for Component {
Err(e) => println!("terminal: {e}"),
};
}
TerminalAction::ProcessEnded(drop_caps) => {
match handle_process_cleanup(drop_caps) {
Ok(()) => continue,
Err(e) => println!("terminal: {e}"),
}
}
}
} else {
println!("terminal: ignoring message from: {}", source);
@ -261,17 +254,7 @@ fn handle_run(
id: parsed_new_process_id.clone(),
wasm_bytes_handle: wasm_path.clone(),
wit_version: None,
on_exit: kt::OnExit::Requests(vec![(
kt::de_wit_address(our.clone()),
kt::Request {
inherit: false,
expects_response: None,
body: serde_json::to_vec(&TerminalAction::ProcessEnded(granted_caps))?,
metadata: None,
capabilities: vec![],
},
None,
)]),
on_exit: kt::OnExit::None,
initial_capabilities: HashSet::new(),
public: entry.public,
})?)
@ -318,7 +301,6 @@ fn handle_run(
}
}
// always give it the cap to message the terminal back
// NOTE a malicious script could use this to drop a ton of caps from other processes
requested_caps.push(kt::de_wit_capability(Capability {
issuer: our.clone(),
params: "\"messaging\"".to_string(),
@ -341,7 +323,7 @@ fn handle_run(
parsed_new_process_id.clone(),
wasm_path.clone(),
"None",
kt::OnExit::None, // TODO fix this
kt::OnExit::None,
entry.public,
{
let mut caps_string = "[".to_string();
@ -426,20 +408,6 @@ fn handle_alias_change(
}
}
fn handle_process_cleanup(caps_to_remove: Vec<(ProcessId, Capability)>) -> anyhow::Result<()> {
for (process, cap) in caps_to_remove {
println!("terminal: cleaning up process {}, {}", process, cap);
Request::new()
.target(("our", "kernel", "distro", "sys"))
.body(serde_json::to_vec(&kt::KernelCommand::DropCapabilities {
target: process,
capabilities: vec![kt::de_wit_capability(cap)],
})?)
.send()?;
}
Ok(())
}
fn get_entry(process: &ProcessId) -> anyhow::Result<kt::DotScriptsEntry> {
let drive_path = format!("/{}:{}/pkg", process.package(), process.publisher());
Request::new()

View File

@ -86,6 +86,7 @@ async fn handle_kernel_request(
senders: &mut Senders,
process_handles: &mut ProcessHandles,
process_map: &mut t::ProcessMap,
reverse_cap_index: &mut t::ReverseCapIndex,
caps_oracle: t::CapMessageSender,
engine: &Engine,
) {
@ -334,7 +335,16 @@ async fn handle_kernel_request(
)
})
.collect();
entry.capabilities.extend(signed_caps);
entry.capabilities.extend(signed_caps.clone());
// add these to reverse cap index
for (cap, _) in &signed_caps {
reverse_cap_index
.entry(cap.clone().issuer.process)
.or_insert_with(HashMap::new)
.entry(target.clone())
.or_insert_with(Vec::new)
.push(cap.clone());
}
let _ = persist_state(&our_name, &send_to_loop, process_map).await;
}
t::KernelCommand::DropCapabilities {
@ -444,7 +454,14 @@ async fn handle_kernel_request(
t::KernelCommand::KillProcess(process_id) => {
// brutal and savage killing: aborting the task.
// do not do this to a process if you don't want to risk
// dropped messages / un-replied-to-requests
// dropped messages / un-replied-to-requests / revoked caps
caps_oracle
.send(t::CapMessage::RevokeAll {
on: process_id.clone(),
responder: tokio::sync::oneshot::channel().0,
})
.await
.expect("event loop: fatal: sender died");
let _ = senders.remove(&process_id);
let process_handle = match process_handles.remove(&process_id) {
Some(ph) => ph,
@ -643,6 +660,7 @@ pub async fn kernel(
our: t::Identity,
keypair: Arc<signature::Ed25519KeyPair>,
mut process_map: t::ProcessMap,
mut reverse_cap_index: t::ReverseCapIndex,
caps_oracle_sender: t::CapMessageSender,
mut caps_oracle_receiver: t::CapMessageReceiver,
send_to_loop: t::MessageSender,
@ -1025,6 +1043,7 @@ pub async fn kernel(
&mut senders,
&mut process_handles,
&mut process_map,
&mut reverse_cap_index,
caps_oracle_sender.clone(),
&engine,
).await;
@ -1081,7 +1100,16 @@ pub async fn kernel(
cap.clone(),
keypair.sign(&rmp_serde::to_vec(&cap).unwrap()).as_ref().to_vec()
)).collect();
entry.capabilities.extend(signed_caps);
entry.capabilities.extend(signed_caps.clone());
// now we have to insert all caps into the reverse cap index
for (cap, _) in &signed_caps {
reverse_cap_index
.entry(cap.clone().issuer.process)
.or_insert_with(HashMap::new)
.entry(on.clone())
.or_insert_with(Vec::new)
.push(cap.clone());
}
let _ = persist_state(&our.name, &send_to_loop, &process_map).await;
let _ = responder.send(true);
},
@ -1115,6 +1143,21 @@ pub async fn kernel(
}
);
},
t::CapMessage::RevokeAll { on, responder } => {
let Some(granter) = reverse_cap_index.get(&on) else {
let _ = responder.send(true);
continue;
};
for (grantee, caps) in granter {
let Some(entry) = process_map.get_mut(&grantee) else {
continue;
};
for cap in caps {
entry.capabilities.remove(&cap);
}
}
let _ = responder.send(true);
}
t::CapMessage::FilterCaps { on, caps, responder } => {
let _ = responder.send(
match process_map.get(&on) {

View File

@ -989,6 +989,11 @@ pub enum CapMessage {
on: ProcessId,
responder: tokio::sync::oneshot::Sender<Vec<(Capability, Vec<u8>)>>,
},
/// Remove all caps issued by `on` from every process on the entire system
RevokeAll {
on: ProcessId,
responder: tokio::sync::oneshot::Sender<bool>,
},
/// before `on` sends a message, filter out any bogus caps it may have attached, sign any new
/// caps it may have created, and retreive the signature for the caps in its store.
FilterCaps {
@ -998,6 +1003,8 @@ pub enum CapMessage {
},
}
pub type ReverseCapIndex = HashMap<ProcessId, HashMap<ProcessId, Vec<Capability>>>;
pub type ProcessMap = HashMap<ProcessId, PersistedProcess>;
#[derive(Clone, Debug, Serialize, Deserialize)]