162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#define _GNU_SOURCE 362306a36Sopenharmony_ci#include <sched.h> 462306a36Sopenharmony_ci#include <stdio.h> 562306a36Sopenharmony_ci#include <errno.h> 662306a36Sopenharmony_ci#include <string.h> 762306a36Sopenharmony_ci#include <sys/types.h> 862306a36Sopenharmony_ci#include <sys/mount.h> 962306a36Sopenharmony_ci#include <sys/wait.h> 1062306a36Sopenharmony_ci#include <sys/vfs.h> 1162306a36Sopenharmony_ci#include <sys/statvfs.h> 1262306a36Sopenharmony_ci#include <stdlib.h> 1362306a36Sopenharmony_ci#include <unistd.h> 1462306a36Sopenharmony_ci#include <fcntl.h> 1562306a36Sopenharmony_ci#include <grp.h> 1662306a36Sopenharmony_ci#include <stdbool.h> 1762306a36Sopenharmony_ci#include <stdarg.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#ifndef CLONE_NEWNS 2062306a36Sopenharmony_ci# define CLONE_NEWNS 0x00020000 2162306a36Sopenharmony_ci#endif 2262306a36Sopenharmony_ci#ifndef CLONE_NEWUTS 2362306a36Sopenharmony_ci# define CLONE_NEWUTS 0x04000000 2462306a36Sopenharmony_ci#endif 2562306a36Sopenharmony_ci#ifndef CLONE_NEWIPC 2662306a36Sopenharmony_ci# define CLONE_NEWIPC 0x08000000 2762306a36Sopenharmony_ci#endif 2862306a36Sopenharmony_ci#ifndef CLONE_NEWNET 2962306a36Sopenharmony_ci# define CLONE_NEWNET 0x40000000 3062306a36Sopenharmony_ci#endif 3162306a36Sopenharmony_ci#ifndef CLONE_NEWUSER 3262306a36Sopenharmony_ci# define CLONE_NEWUSER 0x10000000 3362306a36Sopenharmony_ci#endif 3462306a36Sopenharmony_ci#ifndef CLONE_NEWPID 3562306a36Sopenharmony_ci# define CLONE_NEWPID 0x20000000 3662306a36Sopenharmony_ci#endif 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#ifndef MS_REC 3962306a36Sopenharmony_ci# define MS_REC 16384 4062306a36Sopenharmony_ci#endif 4162306a36Sopenharmony_ci#ifndef MS_RELATIME 4262306a36Sopenharmony_ci# define MS_RELATIME (1 << 21) 4362306a36Sopenharmony_ci#endif 4462306a36Sopenharmony_ci#ifndef MS_STRICTATIME 4562306a36Sopenharmony_ci# define MS_STRICTATIME (1 << 24) 4662306a36Sopenharmony_ci#endif 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic void die(char *fmt, ...) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci va_list ap; 5162306a36Sopenharmony_ci va_start(ap, fmt); 5262306a36Sopenharmony_ci vfprintf(stderr, fmt, ap); 5362306a36Sopenharmony_ci va_end(ap); 5462306a36Sopenharmony_ci exit(EXIT_FAILURE); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic void vmaybe_write_file(bool enoent_ok, char *filename, char *fmt, va_list ap) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci char buf[4096]; 6062306a36Sopenharmony_ci int fd; 6162306a36Sopenharmony_ci ssize_t written; 6262306a36Sopenharmony_ci int buf_len; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci buf_len = vsnprintf(buf, sizeof(buf), fmt, ap); 6562306a36Sopenharmony_ci if (buf_len < 0) { 6662306a36Sopenharmony_ci die("vsnprintf failed: %s\n", 6762306a36Sopenharmony_ci strerror(errno)); 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci if (buf_len >= sizeof(buf)) { 7062306a36Sopenharmony_ci die("vsnprintf output truncated\n"); 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci fd = open(filename, O_WRONLY); 7462306a36Sopenharmony_ci if (fd < 0) { 7562306a36Sopenharmony_ci if ((errno == ENOENT) && enoent_ok) 7662306a36Sopenharmony_ci return; 7762306a36Sopenharmony_ci die("open of %s failed: %s\n", 7862306a36Sopenharmony_ci filename, strerror(errno)); 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci written = write(fd, buf, buf_len); 8162306a36Sopenharmony_ci if (written != buf_len) { 8262306a36Sopenharmony_ci if (written >= 0) { 8362306a36Sopenharmony_ci die("short write to %s\n", filename); 8462306a36Sopenharmony_ci } else { 8562306a36Sopenharmony_ci die("write to %s failed: %s\n", 8662306a36Sopenharmony_ci filename, strerror(errno)); 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci if (close(fd) != 0) { 9062306a36Sopenharmony_ci die("close of %s failed: %s\n", 9162306a36Sopenharmony_ci filename, strerror(errno)); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic void maybe_write_file(char *filename, char *fmt, ...) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci va_list ap; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci va_start(ap, fmt); 10062306a36Sopenharmony_ci vmaybe_write_file(true, filename, fmt, ap); 10162306a36Sopenharmony_ci va_end(ap); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic void write_file(char *filename, char *fmt, ...) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci va_list ap; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci va_start(ap, fmt); 11062306a36Sopenharmony_ci vmaybe_write_file(false, filename, fmt, ap); 11162306a36Sopenharmony_ci va_end(ap); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int read_mnt_flags(const char *path) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci int ret; 11862306a36Sopenharmony_ci struct statvfs stat; 11962306a36Sopenharmony_ci int mnt_flags; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci ret = statvfs(path, &stat); 12262306a36Sopenharmony_ci if (ret != 0) { 12362306a36Sopenharmony_ci die("statvfs of %s failed: %s\n", 12462306a36Sopenharmony_ci path, strerror(errno)); 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci if (stat.f_flag & ~(ST_RDONLY | ST_NOSUID | ST_NODEV | \ 12762306a36Sopenharmony_ci ST_NOEXEC | ST_NOATIME | ST_NODIRATIME | ST_RELATIME | \ 12862306a36Sopenharmony_ci ST_SYNCHRONOUS | ST_MANDLOCK)) { 12962306a36Sopenharmony_ci die("Unrecognized mount flags\n"); 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci mnt_flags = 0; 13262306a36Sopenharmony_ci if (stat.f_flag & ST_RDONLY) 13362306a36Sopenharmony_ci mnt_flags |= MS_RDONLY; 13462306a36Sopenharmony_ci if (stat.f_flag & ST_NOSUID) 13562306a36Sopenharmony_ci mnt_flags |= MS_NOSUID; 13662306a36Sopenharmony_ci if (stat.f_flag & ST_NODEV) 13762306a36Sopenharmony_ci mnt_flags |= MS_NODEV; 13862306a36Sopenharmony_ci if (stat.f_flag & ST_NOEXEC) 13962306a36Sopenharmony_ci mnt_flags |= MS_NOEXEC; 14062306a36Sopenharmony_ci if (stat.f_flag & ST_NOATIME) 14162306a36Sopenharmony_ci mnt_flags |= MS_NOATIME; 14262306a36Sopenharmony_ci if (stat.f_flag & ST_NODIRATIME) 14362306a36Sopenharmony_ci mnt_flags |= MS_NODIRATIME; 14462306a36Sopenharmony_ci if (stat.f_flag & ST_RELATIME) 14562306a36Sopenharmony_ci mnt_flags |= MS_RELATIME; 14662306a36Sopenharmony_ci if (stat.f_flag & ST_SYNCHRONOUS) 14762306a36Sopenharmony_ci mnt_flags |= MS_SYNCHRONOUS; 14862306a36Sopenharmony_ci if (stat.f_flag & ST_MANDLOCK) 14962306a36Sopenharmony_ci mnt_flags |= ST_MANDLOCK; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return mnt_flags; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void create_and_enter_userns(void) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci uid_t uid; 15762306a36Sopenharmony_ci gid_t gid; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci uid = getuid(); 16062306a36Sopenharmony_ci gid = getgid(); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (unshare(CLONE_NEWUSER) !=0) { 16362306a36Sopenharmony_ci die("unshare(CLONE_NEWUSER) failed: %s\n", 16462306a36Sopenharmony_ci strerror(errno)); 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci maybe_write_file("/proc/self/setgroups", "deny"); 16862306a36Sopenharmony_ci write_file("/proc/self/uid_map", "0 %d 1", uid); 16962306a36Sopenharmony_ci write_file("/proc/self/gid_map", "0 %d 1", gid); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (setgid(0) != 0) { 17262306a36Sopenharmony_ci die ("setgid(0) failed %s\n", 17362306a36Sopenharmony_ci strerror(errno)); 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci if (setuid(0) != 0) { 17662306a36Sopenharmony_ci die("setuid(0) failed %s\n", 17762306a36Sopenharmony_ci strerror(errno)); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic 18262306a36Sopenharmony_cibool test_unpriv_remount(const char *fstype, const char *mount_options, 18362306a36Sopenharmony_ci int mount_flags, int remount_flags, int invalid_flags) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci pid_t child; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci child = fork(); 18862306a36Sopenharmony_ci if (child == -1) { 18962306a36Sopenharmony_ci die("fork failed: %s\n", 19062306a36Sopenharmony_ci strerror(errno)); 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci if (child != 0) { /* parent */ 19362306a36Sopenharmony_ci pid_t pid; 19462306a36Sopenharmony_ci int status; 19562306a36Sopenharmony_ci pid = waitpid(child, &status, 0); 19662306a36Sopenharmony_ci if (pid == -1) { 19762306a36Sopenharmony_ci die("waitpid failed: %s\n", 19862306a36Sopenharmony_ci strerror(errno)); 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci if (pid != child) { 20162306a36Sopenharmony_ci die("waited for %d got %d\n", 20262306a36Sopenharmony_ci child, pid); 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci if (!WIFEXITED(status)) { 20562306a36Sopenharmony_ci die("child did not terminate cleanly\n"); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci return WEXITSTATUS(status) == EXIT_SUCCESS; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci create_and_enter_userns(); 21162306a36Sopenharmony_ci if (unshare(CLONE_NEWNS) != 0) { 21262306a36Sopenharmony_ci die("unshare(CLONE_NEWNS) failed: %s\n", 21362306a36Sopenharmony_ci strerror(errno)); 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (mount("testing", "/tmp", fstype, mount_flags, mount_options) != 0) { 21762306a36Sopenharmony_ci die("mount of %s with options '%s' on /tmp failed: %s\n", 21862306a36Sopenharmony_ci fstype, 21962306a36Sopenharmony_ci mount_options? mount_options : "", 22062306a36Sopenharmony_ci strerror(errno)); 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci create_and_enter_userns(); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (unshare(CLONE_NEWNS) != 0) { 22662306a36Sopenharmony_ci die("unshare(CLONE_NEWNS) failed: %s\n", 22762306a36Sopenharmony_ci strerror(errno)); 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (mount("/tmp", "/tmp", "none", 23162306a36Sopenharmony_ci MS_REMOUNT | MS_BIND | remount_flags, NULL) != 0) { 23262306a36Sopenharmony_ci /* system("cat /proc/self/mounts"); */ 23362306a36Sopenharmony_ci die("remount of /tmp failed: %s\n", 23462306a36Sopenharmony_ci strerror(errno)); 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (mount("/tmp", "/tmp", "none", 23862306a36Sopenharmony_ci MS_REMOUNT | MS_BIND | invalid_flags, NULL) == 0) { 23962306a36Sopenharmony_ci /* system("cat /proc/self/mounts"); */ 24062306a36Sopenharmony_ci die("remount of /tmp with invalid flags " 24162306a36Sopenharmony_ci "succeeded unexpectedly\n"); 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci exit(EXIT_SUCCESS); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic bool test_unpriv_remount_simple(int mount_flags) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci return test_unpriv_remount("ramfs", NULL, mount_flags, mount_flags, 0); 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic bool test_unpriv_remount_atime(int mount_flags, int invalid_flags) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci return test_unpriv_remount("ramfs", NULL, mount_flags, mount_flags, 25462306a36Sopenharmony_ci invalid_flags); 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic bool test_priv_mount_unpriv_remount(void) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci pid_t child; 26062306a36Sopenharmony_ci int ret; 26162306a36Sopenharmony_ci const char *orig_path = "/dev"; 26262306a36Sopenharmony_ci const char *dest_path = "/tmp"; 26362306a36Sopenharmony_ci int orig_mnt_flags, remount_mnt_flags; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci child = fork(); 26662306a36Sopenharmony_ci if (child == -1) { 26762306a36Sopenharmony_ci die("fork failed: %s\n", 26862306a36Sopenharmony_ci strerror(errno)); 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci if (child != 0) { /* parent */ 27162306a36Sopenharmony_ci pid_t pid; 27262306a36Sopenharmony_ci int status; 27362306a36Sopenharmony_ci pid = waitpid(child, &status, 0); 27462306a36Sopenharmony_ci if (pid == -1) { 27562306a36Sopenharmony_ci die("waitpid failed: %s\n", 27662306a36Sopenharmony_ci strerror(errno)); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci if (pid != child) { 27962306a36Sopenharmony_ci die("waited for %d got %d\n", 28062306a36Sopenharmony_ci child, pid); 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci if (!WIFEXITED(status)) { 28362306a36Sopenharmony_ci die("child did not terminate cleanly\n"); 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci return WEXITSTATUS(status) == EXIT_SUCCESS; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci orig_mnt_flags = read_mnt_flags(orig_path); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci create_and_enter_userns(); 29162306a36Sopenharmony_ci ret = unshare(CLONE_NEWNS); 29262306a36Sopenharmony_ci if (ret != 0) { 29362306a36Sopenharmony_ci die("unshare(CLONE_NEWNS) failed: %s\n", 29462306a36Sopenharmony_ci strerror(errno)); 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci ret = mount(orig_path, dest_path, "bind", MS_BIND | MS_REC, NULL); 29862306a36Sopenharmony_ci if (ret != 0) { 29962306a36Sopenharmony_ci die("recursive bind mount of %s onto %s failed: %s\n", 30062306a36Sopenharmony_ci orig_path, dest_path, strerror(errno)); 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci ret = mount(dest_path, dest_path, "none", 30462306a36Sopenharmony_ci MS_REMOUNT | MS_BIND | orig_mnt_flags , NULL); 30562306a36Sopenharmony_ci if (ret != 0) { 30662306a36Sopenharmony_ci /* system("cat /proc/self/mounts"); */ 30762306a36Sopenharmony_ci die("remount of /tmp failed: %s\n", 30862306a36Sopenharmony_ci strerror(errno)); 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci remount_mnt_flags = read_mnt_flags(dest_path); 31262306a36Sopenharmony_ci if (orig_mnt_flags != remount_mnt_flags) { 31362306a36Sopenharmony_ci die("Mount flags unexpectedly changed during remount of %s originally mounted on %s\n", 31462306a36Sopenharmony_ci dest_path, orig_path); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci exit(EXIT_SUCCESS); 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ciint main(int argc, char **argv) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci if (!test_unpriv_remount_simple(MS_RDONLY)) { 32262306a36Sopenharmony_ci die("MS_RDONLY malfunctions\n"); 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci if (!test_unpriv_remount("devpts", "newinstance", MS_NODEV, MS_NODEV, 0)) { 32562306a36Sopenharmony_ci die("MS_NODEV malfunctions\n"); 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci if (!test_unpriv_remount_simple(MS_NOSUID)) { 32862306a36Sopenharmony_ci die("MS_NOSUID malfunctions\n"); 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci if (!test_unpriv_remount_simple(MS_NOEXEC)) { 33162306a36Sopenharmony_ci die("MS_NOEXEC malfunctions\n"); 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci if (!test_unpriv_remount_atime(MS_RELATIME, 33462306a36Sopenharmony_ci MS_NOATIME)) 33562306a36Sopenharmony_ci { 33662306a36Sopenharmony_ci die("MS_RELATIME malfunctions\n"); 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci if (!test_unpriv_remount_atime(MS_STRICTATIME, 33962306a36Sopenharmony_ci MS_NOATIME)) 34062306a36Sopenharmony_ci { 34162306a36Sopenharmony_ci die("MS_STRICTATIME malfunctions\n"); 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci if (!test_unpriv_remount_atime(MS_NOATIME, 34462306a36Sopenharmony_ci MS_STRICTATIME)) 34562306a36Sopenharmony_ci { 34662306a36Sopenharmony_ci die("MS_NOATIME malfunctions\n"); 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci if (!test_unpriv_remount_atime(MS_RELATIME|MS_NODIRATIME, 34962306a36Sopenharmony_ci MS_NOATIME)) 35062306a36Sopenharmony_ci { 35162306a36Sopenharmony_ci die("MS_RELATIME|MS_NODIRATIME malfunctions\n"); 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci if (!test_unpriv_remount_atime(MS_STRICTATIME|MS_NODIRATIME, 35462306a36Sopenharmony_ci MS_NOATIME)) 35562306a36Sopenharmony_ci { 35662306a36Sopenharmony_ci die("MS_STRICTATIME|MS_NODIRATIME malfunctions\n"); 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci if (!test_unpriv_remount_atime(MS_NOATIME|MS_NODIRATIME, 35962306a36Sopenharmony_ci MS_STRICTATIME)) 36062306a36Sopenharmony_ci { 36162306a36Sopenharmony_ci die("MS_NOATIME|MS_DIRATIME malfunctions\n"); 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci if (!test_unpriv_remount("ramfs", NULL, MS_STRICTATIME, 0, MS_NOATIME)) 36462306a36Sopenharmony_ci { 36562306a36Sopenharmony_ci die("Default atime malfunctions\n"); 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci if (!test_priv_mount_unpriv_remount()) { 36862306a36Sopenharmony_ci die("Mount flags unexpectedly changed after remount\n"); 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci return EXIT_SUCCESS; 37162306a36Sopenharmony_ci} 372