@@ -7,8 +7,8 @@ mod cgroup;
7
7
mod env;
8
8
mod uuid;
9
9
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} ;
12
12
use std:: io;
13
13
use std:: os:: unix:: io:: AsRawFd ;
14
14
use std:: os:: unix:: net:: UnixListener ;
@@ -19,8 +19,7 @@ use env::Env;
19
19
use uuid:: validate;
20
20
21
21
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 ;
24
23
25
24
const SOCKET_FILE_NAME : & str = "api.socket" ;
26
25
@@ -29,9 +28,12 @@ pub enum Error {
29
28
Canonicalize ( PathBuf , io:: Error ) ,
30
29
CgroupLineNotFound ( & ' static str , & ' static str ) ,
31
30
CgroupLineNotUnique ( & ' static str , & ' static str ) ,
31
+ ChangeDevNetTunOwner ( sys_util:: Error ) ,
32
32
Chroot ( i32 ) ,
33
33
Copy ( PathBuf , PathBuf , io:: Error ) ,
34
34
CreateDir ( PathBuf , io:: Error ) ,
35
+ OsStringParsing ( PathBuf , OsString ) ,
36
+ CStringParsing ( String , NulError ) ,
35
37
Exec ( io:: Error ) ,
36
38
FileCreate ( PathBuf , io:: Error ) ,
37
39
FileName ( PathBuf ) ,
@@ -44,12 +46,11 @@ pub enum Error {
44
46
NotAlphanumeric ( String ) ,
45
47
NumaNode ( String ) ,
46
48
OpenDevKvm ( sys_util:: Error ) ,
47
- OpenDevNetTun ( sys_util:: Error ) ,
49
+ MknodDevNetTun ( sys_util:: Error ) ,
48
50
ReadLine ( PathBuf , io:: Error ) ,
49
51
RegEx ( regex:: Error ) ,
50
52
Uid ( String ) ,
51
53
UnexpectedKvmFd ( i32 ) ,
52
- UnexpectedDevNetTunFd ( i32 ) ,
53
54
UnexpectedListenerFd ( i32 ) ,
54
55
UnixListener ( io:: Error ) ,
55
56
UnsetCloexec ( sys_util:: Error ) ,
@@ -129,7 +130,7 @@ pub fn run(args: JailerArgs) -> Result<()> {
129
130
// We open /dev/kvm, /dev/tun, and create the listening socket. These file descriptors will be
130
131
// passed on to Firecracker post exec, and used via knowing their values in advance.
131
132
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 ?
133
134
134
135
// TODO: can a malicious guest that takes over firecracker use its access to the KVM fd to
135
136
// 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<()> {
143
144
return Err ( Error :: UnexpectedKvmFd ( ret) ) ;
144
145
}
145
146
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.
148
167
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 ) ,
152
172
)
153
173
} ;
174
+
154
175
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 ( ) ) ) ;
159
177
}
160
178
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
+ }
162
184
163
185
// The unwrap should not fail, since the end of chroot_dir looks like ..../<id>/root
164
186
let listener = UnixListener :: bind ( env. chroot_dir ( ) . parent ( ) . unwrap ( ) . join ( SOCKET_FILE_NAME ) )
@@ -186,3 +208,14 @@ pub fn run(args: JailerArgs) -> Result<()> {
186
208
187
209
env. run ( )
188
210
}
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