blob: 957e3cdae4eb9bba9b4027fc108ae1eea43ed3d4 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
use crate::windows::checked;
use maka_sandbox::windows::Account;
use serde::{Serialize, de::DeserializeOwned};
use std::{
io,
mem::size_of,
os::windows::io::{AsRawHandle, BorrowedHandle},
ptr,
time::Duration,
};
use tokio::{
io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt},
net::windows::named_pipe::{NamedPipeServer, ServerOptions},
};
use uuid::Uuid;
use windows_sys::Win32::{
Foundation::LocalFree,
Security::{
Authorization::ConvertStringSecurityDescriptorToSecurityDescriptorW, SECURITY_ATTRIBUTES,
},
System::{Pipes::GetNamedPipeClientProcessId, Threading::GetProcessId},
};
pub struct Endpoint {
id: Uuid,
server: NamedPipeServer,
}
pub struct Channel(NamedPipeServer);
impl Endpoint {
pub fn new(account: &Account) -> io::Result<Self> {
Self::principal(account.sid())
}
pub(super) fn administrative() -> io::Result<Self> {
Self::principal("BA")
}
fn principal(identity: &str) -> io::Result<Self> {
let id = Uuid::new_v4();
// Only validated account SIDs or the built-in Administrators alias.
let sddl: Vec<_> = format!("D:P(A;;GA;;;OW)(A;;GA;;;SY)(A;;GRGW;;;{identity})")
.encode_utf16()
.chain(Some(0))
.collect();
let mut descriptor = ptr::null_mut();
checked(unsafe {
ConvertStringSecurityDescriptorToSecurityDescriptorW(
sddl.as_ptr(),
1,
&mut descriptor,
ptr::null_mut(),
)
})?;
let attributes = SECURITY_ATTRIBUTES {
nLength: size_of::<SECURITY_ATTRIBUTES>() as u32,
lpSecurityDescriptor: descriptor,
bInheritHandle: 0,
};
let server = unsafe {
ServerOptions::new()
.first_pipe_instance(true)
.max_instances(1)
.reject_remote_clients(true)
.create_with_security_attributes_raw(
name(id),
(&attributes as *const SECURITY_ATTRIBUTES)
.cast_mut()
.cast(),
)
};
unsafe {
LocalFree(descriptor);
}
Ok(Self {
id,
server: server?,
})
}
pub fn id(&self) -> Uuid {
self.id
}
/// No credential or command is sent until the connecting process matches
/// the live process handle returned by the account launch.
pub async fn accept(self, process: BorrowedHandle<'_>) -> io::Result<Channel> {
let expected = unsafe { GetProcessId(process.as_raw_handle()) };
if expected == 0 {
return Err(io::Error::last_os_error());
}
let process = process.try_clone_to_owned()?;
tokio::time::timeout(Duration::from_secs(15), async {
tokio::select! {
biased;
connected = self.server.connect() => connected,
status = crate::windows::wait_process(&process) => {
Err(io::Error::other(format!("sandbox helper exited before connecting: {}", status?)))
}
}
})
.await
.map_err(|_| {
io::Error::new(io::ErrorKind::TimedOut, "sandbox runner did not connect")
})??;
let mut client = 0;
checked(unsafe { GetNamedPipeClientProcessId(self.server.as_raw_handle(), &mut client) })?;
if client != expected {
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"unexpected sandbox runner peer",
));
}
Ok(Channel(self.server))
}
}
impl Channel {
pub async fn send(&mut self, request: &impl Serialize) -> io::Result<()> {
send(&mut self.0, request).await
}
pub async fn receive<T: DeserializeOwned>(&mut self) -> io::Result<T> {
receive(&mut self.0).await
}
}
pub(super) fn name(id: Uuid) -> String {
format!(r"\\.\pipe\maka-sandbox-{id}")
}
pub(super) async fn send(
output: &mut (impl AsyncWrite + Unpin),
value: &impl Serialize,
) -> io::Result<()> {
let bytes = serde_json::to_vec(value).map_err(io::Error::other)?;
if u32::try_from(bytes.len()).is_err() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"sandbox frame exceeds limit",
));
}
output.write_u32_le(bytes.len() as u32).await?;
output.write_all(&bytes).await?;
output.flush().await
}
pub(super) async fn receive<T: DeserializeOwned>(
input: &mut (impl AsyncRead + Unpin),
) -> io::Result<T> {
let size = input.read_u32_le().await? as usize;
let mut bytes = vec![0; size];
input.read_exact(&mut bytes).await?;
serde_json::from_slice(&bytes).map_err(io::Error::other)
}