brintos

brintos / linux-shallow public Read only

0
0
Text · 4.4 KiB · 650d6d8 Raw
219 lines · c
1// SPDX-License-Identifier: GPL-2.02#define _GNU_SOURCE3#include <errno.h>4#include <fcntl.h>5#include <limits.h>6#include <sched.h>7#include <stdarg.h>8#include <stdbool.h>9#include <stdio.h>10#include <stdlib.h>11#include <string.h>12#include <sys/mount.h>13#include <sys/stat.h>14#include <sys/types.h>15#include <sys/vfs.h>16#include <unistd.h>17 18#ifndef MS_NOSYMFOLLOW19# define MS_NOSYMFOLLOW 256     /* Do not follow symlinks */20#endif21 22#ifndef ST_NOSYMFOLLOW23# define ST_NOSYMFOLLOW 0x2000  /* Do not follow symlinks */24#endif25 26#define DATA "/tmp/data"27#define LINK "/tmp/symlink"28#define TMP  "/tmp"29 30static void die(char *fmt, ...)31{32	va_list ap;33 34	va_start(ap, fmt);35	vfprintf(stderr, fmt, ap);36	va_end(ap);37	exit(EXIT_FAILURE);38}39 40static void vmaybe_write_file(bool enoent_ok, char *filename, char *fmt,41		va_list ap)42{43	ssize_t written;44	char buf[4096];45	int buf_len;46	int fd;47 48	buf_len = vsnprintf(buf, sizeof(buf), fmt, ap);49	if (buf_len < 0)50		die("vsnprintf failed: %s\n", strerror(errno));51 52	if (buf_len >= sizeof(buf))53		die("vsnprintf output truncated\n");54 55	fd = open(filename, O_WRONLY);56	if (fd < 0) {57		if ((errno == ENOENT) && enoent_ok)58			return;59		die("open of %s failed: %s\n", filename, strerror(errno));60	}61 62	written = write(fd, buf, buf_len);63	if (written != buf_len) {64		if (written >= 0) {65			die("short write to %s\n", filename);66		} else {67			die("write to %s failed: %s\n",68				filename, strerror(errno));69		}70	}71 72	if (close(fd) != 0)73		die("close of %s failed: %s\n", filename, strerror(errno));74}75 76static void maybe_write_file(char *filename, char *fmt, ...)77{78	va_list ap;79 80	va_start(ap, fmt);81	vmaybe_write_file(true, filename, fmt, ap);82	va_end(ap);83}84 85static void write_file(char *filename, char *fmt, ...)86{87	va_list ap;88 89	va_start(ap, fmt);90	vmaybe_write_file(false, filename, fmt, ap);91	va_end(ap);92}93 94static void create_and_enter_ns(void)95{96	uid_t uid = getuid();97	gid_t gid = getgid();98 99	if (unshare(CLONE_NEWUSER) != 0)100		die("unshare(CLONE_NEWUSER) failed: %s\n", strerror(errno));101 102	maybe_write_file("/proc/self/setgroups", "deny");103	write_file("/proc/self/uid_map", "0 %d 1", uid);104	write_file("/proc/self/gid_map", "0 %d 1", gid);105 106	if (setgid(0) != 0)107		die("setgid(0) failed %s\n", strerror(errno));108	if (setuid(0) != 0)109		die("setuid(0) failed %s\n", strerror(errno));110 111	if (unshare(CLONE_NEWNS) != 0)112		die("unshare(CLONE_NEWNS) failed: %s\n", strerror(errno));113}114 115static void setup_symlink(void)116{117	int data, err;118 119	data = creat(DATA, O_RDWR);120	if (data < 0)121		die("creat failed: %s\n", strerror(errno));122 123	err = symlink(DATA, LINK);124	if (err < 0)125		die("symlink failed: %s\n", strerror(errno));126 127	if (close(data) != 0)128		die("close of %s failed: %s\n", DATA, strerror(errno));129}130 131static void test_link_traversal(bool nosymfollow)132{133	int link;134 135	link = open(LINK, 0, O_RDWR);136	if (nosymfollow) {137		if ((link != -1 || errno != ELOOP)) {138			die("link traversal unexpected result: %d, %s\n",139					link, strerror(errno));140		}141	} else {142		if (link < 0)143			die("link traversal failed: %s\n", strerror(errno));144 145		if (close(link) != 0)146			die("close of link failed: %s\n", strerror(errno));147	}148}149 150static void test_readlink(void)151{152	char buf[4096];153	ssize_t ret;154 155	bzero(buf, sizeof(buf));156 157	ret = readlink(LINK, buf, sizeof(buf));158	if (ret < 0)159		die("readlink failed: %s\n", strerror(errno));160	if (strcmp(buf, DATA) != 0)161		die("readlink strcmp failed: '%s' '%s'\n", buf, DATA);162}163 164static void test_realpath(void)165{166	char *path = realpath(LINK, NULL);167 168	if (!path)169		die("realpath failed: %s\n", strerror(errno));170	if (strcmp(path, DATA) != 0)171		die("realpath strcmp failed\n");172 173	free(path);174}175 176static void test_statfs(bool nosymfollow)177{178	struct statfs buf;179	int ret;180 181	ret = statfs(TMP, &buf);182	if (ret)183		die("statfs failed: %s\n", strerror(errno));184 185	if (nosymfollow) {186		if ((buf.f_flags & ST_NOSYMFOLLOW) == 0)187			die("ST_NOSYMFOLLOW not set on %s\n", TMP);188	} else {189		if ((buf.f_flags & ST_NOSYMFOLLOW) != 0)190			die("ST_NOSYMFOLLOW set on %s\n", TMP);191	}192}193 194static void run_tests(bool nosymfollow)195{196	test_link_traversal(nosymfollow);197	test_readlink();198	test_realpath();199	test_statfs(nosymfollow);200}201 202int main(int argc, char **argv)203{204	create_and_enter_ns();205 206	if (mount("testing", TMP, "ramfs", 0, NULL) != 0)207		die("mount failed: %s\n", strerror(errno));208 209	setup_symlink();210	run_tests(false);211 212	if (mount("testing", TMP, "ramfs", MS_REMOUNT|MS_NOSYMFOLLOW, NULL) != 0)213		die("remount failed: %s\n", strerror(errno));214 215	run_tests(true);216 217	return EXIT_SUCCESS;218}219