Skip to content

Commit babaf05

Browse files
committed
work
1 parent 44ddabb commit babaf05

File tree

9 files changed

+307
-153
lines changed

9 files changed

+307
-153
lines changed

Cargo.lock

Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ serde = { version = "1.0.106", features = ["derive"] }
1010
backtrace = "0.3.46"
1111
thiserror = "1.0.19"
1212
anyhow = "1.0.32"
13+
tracing = "0.1.19"
1314

1415
[target.'cfg(target_os="linux")'.dependencies]
1516
tiny-nix-ipc = { git = "https://github.com/myfreeweb/tiny-nix-ipc", features = ["ser_json", "zero_copy"] }
@@ -21,7 +22,7 @@ serde_json = "1.0.51"
2122

2223
[target.'cfg(target_os="windows")'.dependencies]
2324
winapi = { version = "0.3.9", features = ["std", "processthreadsapi", "jobapi2", "errhandlingapi",
24-
"namedpipeapi", "fileapi", "synchapi", "winnt", "userenv", "handleapi"] }
25+
"namedpipeapi", "fileapi", "synchapi", "winnt", "userenv", "handleapi", "securitybaseapi", "sddl"] }
2526

2627
[workspace]
2728
members = ["minion-ffi", ".", "minion-tests", "minion-cli"]

rustfmt.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
merge_imports = true

src/windows.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
mod child;
44
pub mod error;
55
mod pipe;
6-
mod sandbox;
6+
mod constrain;
77
mod spawn;
8+
mod sandbox;
9+
mod isolate;
810

911
pub use error::Error;
1012
pub use pipe::{ReadPipe, WritePipe};
@@ -36,5 +38,6 @@ impl crate::Backend for WindowsBackend {
3638
&self,
3739
options: crate::ChildProcessOptions<Self::Sandbox>,
3840
) -> Result<Self::ChildProcess, Self::Error> {
41+
todo!()
3942
}
4043
}

