Skip to content

Commit 18f9ecb

Browse files
committed
jailer: create /dev/net/tun device...
inside the jailer using mknod command and then chown to change the owner to the uid and gid we receive as jailer parameters. It is mandatory to have it there as there is a 1:1 relation between a tap interface and this device. Signed-off-by: Diana Popa <[email protected]>
1 parent e1aed24 commit 18f9ecb

File tree

2 files changed

+60
-26
lines changed

2 files changed

+60
-26
lines changed

jailer/src/env.rs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::process::{Command, Stdio};
77
use libc;
88

99
use super::cgroup::Cgroup;
10-
use super::{Error, JailerArgs, Result};
10+
use super::{into_cstring, Error, JailerArgs, Result};
1111

1212
pub struct Env {
1313
cgroup: Cgroup,
@@ -61,17 +61,18 @@ impl Env {
6161
self.chroot_dir.as_path()
6262
}
6363

64+
pub fn gid(&self) -> u32 {
65+
self.gid
66+
}
67+
68+
pub fn uid(&self) -> u32 {
69+
self.uid
70+
}
71+
6472
pub fn run(self) -> Result<()> {
6573
self.cgroup.attach_pid()?;
6674

67-
// Turn self.chroot_dir into a CString. The expect should not fail, since Linux paths
68-
// only contain valid Unicode chars (do they?), and do not contain null bytes (do they?).
69-
let chroot_dir = CString::new(
70-
self.chroot_dir
71-
.into_os_string()
72-
.into_string()
73-
.expect("Could not convert chroot_dir to String."),
74-
).expect("Could not convert chroot_dir String to CString.");
75+
let chroot_dir: CString = into_cstring(self.chroot_dir)?;
7576
let ret = unsafe { libc::chroot(chroot_dir.as_ptr()) };
7677
if ret < 0 {
7778
return Err(Error::Chroot(ret));

jailer/src/lib.rs

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ mod cgroup;
77
mod env;
88
mod uuid;
99

10-
use std::ffi::OsStr;
11-
use std::fs::{canonicalize, metadata};
10+
use std::ffi::{CString, NulError, OsStr, OsString};
11+
use std::fs::{canonicalize, create_dir_all, metadata};
1212
use std::io;
1313
use std::os::unix::io::AsRawFd;
1414
use std::os::unix::net::UnixListener;
@@ -19,8 +19,7 @@ use env::Env;
1919
use uuid::validate;
2020

2121
pub const KVM_FD: i32 = 3;
22-
pub const DEV_NET_TUN_FD: i32 = 4;
23-
pub const LISTENER_FD: i32 = 5;
22+
pub const LISTENER_FD: i32 = 4;
2423

2524
const SOCKET_FILE_NAME: &str = "api.socket";
2625

@@ -29,9 +28,12 @@ pub enum Error {
2928
Canonicalize(PathBuf, io::Error),
3029
CgroupLineNotFound(&'static str, &'static str),
3130
CgroupLineNotUnique(&'static str, &'static str),
31+
ChangeDevNetTunOwner(sys_util::Error),
3232
Chroot(i32),
3333
Copy(PathBuf, PathBuf, io::Error),
3434
CreateDir(PathBuf, io::Error),
35+
OsStringParsing(PathBuf, OsString),
36+
CStringParsing(String, NulError),
3537
Exec(io::Error),
3638
FileCreate(PathBuf, io::Error),
3739
FileName(PathBuf),
@@ -44,12 +46,11 @@ pub enum Error {
4446
NotAlphanumeric(String),
4547
NumaNode(String),
4648
OpenDevKvm(sys_util::Error),
47-
OpenDevNetTun(sys_util::Error),
49+
MknodDevNetTun(sys_util::Error),
4850
ReadLine(PathBuf, io::Error),
4951
RegEx(regex::Error),
5052
Uid(String),
5153
UnexpectedKvmFd(i32),
52-
UnexpectedDevNetTunFd(i32),
5354
UnexpectedListenerFd(i32),
5455
UnixListener(io::Error),
5556
UnsetCloexec(sys_util::Error),
@@ -129,7 +130,7 @@ pub fn run(args: JailerArgs) -> Result<()> {
129130
// We open /dev/kvm, /dev/tun, and create the listening socket. These file descriptors will be
130131
// passed on to Firecracker post exec, and used via knowing their values in advance.
131132

132-
// TODO: use dup2 to make sure we're actually getting 3, 4, and 5?
133+
// TODO: use dup2 to make sure we're actually getting 3 and 4?
133134

134135
// TODO: can a malicious guest that takes over firecracker use its access to the KVM fd to
135136
// starve the host of resources? (cgroups should take care of that, but do they currently?)
@@ -143,22 +144,43 @@ pub fn run(args: JailerArgs) -> Result<()> {
143144
return Err(Error::UnexpectedKvmFd(ret));
144145
}
145146

146-
// TODO: is RDWR required for /dev/tun (most likely)?
147-
// Safe because we use a constant null-terminated string and verify the result.
147+
let env = Env::new(args)?;
148+
149+
// Here we are creating the /dev/net/tun device inside the jailer.
150+
// Following commands can be translated into bash like this:
151+
// $: mkdir -p $chroot_dir/dev/net
152+
// $: dev_net_tun_path={$chroot_dir}/"tun"
153+
// $: mknod $dev_net_tun_path c 10 200
154+
// www.kernel.org/doc/Documentation/networking/tuntap.txt specifies 10 and 200 as the minor
155+
// and major for the /dev/net/tun device.
156+
let mut chroot_dir = PathBuf::from(env.chroot_dir());
157+
chroot_dir.push("dev/net");
158+
create_dir_all(&chroot_dir).map_err(|e| Error::CreateDir(chroot_dir.clone(), e))?;
159+
160+
let dev_net_tun_path: CString = into_cstring(chroot_dir.join("tun"))?;
161+
// As per sysstat.h:
162+
// S_IFCHR -> character special device
163+
// S_IRUSR -> read permission, owner
164+
// S_IWUSR -> write permission, owner
165+
// See www.kernel.org/doc/Documentation/networking/tuntap.txt, 'Configuration' chapter for
166+
// more clarity.
148167
let ret = unsafe {
149-
libc::open(
150-
"/dev/net/tun\0".as_ptr() as *const libc::c_char,
151-
libc::O_RDWR | libc::O_NONBLOCK,
168+
libc::mknod(
169+
dev_net_tun_path.as_ptr(),
170+
libc::S_IFCHR | libc::S_IRUSR | libc::S_IWUSR,
171+
libc::makedev(10, 200),
152172
)
153173
};
174+
154175
if ret < 0 {
155-
return Err(Error::OpenDevNetTun(sys_util::Error::last()));
156-
}
157-
if ret != DEV_NET_TUN_FD {
158-
return Err(Error::UnexpectedDevNetTunFd(ret));
176+
return Err(Error::MknodDevNetTun(sys_util::Error::last()));
159177
}
160178

161-
let env = Env::new(args)?;
179+
let ret = unsafe { libc::chown(dev_net_tun_path.as_ptr(), env.uid(), env.gid()) };
180+
181+
if ret < 0 {
182+
return Err(Error::ChangeDevNetTunOwner(sys_util::Error::last()));
183+
}
162184

163185
// The unwrap should not fail, since the end of chroot_dir looks like ..../<id>/root
164186
let listener = UnixListener::bind(env.chroot_dir().parent().unwrap().join(SOCKET_FILE_NAME))
@@ -186,3 +208,14 @@ pub fn run(args: JailerArgs) -> Result<()> {
186208

187209
env.run()
188210
}
211+
212+
/// Turns a PathBuf into a CString (c style string).
213+
/// The expect should not fail, since Linux paths only contain valid Unicode chars (do they?),
214+
/// and do not contain null bytes (do they?).
215+
pub fn into_cstring(path: PathBuf) -> Result<CString> {
216+
let path_str = path.clone()
217+
.into_os_string()
218+
.into_string()
219+
.map_err(|e| Error::OsStringParsing(path, e))?;
220+
CString::new(path_str.clone()).map_err(|e| Error::CStringParsing(path_str, e))
221+
}

0 commit comments

Comments
 (0)