1#define _GNU_SOURCE 2#include <sched.h> 3#include <stdio.h> 4#include <errno.h> 5#include <string.h> 6#include <sys/types.h> 7#include <sys/mount.h> 8#include <sys/wait.h> 9#include <sys/vfs.h> 10#include <sys/statvfs.h> 11#include <stdlib.h> 12#include <unistd.h> 13#include <fcntl.h> 14#include <grp.h> 15#include <stdbool.h> 16#include <stdarg.h> 17 18#ifndef CLONE_NEWNS 19# define CLONE_NEWNS 0x00020000 20#endif 21#ifndef CLONE_NEWUTS 22# define CLONE_NEWUTS 0x04000000 23#endif 24#ifndef CLONE_NEWIPC 25# define CLONE_NEWIPC 0x08000000 26#endif 27#ifndef CLONE_NEWNET 28# define CLONE_NEWNET 0x40000000 29#endif 30#ifndef CLONE_NEWUSER 31# define CLONE_NEWUSER 0x10000000 32#endif 33#ifndef CLONE_NEWPID 34# define CLONE_NEWPID 0x20000000 35#endif 36 37#ifndef MS_REC 38# define MS_REC 16384 39#endif 40#ifndef MS_RELATIME 41# define MS_RELATIME (1 << 21) 42#endif 43#ifndef MS_STRICTATIME 44# define MS_STRICTATIME (1 << 24) 45#endif 46 47static void die(char *fmt, ...) 48{ 49 va_list ap; 50 va_start(ap, fmt); 51 vfprintf(stderr, fmt, ap); 52 va_end(ap); 53 exit(EXIT_FAILURE); 54} 55 56static void vmaybe_write_file(bool enoent_ok, char *filename, char *fmt, va_list ap) 57{ 58 char buf[4096]; 59 int fd; 60 ssize_t written; 61 int buf_len; 62 63 buf_len = vsnprintf(buf, sizeof(buf), fmt, ap); 64 if (buf_len < 0) { 65 die("vsnprintf failed: %s\n", 66 strerror(errno)); 67 } 68 if (buf_len >= sizeof(buf)) { 69 die("vsnprintf output truncated\n"); 70 } 71 72 fd = open(filename, O_WRONLY); 73 if (fd < 0) { 74 if ((errno == ENOENT) && enoent_ok) 75 return; 76 die("open of %s failed: %s\n", 77 filename, strerror(errno)); 78 } 79 written = write(fd, buf, buf_len); 80 if (written != buf_len) { 81 if (written >= 0) { 82 die("short write to %s\n", filename); 83 } else { 84 die("write to %s failed: %s\n", 85 filename, strerror(errno)); 86 } 87 } 88 if (close(fd) != 0) { 89 die("close of %s failed: %s\n", 90 filename, strerror(errno)); 91 } 92} 93 94static void maybe_write_file(char *filename, char *fmt, ...) 95{ 96 va_list ap; 97 98 va_start(ap, fmt); 99 vmaybe_write_file(true, filename, fmt, ap); 100 va_end(ap); 101 102} 103 104static void write_file(char *filename, char *fmt, ...) 105{ 106 va_list ap; 107 108 va_start(ap, fmt); 109 vmaybe_write_file(false, filename, fmt, ap); 110 va_end(ap); 111 112} 113 114static int read_mnt_flags(const char *path) 115{ 116 int ret; 117 struct statvfs stat; 118 int mnt_flags; 119 120 ret = statvfs(path, &stat); 121 if (ret != 0) { 122 die("statvfs of %s failed: %s\n", 123 path, strerror(errno)); 124 } 125 if (stat.f_flag & ~(ST_RDONLY | ST_NOSUID | ST_NODEV | \ 126 ST_NOEXEC | ST_NOATIME | ST_NODIRATIME | ST_RELATIME | \ 127 ST_SYNCHRONOUS | ST_MANDLOCK)) { 128 die("Unrecognized mount flags\n"); 129 } 130 mnt_flags = 0; 131 if (stat.f_flag & ST_RDONLY) 132 mnt_flags |= MS_RDONLY; 133 if (stat.f_flag & ST_NOSUID) 134 mnt_flags |= MS_NOSUID; 135 if (stat.f_flag & ST_NODEV) 136 mnt_flags |= MS_NODEV; 137 if (stat.f_flag & ST_NOEXEC) 138 mnt_flags |= MS_NOEXEC; 139 if (stat.f_flag & ST_NOATIME) 140 mnt_flags |= MS_NOATIME; 141 if (stat.f_flag & ST_NODIRATIME) 142 mnt_flags |= MS_NODIRATIME; 143 if (stat.f_flag & ST_RELATIME) 144 mnt_flags |= MS_RELATIME; 145 if (stat.f_flag & ST_SYNCHRONOUS) 146 mnt_flags |= MS_SYNCHRONOUS; 147 if (stat.f_flag & ST_MANDLOCK) 148 mnt_flags |= ST_MANDLOCK; 149 150 return mnt_flags; 151} 152 153static void create_and_enter_userns(void) 154{ 155 uid_t uid; 156 gid_t gid; 157 158 uid = getuid(); 159 gid = getgid(); 160 161 if (unshare(CLONE_NEWUSER) !=0) { 162 die("unshare(CLONE_NEWUSER) failed: %s\n", 163 strerror(errno)); 164 } 165 166 maybe_write_file("/proc/self/setgroups", "deny"); 167 write_file("/proc/self/uid_map", "0 %d 1", uid); 168 write_file("/proc/self/gid_map", "0 %d 1", gid); 169 170 if (setgid(0) != 0) { 171 die ("setgid(0) failed %s\n", 172 strerror(errno)); 173 } 174 if (setuid(0) != 0) { 175 die("setuid(0) failed %s\n", 176 strerror(errno)); 177 } 178} 179 180static 181bool test_unpriv_remount(const char *fstype, const char *mount_options, 182 int mount_flags, int remount_flags, int invalid_flags) 183{ 184 pid_t child; 185 186 child = fork(); 187 if (child == -1) { 188 die("fork failed: %s\n", 189 strerror(errno)); 190 } 191 if (child != 0) { /* parent */ 192 pid_t pid; 193 int status; 194 pid = waitpid(child, &status, 0); 195 if (pid == -1) { 196 die("waitpid failed: %s\n", 197 strerror(errno)); 198 } 199 if (pid != child) { 200 die("waited for %d got %d\n", 201 child, pid); 202 } 203 if (!WIFEXITED(status)) { 204 die("child did not terminate cleanly\n"); 205 } 206 return WEXITSTATUS(status) == EXIT_SUCCESS ? true : false; 207 } 208 209 create_and_enter_userns(); 210 if (unshare(CLONE_NEWNS) != 0) { 211 die("unshare(CLONE_NEWNS) failed: %s\n", 212 strerror(errno)); 213 } 214 215 if (mount("testing", "/tmp", fstype, mount_flags, mount_options) != 0) { 216 die("mount of %s with options '%s' on /tmp failed: %s\n", 217 fstype, 218 mount_options? mount_options : "", 219 strerror(errno)); 220 } 221 222 create_and_enter_userns(); 223 224 if (unshare(CLONE_NEWNS) != 0) { 225 die("unshare(CLONE_NEWNS) failed: %s\n", 226 strerror(errno)); 227 } 228 229 if (mount("/tmp", "/tmp", "none", 230 MS_REMOUNT | MS_BIND | remount_flags, NULL) != 0) { 231 /* system("cat /proc/self/mounts"); */ 232 die("remount of /tmp failed: %s\n", 233 strerror(errno)); 234 } 235 236 if (mount("/tmp", "/tmp", "none", 237 MS_REMOUNT | MS_BIND | invalid_flags, NULL) == 0) { 238 /* system("cat /proc/self/mounts"); */ 239 die("remount of /tmp with invalid flags " 240 "succeeded unexpectedly\n"); 241 } 242 exit(EXIT_SUCCESS); 243} 244 245static bool test_unpriv_remount_simple(int mount_flags) 246{ 247 return test_unpriv_remount("ramfs", NULL, mount_flags, mount_flags, 0); 248} 249 250static bool test_unpriv_remount_atime(int mount_flags, int invalid_flags) 251{ 252 return test_unpriv_remount("ramfs", NULL, mount_flags, mount_flags, 253 invalid_flags); 254} 255 256static bool test_priv_mount_unpriv_remount(void) 257{ 258 pid_t child; 259 int ret; 260 const char *orig_path = "/dev"; 261 const char *dest_path = "/tmp"; 262 int orig_mnt_flags, remount_mnt_flags; 263 264 child = fork(); 265 if (child == -1) { 266 die("fork failed: %s\n", 267 strerror(errno)); 268 } 269 if (child != 0) { /* parent */ 270 pid_t pid; 271 int status; 272 pid = waitpid(child, &status, 0); 273 if (pid == -1) { 274 die("waitpid failed: %s\n", 275 strerror(errno)); 276 } 277 if (pid != child) { 278 die("waited for %d got %d\n", 279 child, pid); 280 } 281 if (!WIFEXITED(status)) { 282 die("child did not terminate cleanly\n"); 283 } 284 return WEXITSTATUS(status) == EXIT_SUCCESS ? true : false; 285 } 286 287 orig_mnt_flags = read_mnt_flags(orig_path); 288 289 create_and_enter_userns(); 290 ret = unshare(CLONE_NEWNS); 291 if (ret != 0) { 292 die("unshare(CLONE_NEWNS) failed: %s\n", 293 strerror(errno)); 294 } 295 296 ret = mount(orig_path, dest_path, "bind", MS_BIND | MS_REC, NULL); 297 if (ret != 0) { 298 die("recursive bind mount of %s onto %s failed: %s\n", 299 orig_path, dest_path, strerror(errno)); 300 } 301 302 ret = mount(dest_path, dest_path, "none", 303 MS_REMOUNT | MS_BIND | orig_mnt_flags , NULL); 304 if (ret != 0) { 305 /* system("cat /proc/self/mounts"); */ 306 die("remount of /tmp failed: %s\n", 307 strerror(errno)); 308 } 309 310 remount_mnt_flags = read_mnt_flags(dest_path); 311 if (orig_mnt_flags != remount_mnt_flags) { 312 die("Mount flags unexpectedly changed during remount of %s originally mounted on %s\n", 313 dest_path, orig_path); 314 } 315 exit(EXIT_SUCCESS); 316} 317 318int main(int argc, char **argv) 319{ 320 if (!test_unpriv_remount_simple(MS_RDONLY)) { 321 die("MS_RDONLY malfunctions\n"); 322 } 323 if (!test_unpriv_remount("devpts", "newinstance", MS_NODEV, MS_NODEV, 0)) { 324 die("MS_NODEV malfunctions\n"); 325 } 326 if (!test_unpriv_remount_simple(MS_NOSUID)) { 327 die("MS_NOSUID malfunctions\n"); 328 } 329 if (!test_unpriv_remount_simple(MS_NOEXEC)) { 330 die("MS_NOEXEC malfunctions\n"); 331 } 332 if (!test_unpriv_remount_atime(MS_RELATIME, 333 MS_NOATIME)) 334 { 335 die("MS_RELATIME malfunctions\n"); 336 } 337 if (!test_unpriv_remount_atime(MS_STRICTATIME, 338 MS_NOATIME)) 339 { 340 die("MS_STRICTATIME malfunctions\n"); 341 } 342 if (!test_unpriv_remount_atime(MS_NOATIME, 343 MS_STRICTATIME)) 344 { 345 die("MS_NOATIME malfunctions\n"); 346 } 347 if (!test_unpriv_remount_atime(MS_RELATIME|MS_NODIRATIME, 348 MS_NOATIME)) 349 { 350 die("MS_RELATIME|MS_NODIRATIME malfunctions\n"); 351 } 352 if (!test_unpriv_remount_atime(MS_STRICTATIME|MS_NODIRATIME, 353 MS_NOATIME)) 354 { 355 die("MS_STRICTATIME|MS_NODIRATIME malfunctions\n"); 356 } 357 if (!test_unpriv_remount_atime(MS_NOATIME|MS_NODIRATIME, 358 MS_STRICTATIME)) 359 { 360 die("MS_NOATIME|MS_DIRATIME malfunctions\n"); 361 } 362 if (!test_unpriv_remount("ramfs", NULL, MS_STRICTATIME, 0, MS_NOATIME)) 363 { 364 die("Default atime malfunctions\n"); 365 } 366 if (!test_priv_mount_unpriv_remount()) { 367 die("Mount flags unexpectedly changed after remount\n"); 368 } 369 return EXIT_SUCCESS; 370} 371