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