brintos

brintos / linux-shallow public Read only

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