src/windows/constrain.rs

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
//! Implements resource limits
2+
3+
use std::{ffi::OsString, os::windows::ffi::OsStrExt};
4+
5+
use crate::{
6+
windows::{Cvt, Error},
7+
ResourceUsageData,
8+
};
9+
10+
use winapi::um::{
11+
handleapi::CloseHandle,
12+
jobapi2::{
13+
AssignProcessToJobObject, CreateJobObjectW, QueryInformationJobObject,
14+
SetInformationJobObject, TerminateJobObject,
15+
},
16+
winnt::{
17+
JobObjectBasicAccountingInformation, JobObjectExtendedLimitInformation,
18+
JobObjectLimitViolationInformation, HANDLE, JOBOBJECT_BASIC_ACCOUNTING_INFORMATION,
19+
JOBOBJECT_EXTENDED_LIMIT_INFORMATION, JOBOBJECT_LIMIT_VIOLATION_INFORMATION,
20+
JOB_OBJECT_LIMIT_ACTIVE_PROCESS, JOB_OBJECT_LIMIT_JOB_MEMORY, JOB_OBJECT_LIMIT_JOB_TIME,
21+
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE,
22+
},
23+
};
24+
25+
/// Responsible for resource isolation & adding & killing
26+
#[derive(Debug)]
27+
pub(crate) struct Job {
28+
handle: HANDLE,
29+
}
30+
31+
unsafe impl Send for Job {}
32+
unsafe impl Sync for Job {}
33+
34+
impl Job {
35+
pub(crate) fn new(jail_id: &str) -> Result<Self, Error> {
36+
let name: OsString = format!("minion-sandbox-job-{}", jail_id).into();
37+
let name: Vec<u16> = name.encode_wide().collect();
38+
let handle = unsafe {
39+
Cvt::nonzero(CreateJobObjectW(std::ptr::null_mut(), name.as_ptr()) as i32)? as HANDLE
40+
};
41+
Ok(Self { handle })
42+
}
43+
pub(crate) fn enable_resource_limits(
44+
&mut self,
45+
options: &crate::SandboxOptions,
46+
) -> Result<(), Error> {
47+
let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION = unsafe { std::mem::zeroed() };
48+
info.JobMemoryLimit = options.memory_limit as usize;
49+
info.BasicLimitInformation.ActiveProcessLimit = options.max_alive_process_count;
50+
unsafe {
51+
*info
52+
.BasicLimitInformation
53+
.PerJobUserTimeLimit
54+
.QuadPart_mut() = (options.cpu_time_limit.as_nanos() / 100) as i64;
55+
};
56+
info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_JOB_MEMORY
57+
| JOB_OBJECT_LIMIT_ACTIVE_PROCESS
58+
| JOB_OBJECT_LIMIT_JOB_TIME
59+
// let's make sure sandbox will die if we panic / abort
60+
| JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
61+
unsafe {
62+
Cvt::nonzero(SetInformationJobObject(
63+
self.handle,
64+
JobObjectExtendedLimitInformation,
65+
(&mut info as *mut JOBOBJECT_EXTENDED_LIMIT_INFORMATION).cast(),
66+
sizeof::<JOBOBJECT_EXTENDED_LIMIT_INFORMATION>(),
67+
))?;
68+
}
69+
Ok(())
70+
}
71+
pub(crate) fn kill(&self) -> Result<(), Error> {
72+
unsafe { Cvt::nonzero(TerminateJobObject(self.handle, 0xDEADBEEF)).map(|_| ()) }
73+
}
74+
pub(crate) fn add_process(&self, process_handle: HANDLE) -> Result<(), Error> {
75+
unsafe { Cvt::nonzero(AssignProcessToJobObject(self.handle, process_handle)).map(|_| ()) }
76+
}
77+
pub(crate) fn resource_usage(&self) -> Result<crate::ResourceUsageData, Error> {
78+
let cpu = unsafe {
79+
let mut info: JOBOBJECT_BASIC_ACCOUNTING_INFORMATION = std::mem::zeroed();
80+
Cvt::nonzero(QueryInformationJobObject(
81+
self.handle,
82+
JobObjectBasicAccountingInformation,
83+
(&mut info as *mut JOBOBJECT_BASIC_ACCOUNTING_INFORMATION).cast(),
84+
sizeof::<JOBOBJECT_BASIC_ACCOUNTING_INFORMATION>(),
85+
std::ptr::null_mut(),
86+
))?;
87+
88+
let user_ticks = *info.TotalUserTime.QuadPart() as u64;
89+
let kernel_ticks = *info.TotalKernelTime.QuadPart() as u64;
90+
(user_ticks + kernel_ticks) * 100
91+
};
92+
let memory = unsafe {
93+
let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION = std::mem::zeroed();
94+
Cvt::nonzero(QueryInformationJobObject(
95+
self.handle,
96+
JobObjectExtendedLimitInformation,
97+
(&mut info as *mut JOBOBJECT_EXTENDED_LIMIT_INFORMATION).cast(),
98+
sizeof::<JOBOBJECT_EXTENDED_LIMIT_INFORMATION>(),
99+
std::ptr::null_mut(),
100+
))?;
101+
info.PeakJobMemoryUsed as u64
102+
};
103+
104+
Ok(ResourceUsageData {
105+
time: Some(cpu),
106+
memory: Some(memory),
107+
})
108+
}
109+
pub(crate) fn check_cpu_tle(&self) -> Result<bool, Error> {
110+
unsafe {
111+
let mut info: JOBOBJECT_LIMIT_VIOLATION_INFORMATION = std::mem::zeroed();
112+
Cvt::nonzero(QueryInformationJobObject(
113+
self.handle,
114+
JobObjectLimitViolationInformation,
115+
(&mut info as *mut JOBOBJECT_LIMIT_VIOLATION_INFORMATION).cast(),
116+
sizeof::<JOBOBJECT_LIMIT_VIOLATION_INFORMATION>(),
117+
std::ptr::null_mut(),
118+
))?;
119+
let viol = info.ViolationLimitFlags & JOB_OBJECT_LIMIT_JOB_TIME;
120+
Ok(viol != 0)
121+
}
122+
}
123+
124+
pub(crate) fn check_real_tle(&self) -> Result<bool, Error> {
125+
// TODO
126+
Ok(false)
127+
}
128+
}
129+
130+
impl Drop for Job {
131+
fn drop(&mut self) {
132+
unsafe {
133+
CloseHandle(self.handle);
134+
}
135+
}
136+
}
137+
138+
fn sizeof<T>() -> u32 {
139+
let sz = std::mem::size_of::<T>();
140+
assert!(sz <= (u32::max_value() as usize));
141+
sz as u32
142+
}

src/windows/error.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#[derive(Debug, thiserror::Error)]
22
pub enum Error {
3-
#[error("winapi call failed")]
3+
#[error("winapi call failed: {errno}")]
44
Syscall { errno: u32 },
5+
#[error("hresult call failed: {hresult}")]
6+
Hresult { hresult: i32 },
57
}
68

