1#![deny(
2 clippy::clone_on_ref_ptr,
3 clippy::default_trait_access,
4 clippy::doc_markdown,
5 clippy::empty_enum,
6 clippy::empty_line_after_outer_attr,
7 clippy::enum_glob_use,
8 clippy::expl_impl_clone_on_copy,
9 clippy::fallible_impl_from,
10 clippy::float_cmp_const,
11 clippy::items_after_statements,
12 clippy::match_same_arms,
13 clippy::multiple_inherent_impl,
14 clippy::mut_mut,
15 clippy::needless_continue,
16 clippy::print_stdout,
17 clippy::range_plus_one,
18 clippy::single_match_else,
19 clippy::unimplemented,
20 clippy::unnecessary_unwrap,
21 clippy::use_self,
22 clippy::used_underscore_binding,
23 clippy::writeln_empty_string,
24 clippy::wrong_self_convention,
25 missing_copy_implementations,
26 missing_debug_implementations,
27 trivial_casts,
28 trivial_numeric_casts,
29 unreachable_pub,
30 unused_extern_crates,
31 unused_import_braces,
32 unused_qualifications,
33 unused_results,
34 variant_size_differences,
35 warnings
36)]
37
38use std::time::Duration;
39
40use capctl::caps::CapState;
41
42mod cgroups;
43pub mod config;
44pub mod errors;
45mod ffi;
46pub mod run_info;
47pub mod utils;
48
49use config::{Aslr, Config, Interactive, Limits, ShareNet, SpaceUsage, SwapRedirects};
50pub use errors::*;
51use ffi::CloneHandle;
52use run_info::{RunInfo, RunUsage};
53
54pub fn spawn_jail(config: &Config) -> Result<JailHandle> {
55 let user_group_id = ffi::get_user_group_id();
56
57 ffi::set_sig_alarm_handler().map_err(Error::FFIError)?;
58
59 ffi::clone(ShareNet::Share, false, || {
63 ffi::kill_on_parent_death()?;
64 ffi::mount_proc()?;
66 ffi::set_uid_gid_maps(user_group_id)?;
68
69 ffi::clone(config.share_net(), true, || {
70 if config.swap_redirects() == SwapRedirects::Yes {
71 if let Some(stdout) = config.redirect_stdout() {
72 ffi::redirect_fd(ffi::STDOUT, stdout)?;
73 }
74 }
75
76 if let Some(stdin) = config.redirect_stdin() {
77 ffi::redirect_fd(ffi::STDIN, stdin)?;
78 }
79
80 if config.swap_redirects() == SwapRedirects::No {
81 if let Some(stdout) = config.redirect_stdout() {
82 ffi::redirect_fd(ffi::STDOUT, stdout)?;
83 }
84 }
85
86 if let Some(stderr) = config.redirect_stderr() {
87 ffi::redirect_fd(ffi::STDERR, stderr)?;
88 }
89
90 ffi::set_stack_rlimit(config.limits().stack())?;
91 ffi::set_pids_rlimit(config.limits().pids())?;
92
93 if let Some(hierarchy) = config.hierarchy_path() {
95 cgroups::enter_all_cgroups(
96 hierarchy,
97 config.instance_name(),
98 config.limits(),
99 config.clear_usage(),
100 )?;
101 } else {
102 ffi::set_cpu_rlimit(config.limits().user_time())?;
104 ffi::set_memory_rlimit(config.limits().memory())?;
105 }
106
107 ffi::unshare_cgroup()?;
108
109 ffi::remount_private()?;
111
112 if let Some(new_root) = config.new_root() {
113 for mount in config.mounts() {
114 ffi::mount_inside(new_root, mount)?;
115 }
116
117 ffi::pivot_root(new_root, || {
118 ffi::mount_proc()
122 })?;
123 } else {
124 ffi::mount_proc()?;
125 }
126
127 ffi::set_uid_gid_maps((ffi::UserId::ROOT, ffi::GroupId::ROOT))?;
133
134 if config.interactive() == Interactive::No {
135 ffi::move_to_different_process_group()?;
139 }
140
141 if config.aslr() == Aslr::NoRandomize {
142 ffi::disable_aslr()?;
143 }
144
145 if !config.privileged {
146 CapState::empty().set_current()?;
147 ffi::remove_future_privileges()?;
148 }
149
150 ffi::exec_command(config.command(), &config.args(), config.environment())?;
151
152 Ok(())
153 })?
154 .wait(config.limits(), |wall_time| {
155 if let Some(hierarchy) = config.hierarchy_path() {
156 Ok(cgroups::get_usage(
157 hierarchy,
158 config.instance_name(),
159 wall_time,
160 )?)
161 } else {
162 Ok(RunUsage::new(
163 Duration::default(),
164 wall_time,
165 SpaceUsage::default(),
166 ))
167 }
168 })
169 .and_then(|run_info| {
170 run_info.and_then(|option| match option {
171 None => Ok(()),
172 Some(result) => result.map_err(Error::ChildError),
173 })
174 })
175 })
176 .map(JailHandle::new)
177 .map_err(Error::from)
178}
179
180#[allow(missing_debug_implementations)]
181pub struct JailHandle {
182 handle: CloneHandle<Result<RunInfo<()>>>,
183}
184
185impl JailHandle {
186 fn new(handle: CloneHandle<Result<RunInfo<()>>>) -> Self {
187 Self { handle }
188 }
189
190 pub fn wait(self) -> Result<RunInfo<()>> {
191 self.handle
192 .wait(Limits::default(), |_| Ok(RunUsage::default()))
193 .and_then(|run_info| {
194 run_info
195 .success() .and_then(|x| x) .ok_or(Error::SupervisorProcessDiedError)
198 .and_then(|x| x) })
200 }
201}