@@ -7,8 +7,8 @@ mod cgroup;
77mod env;
88mod 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} ;
1212use std:: io;
1313use std:: os:: unix:: io:: AsRawFd ;
1414use std:: os:: unix:: net:: UnixListener ;
@@ -19,8 +19,7 @@ use env::Env;
1919use uuid:: validate;
2020
2121pub 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
2524const 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