79
impl From<u32> for Error {
@@ -12,9 +14,9 @@ impl From<u32> for Error {
1214

1315
impl Error {
1416
pub(crate) fn last() -> Self {
15-
Error::Syscall {
16-
errno: unsafe { winapi::um::errhandlingapi::GetLastError() },
17-
}
17+
let errno = unsafe { winapi::um::errhandlingapi::GetLastError() };
18+
tracing::error!(errno = errno, "win32 error");
19+
Error::Syscall { errno }
1820
}
1921
}
2022

@@ -32,4 +34,14 @@ impl Cvt {
3234
Err(Error::last())
3335
}
3436
}
37+
38+
/// Checks HRESULT is successful
39+
pub fn hresult(hr: winapi::shared::winerror::HRESULT) -> Result<(), Error> {
40+
if winapi::shared::winerror::SUCCEEDED(hr) {
41+
Ok(())
42+
} else {
43+
tracing::error!(result = hr, "Unsuccessful HRESULT");
44+
Err(Error::Hresult { hresult: hr })
45+
}
46+
}
3547
}

src/windows/isolate.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//! Implements security restrictions
2+
use crate::windows::{Cvt, Error};
3+
use std::{
4+
ffi::OsString,
5+
os::windows::ffi::{OsStrExt, OsStringExt},
6+
};
7+
use tracing::instrument;
8+
use winapi::{
9+
shared::sddl::ConvertSidToStringSidW,
10+
um::{
11+
securitybaseapi::FreeSid,
12+
userenv::{CreateAppContainerProfile, DeleteAppContainerProfile},
13+
winbase::LocalFree,
14+
winnt::{PSID, SECURITY_CAPABILITIES},
15+
},
16+
};
17+
18+
/// Represents one AppContainer
19+
#[derive(Debug)]
20+
pub(crate) struct Profile {
21+
/// Pointer to the Security Identifier (SID) of this container
22+
sid: PSID,
23+
}
24+
25+
unsafe impl Send for Profile {}
26+
unsafe impl Sync for Profile {}
27+
28+
impl Profile {
29+
/// Creates new profile. Takes new, unique sandbox_id.
30+
#[instrument(skip(sandbox_id))]
31+
pub(crate) fn new(sandbox_id: &str) -> Result<Profile, Error> {
32+
tracing::info!(sandbox_id, "creating profile");
33+
let profile_name = OsString::from(format!("minion-sandbox-appcontainer-{}", sandbox_id));
34+
let mut profile_name: Vec<u16> = profile_name.encode_wide().collect();
35+
profile_name.push(0);
36+
let profile_name = profile_name.as_ptr();
37+
let mut sid = std::ptr::null_mut();
38+
unsafe {
39+
let mut sid_string_repr = std::ptr::null_mut();
40+
let res = ConvertSidToStringSidW(sid, &mut sid_string_repr);
41+
if res != 0 {
42+
let mut cnt = 0;
43+
while *(sid_string_repr.add(cnt)) == 0 {
44+
cnt += 1;
45+
}
46+
let repr = OsString::from_wide(std::slice::from_raw_parts(sid_string_repr, cnt));
47+
let repr = repr.to_string_lossy();
48+
tracing::info!(sid=%repr, "obtained SID");
49+
LocalFree(sid_string_repr.cast());
50+
}
51+
}
52+
unsafe {
53+
Cvt::hresult(CreateAppContainerProfile(
54+
profile_name,
55+
profile_name,
56+
profile_name,
57+
std::ptr::null_mut(),
58+
0,
59+
&mut sid,
60+
))?;
61+
}
62+
Ok(Profile { sid })
63+
}
64+
/// Returns `SECURITY_CAPABILITIES` representing this container.
65+
#[instrument(skip(self))]
66+
pub(crate) fn get_security_capabilities(&self) -> SECURITY_CAPABILITIES {
67+
let mut caps: SECURITY_CAPABILITIES = unsafe { std::mem::zeroed() };
68+
caps.CapabilityCount = 0;
69+
caps.AppContainerSid = self.sid;
70+
caps
71+
}
72+
}
73+
74+
impl Drop for Profile {
75+
fn drop(&mut self) {
76+
unsafe {
77+
FreeSid(self.sid);
78+
}
79+
}
80+
}

0 commit comments

Comments
 (0)