1 // Impelmentation note: to allow unprivileged users to run it, this test makes
2 // use of user and mount namespaces. On systems that allow unprivileged user
3 // namespaces (Linux >= 3.8 compiled with CONFIG_USER_NS), the test should run
4 // without root.
5
6 extern crate libc;
7 extern crate nix;
8 extern crate tempfile;
9
10 #[cfg(target_os = "linux")]
11 mod test_mount {
12 use std::fs::{self, File};
13 use std::io::{self, Read, Write};
14 use std::os::unix::fs::OpenOptionsExt;
15 use std::os::unix::fs::PermissionsExt;
16 use std::process::{self, Command};
17
18 use libc::{EACCES, EROFS};
19
20 use nix::errno::Errno;
21 use nix::mount::{mount, umount, MsFlags};
22 use nix::sched::{unshare, CloneFlags};
23 use nix::sys::stat::{self, Mode};
24 use nix::unistd::getuid;
25
26 use tempfile;
27
28 static SCRIPT_CONTENTS: &'static [u8] = b"#!/bin/sh
29 exit 23";
30
31 const EXPECTED_STATUS: i32 = 23;
32
33 const NONE: Option<&'static [u8]> = None;
test_mount_tmpfs_without_flags_allows_rwx()34 pub fn test_mount_tmpfs_without_flags_allows_rwx() {
35 let tempdir = tempfile::tempdir().unwrap();
36
37 mount(NONE,
38 tempdir.path(),
39 Some(b"tmpfs".as_ref()),
40 MsFlags::empty(),
41 NONE)
42 .unwrap_or_else(|e| panic!("mount failed: {}", e));
43
44 let test_path = tempdir.path().join("test");
45
46 // Verify write.
47 fs::OpenOptions::new()
48 .create(true)
49 .write(true)
50 .mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits())
51 .open(&test_path)
52 .or_else(|e|
53 if Errno::from_i32(e.raw_os_error().unwrap()) == Errno::EOVERFLOW {
54 // Skip tests on certain Linux kernels which have a bug
55 // regarding tmpfs in namespaces.
56 // Ubuntu 14.04 and 16.04 are known to be affected; 16.10 is
57 // not. There is no legitimate reason for open(2) to return
58 // EOVERFLOW here.
59 // https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1659087
60 let stderr = io::stderr();
61 let mut handle = stderr.lock();
62 writeln!(handle, "Buggy Linux kernel detected. Skipping test.")
63 .unwrap();
64 process::exit(0);
65 } else {
66 panic!("open failed: {}", e);
67 }
68 )
69 .and_then(|mut f| f.write(SCRIPT_CONTENTS))
70 .unwrap_or_else(|e| panic!("write failed: {}", e));
71
72 // Verify read.
73 let mut buf = Vec::new();
74 File::open(&test_path)
75 .and_then(|mut f| f.read_to_end(&mut buf))
76 .unwrap_or_else(|e| panic!("read failed: {}", e));
77 assert_eq!(buf, SCRIPT_CONTENTS);
78
79 // Verify execute.
80 assert_eq!(EXPECTED_STATUS,
81 Command::new(&test_path)
82 .status()
83 .unwrap_or_else(|e| panic!("exec failed: {}", e))
84 .code()
85 .unwrap_or_else(|| panic!("child killed by signal")));
86
87 umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {}", e));
88 }
89
test_mount_rdonly_disallows_write()90 pub fn test_mount_rdonly_disallows_write() {
91 let tempdir = tempfile::tempdir().unwrap();
92
93 mount(NONE,
94 tempdir.path(),
95 Some(b"tmpfs".as_ref()),
96 MsFlags::MS_RDONLY,
97 NONE)
98 .unwrap_or_else(|e| panic!("mount failed: {}", e));
99
100 // EROFS: Read-only file system
101 assert_eq!(EROFS as i32,
102 File::create(tempdir.path().join("test")).unwrap_err().raw_os_error().unwrap());
103
104 umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {}", e));
105 }
106
test_mount_noexec_disallows_exec()107 pub fn test_mount_noexec_disallows_exec() {
108 let tempdir = tempfile::tempdir().unwrap();
109
110 mount(NONE,
111 tempdir.path(),
112 Some(b"tmpfs".as_ref()),
113 MsFlags::MS_NOEXEC,
114 NONE)
115 .unwrap_or_else(|e| panic!("mount failed: {}", e));
116
117 let test_path = tempdir.path().join("test");
118
119 fs::OpenOptions::new()
120 .create(true)
121 .write(true)
122 .mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits())
123 .open(&test_path)
124 .and_then(|mut f| f.write(SCRIPT_CONTENTS))
125 .unwrap_or_else(|e| panic!("write failed: {}", e));
126
127 // Verify that we cannot execute despite a+x permissions being set.
128 let mode = stat::Mode::from_bits_truncate(fs::metadata(&test_path)
129 .map(|md| md.permissions().mode())
130 .unwrap_or_else(|e| {
131 panic!("metadata failed: {}", e)
132 }));
133
134 assert!(mode.contains(Mode::S_IXUSR | Mode::S_IXGRP | Mode::S_IXOTH),
135 "{:?} did not have execute permissions",
136 &test_path);
137
138 // EACCES: Permission denied
139 assert_eq!(EACCES as i32,
140 Command::new(&test_path).status().unwrap_err().raw_os_error().unwrap());
141
142 umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {}", e));
143 }
144
test_mount_bind()145 pub fn test_mount_bind() {
146 let tempdir = tempfile::tempdir().unwrap();
147 let file_name = "test";
148
149 {
150 let mount_point = tempfile::tempdir().unwrap();
151
152 mount(Some(tempdir.path()),
153 mount_point.path(),
154 NONE,
155 MsFlags::MS_BIND,
156 NONE)
157 .unwrap_or_else(|e| panic!("mount failed: {}", e));
158
159 fs::OpenOptions::new()
160 .create(true)
161 .write(true)
162 .mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits())
163 .open(mount_point.path().join(file_name))
164 .and_then(|mut f| f.write(SCRIPT_CONTENTS))
165 .unwrap_or_else(|e| panic!("write failed: {}", e));
166
167 umount(mount_point.path()).unwrap_or_else(|e| panic!("umount failed: {}", e));
168 }
169
170 // Verify the file written in the mount shows up in source directory, even
171 // after unmounting.
172
173 let mut buf = Vec::new();
174 File::open(tempdir.path().join(file_name))
175 .and_then(|mut f| f.read_to_end(&mut buf))
176 .unwrap_or_else(|e| panic!("read failed: {}", e));
177 assert_eq!(buf, SCRIPT_CONTENTS);
178 }
179
setup_namespaces()180 pub fn setup_namespaces() {
181 // Hold on to the uid in the parent namespace.
182 let uid = getuid();
183
184 unshare(CloneFlags::CLONE_NEWNS | CloneFlags::CLONE_NEWUSER).unwrap_or_else(|e| {
185 let stderr = io::stderr();
186 let mut handle = stderr.lock();
187 writeln!(handle,
188 "unshare failed: {}. Are unprivileged user namespaces available?",
189 e).unwrap();
190 writeln!(handle, "mount is not being tested").unwrap();
191 // Exit with success because not all systems support unprivileged user namespaces, and
192 // that's not what we're testing for.
193 process::exit(0);
194 });
195
196 // Map user as uid 1000.
197 fs::OpenOptions::new()
198 .write(true)
199 .open("/proc/self/uid_map")
200 .and_then(|mut f| f.write(format!("1000 {} 1\n", uid).as_bytes()))
201 .unwrap_or_else(|e| panic!("could not write uid map: {}", e));
202 }
203 }
204
205
206 // Test runner
207
208 /// Mimic normal test output (hackishly).
209 #[cfg(target_os = "linux")]
210 macro_rules! run_tests {
211 ( $($test_fn:ident),* ) => {{
212 println!();
213
214 $(
215 print!("test test_mount::{} ... ", stringify!($test_fn));
216 $test_fn();
217 println!("ok");
218 )*
219
220 println!();
221 }}
222 }
223
224 #[cfg(target_os = "linux")]
main()225 fn main() {
226 use test_mount::{setup_namespaces, test_mount_tmpfs_without_flags_allows_rwx,
227 test_mount_rdonly_disallows_write, test_mount_noexec_disallows_exec,
228 test_mount_bind};
229 setup_namespaces();
230
231 run_tests!(test_mount_tmpfs_without_flags_allows_rwx,
232 test_mount_rdonly_disallows_write,
233 test_mount_noexec_disallows_exec,
234 test_mount_bind);
235 }
236
237 #[cfg(not(target_os = "linux"))]
main()238 fn main() {}
239