Skip to content

Commit 8780e3f

Browse files
committed
First cut of process environment sandbox for rustc
The sandbox can control which environment variables are readable and can set their values. It also maintains a set of path prefixes which can be used for file access; accessing a path without one of the defined prefixes is disallowed.
1 parent 6b16797 commit 8780e3f

File tree

2 files changed

+294
-0
lines changed

2 files changed

+294
-0
lines changed

src/librustc_env_sandbox/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
authors = ["The Rust Project Developers"]
3+
name = "env_sandbox"
4+
version = "0.0.0"
5+
6+
[lib]
7+
name = "env_sandbox"
8+
path = "lib.rs"
9+
crate-type = ["dylib"]
10+
11+
[dev-dependencies]
12+
tempdir = "0.3"

src/librustc_env_sandbox/lib.rs

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
#![deny(warnings)]
11+
12+
#[cfg(test)]
13+
extern crate tempdir;
14+
15+
use std::collections::HashMap;
16+
use std::env;
17+
use std::fs::{self, File};
18+
use std::io;
19+
use std::path::{Path, PathBuf};
20+
21+
// Set up a sandbox to access the compiler's system environment. This includes:
22+
// - what appears in the environment (ie, visible to the `env!()` and `option_env!()` macros)
23+
// - constrain the path prefixs files may be included from
24+
25+
/// Aspects of the compiler's environment which can be sandboxed. If the field is `None`,
26+
/// is unconstrained.
27+
#[derive(Debug, Clone, Default)]
28+
pub struct EnvSandbox {
29+
/// Set of environment variables available. If a variable appears with a `None` value
30+
/// then it means the variable can be read from the real system environment. Otherwise
31+
/// the variable is as it has been defined here.
32+
env: Option<HashMap<String, Option<String>>>,
33+
/// Set of paths from which files can be included. These are normalized to the real path,
34+
/// so all files being opened are also opened via normalized real paths.
35+
paths: Option<Vec<PathBuf>>,
36+
}
37+
38+
/// Builder for an `EnvSandbox`
39+
pub struct EnvSandboxBuilder(EnvSandbox);
40+
41+
impl EnvSandboxBuilder {
42+
/// Construct a new `EnvSandboxBuilder`.
43+
pub fn new() -> Self {
44+
EnvSandboxBuilder(EnvSandbox {
45+
env: None,
46+
paths: None,
47+
})
48+
}
49+
50+
fn env_add_filter<K, V>(&mut self, var: K, val: Option<V>)
51+
where
52+
String: From<K>,
53+
String: From<V>,
54+
{
55+
let var = String::from(var);
56+
let val = val.map(String::from);
57+
58+
let mut env = self.0.env.take().unwrap_or(HashMap::new());
59+
60+
env.insert(var, val);
61+
62+
self.0.env = Some(env);
63+
}
64+
65+
/// Define a new name->value mapping
66+
pub fn env_define<K, V>(&mut self, var: K, val: V) -> &mut Self
67+
where
68+
String: From<K>,
69+
String: From<V>,
70+
{
71+
self.env_add_filter(var, Some(val));
72+
self
73+
}
74+
75+
/// Allow a give name to be accessed from the environment
76+
pub fn env_allow<K>(&mut self, var: K) -> &mut Self
77+
where
78+
String: From<K>,
79+
{
80+
self.env_add_filter::<_, String>(var, None);
81+
self
82+
}
83+
84+
/// Set up an empty mapping, prohibiting access to the process environment without
85+
/// defining any new variables.
86+
pub fn env_clear(&mut self) -> &mut Self {
87+
self.0.env = Some(HashMap::new());
88+
self
89+
}
90+
91+
/// Clear all path prefixes, leaving no valid prefixes. This prevents all file access.
92+
pub fn paths_clear(&mut self) -> &mut Self {
93+
self.0.paths = Some(Vec::new());
94+
self
95+
}
96+
97+
/// Add a path prefix to the allowed set. The path must exist so that it can
98+
/// be canonicalized.
99+
pub fn path_add<P: AsRef<Path>>(&mut self, path: P) -> io::Result<&mut Self> {
100+
let path = path.as_ref();
101+
let path = fs::canonicalize(path)?;
102+
103+
let mut paths = self.0.paths.take().unwrap_or(Vec::new());
104+
paths.push(path);
105+
self.0.paths = Some(paths);
106+
107+
Ok(self)
108+
}
109+
110+
/// Construct an `EnvSandbox` from the builder
111+
pub fn build(self) -> EnvSandbox {
112+
self.0
113+
}
114+
}
115+
116+
impl EnvSandbox {
117+
/// Get an environment variable, either from the real environment
118+
/// or from the locally defined sandbox variables.
119+
pub fn env_get(&self, var: &str) -> Option<String> {
120+
match &self.env {
121+
&None => env::var(var).ok(),
122+
&Some(ref map) => match map.get(var) {
123+
None => None,
124+
Some(&Some(ref val)) => Some(val.to_string()),
125+
Some(&None) => env::var(var).ok(),
126+
},
127+
}
128+
}
129+
130+
/// Return true if a given path has a valid prefix. Fails if the path
131+
/// can't be canonicalized.
132+
pub fn path_ok<P: AsRef<Path>>(&self, path: P) -> io::Result<bool> {
133+
let path = path.as_ref();
134+
let fullpath = fs::canonicalize(path)?;
135+
136+
let ret = if let Some(ref paths) = self.paths {
137+
paths.iter().any(|p| fullpath.starts_with(p))
138+
} else {
139+
true
140+
};
141+
Ok(ret)
142+
}
143+
144+
/// Map a path to itself if it has a valid prefix, otherwise return a suitable
145+
/// error.
146+
pub fn path_lookup<P>(&self, path: P) -> io::Result<P>
147+
where
148+
P: AsRef<Path>,
149+
{
150+
if self.path_ok(&path)? {
151+
Ok(path)
152+
} else {
153+
Err(io::Error::new(
154+
io::ErrorKind::PermissionDenied,
155+
"path does not have a valid prefix",
156+
))
157+
}
158+
}
159+
160+
/// Open a path for reading if it has a valid prefix.
161+
pub fn path_open<P: AsRef<Path>>(&self, path: P) -> io::Result<File> {
162+
File::open(self.path_lookup(path)?)
163+
}
164+
}
165+
166+
#[cfg(test)]
167+
mod test {
168+
use super::*;
169+
170+
use std::env;
171+
use tempdir::TempDir;
172+
173+
#[test]
174+
fn test_env() {
175+
env::set_var("ZUBZUB", "ZIBZIB");
176+
177+
let sb = EnvSandbox::default();
178+
179+
assert_eq!(env::var("ZUBZUB"), Ok("ZIBZIB".to_string()));
180+
assert_eq!(sb.env_get("ZUBZUB"), Some("ZIBZIB".to_string()));
181+
}
182+
183+
#[test]
184+
fn test_env_filter() {
185+
let mut sb = EnvSandboxBuilder::new();
186+
187+
sb.env_define("FOO", "BAR");
188+
189+
let sb = sb.build();
190+
191+
assert_eq!(sb.env_get("FOO"), Some("BAR".to_string()));
192+
assert_eq!(sb.env_get("BLAHBLAH"), None);
193+
}
194+
195+
#[test]
196+
fn test_env_allow() {
197+
let mut sb = EnvSandboxBuilder::new();
198+
199+
sb.env_define("FOO", "BAR")
200+
.env_allow("BLOP")
201+
.env_allow("GLUB");
202+
203+
let sb = sb.build();
204+
205+
env::set_var("ZUBZUB", "ZIBZIB");
206+
env::set_var("BLOP", "Blub");
207+
env::remove_var("GLUB");
208+
209+
assert_eq!(sb.env_get("FOO"), Some("BAR".to_string()));
210+
assert_eq!(sb.env_get("ZUBZUB"), None);
211+
assert_eq!(sb.env_get("BLOP"), Some("Blub".to_string()));
212+
assert_eq!(sb.env_get("GLUB"), None);
213+
}
214+
215+
#[test]
216+
fn test_env_clear() {
217+
let mut sb = EnvSandboxBuilder::new();
218+
219+
sb.env_define("FOO", "BAR")
220+
.env_allow("BLOP")
221+
.env_allow("GLUB")
222+
.env_clear();
223+
224+
let sb = sb.build();
225+
226+
env::set_var("ZUBZUB", "ZIBZIB");
227+
env::set_var("BLOP", "Blub");
228+
env::remove_var("GLUB");
229+
230+
assert_eq!(sb.env_get("FOO"), None);
231+
assert_eq!(sb.env_get("ZUBZUB"), None);
232+
assert_eq!(sb.env_get("BLOP"), None);
233+
assert_eq!(sb.env_get("GLUB"), None);
234+
}
235+
236+
#[test]
237+
fn test_path() {
238+
let dir = TempDir::new("test").expect("tempdir failed");
239+
let subdir = dir.path().join("subdir");
240+
fs::create_dir(&subdir).expect("subdir failed");
241+
242+
let sb = EnvSandbox::default();
243+
244+
assert!(sb.path_ok(&dir).unwrap());
245+
assert!(sb.path_ok(dir.path().parent().unwrap()).unwrap());
246+
assert!(sb.path_ok(&subdir).unwrap());
247+
assert!(sb.path_ok("/").unwrap());
248+
}
249+
250+
#[test]
251+
fn test_path_filter() {
252+
let dir = TempDir::new("test").expect("tempdir failed");
253+
let subdir = dir.path().join("subdir");
254+
fs::create_dir(&subdir).expect("subdir failed");
255+
256+
let mut sb = EnvSandboxBuilder::new();
257+
sb.path_add(&dir).unwrap();
258+
let sb = sb.build();
259+
260+
assert!(sb.path_ok(&dir).unwrap());
261+
assert!(!sb.path_ok(dir.path().parent().unwrap()).unwrap());
262+
assert!(sb.path_ok(&subdir).unwrap());
263+
assert!(!sb.path_ok("/").unwrap());
264+
}
265+
266+
#[test]
267+
fn test_path_clear() {
268+
let dir = TempDir::new("test").expect("tempdir failed");
269+
let subdir = dir.path().join("subdir");
270+
fs::create_dir(&subdir).expect("subdir failed");
271+
272+
let mut sb = EnvSandboxBuilder::new();
273+
sb.path_add(&dir).unwrap();
274+
sb.paths_clear();
275+
let sb = sb.build();
276+
277+
assert!(!sb.path_ok(&dir).unwrap());
278+
assert!(!sb.path_ok(dir.path().parent().unwrap()).unwrap());
279+
assert!(!sb.path_ok(&subdir).unwrap());
280+
assert!(!sb.path_ok("/").unwrap());
281+
}
282+
}

0 commit comments

Comments
 (0)