xref: /freebsd/tools/regression/security/cap_test/cap_test_capmode.c (revision 884a2a699669ec61e2366e3e358342dbc94be24a)
1 /*-
2  * Copyright (c) 2008-2009 Robert N. M. Watson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 /*
30  * Test routines to make sure a variety of system calls are or are not
31  * available in capability mode.  The goal is not to see if they work, just
32  * whether or not they return the expected ECAPMODE.
33  */
34 
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37 
38 #include <sys/param.h>
39 #include <sys/capability.h>
40 #include <sys/mman.h>
41 #include <sys/mount.h>
42 #include <sys/poll.h>
43 #include <sys/socket.h>
44 #include <sys/stat.h>
45 #include <sys/wait.h>
46 
47 #include <machine/sysarch.h>
48 #include <netinet/in.h>
49 
50 #include <err.h>
51 #include <errno.h>
52 #include <fcntl.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
56 
57 /* Need to check machine-dependent sysarch(). */
58 #define	ARCH_IS(s)	(!strncmp(s, MACHINE, sizeof(s) + 1))
59 
60 #include "cap_test.h"
61 
62 void
63 test_capmode(void)
64 {
65 	struct sockaddr_in sin;
66 	struct statfs statfs;
67 	struct stat sb;
68 	ssize_t len;
69 	long sysarch_arg = 0;
70 	int fd, fd_close, fd_dir, fd_file, fd_socket, fd2[2], ret;
71 	pid_t pid, wpid;
72 	char ch;
73 
74 	fd_file = open("/tmp/cap_test_syscalls", O_RDWR|O_CREAT, 0644);
75 	if (fd_file < 0)
76 		err(-1, "test_syscalls:prep: open cap_test_syscalls");
77 
78 	fd_close = open("/dev/null", O_RDWR);
79 	if (fd_close < 0)
80 		err(-1, "test_syscalls:prep: open /dev/null");
81 
82 	fd_dir = open("/tmp", O_RDONLY);
83 	if (fd_dir < 0)
84 		err(-1, "test_syscalls:prep: open /tmp");
85 
86 	fd_socket = socket(PF_INET, SOCK_DGRAM, 0);
87 	if (fd_socket < 0)
88 		err(-1, "test_syscalls:prep: socket");
89 
90 	if (cap_enter() < 0)
91 		err(-1, "test_syscalls:prep: cap_enter");
92 
93 
94 	bzero(&sin, sizeof(sin));
95 	sin.sin_len = sizeof(sin);
96 	sin.sin_family = AF_INET;
97 
98 	/*
99 	 * Here begin the tests, sorted roughly alphabetically by system call
100 	 * name.
101 	 */
102 	fd = accept(fd_socket, NULL, NULL);
103 	if (fd < 0) {
104 		if (errno == ECAPMODE)
105 			warnx("test_syscalls:accept");
106 	} else {
107 		warnx("test_syscalls:accept succeeded");
108 		close(fd);
109 	}
110 
111 	if (access("/tmp/cap_test_syscalls_access", F_OK) < 0) {
112 		if (errno != ECAPMODE)
113 			warn("test_syscalls:access");
114 	} else
115 		warnx("test_syscalls:access succeeded");
116 
117 	if (acct("/tmp/cap_test_syscalls_acct") < 0) {
118 		if (errno != ECAPMODE)
119 			warn("test_syscalls:acct");
120 	} else
121 		warnx("test_syscalls:acct succeeded");
122 
123 	if (bind(PF_INET, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
124 		if (errno != ECAPMODE)
125 			warn("test_syscall:bind");
126 	} else
127 		warnx("test_syscall:bind succeeded");
128 
129 	if (chdir("/tmp/cap_test_syscalls_chdir") < 0) {
130 		if (errno != ECAPMODE)
131 			warn("test_syscalls:chdir");
132 	} else
133 		warnx("test_syscalls:chdir succeeded");
134 
135 	if (chflags("/tmp/cap_test_syscalls_chflags", UF_NODUMP) < 0) {
136 		if (errno != ECAPMODE)
137 			warn("test_syscalls:chflags");
138 	} else
139 		warnx("test_syscalls:chflags succeeded");
140 
141 	if (chmod("/tmp/cap_test_syscalls_chmod", 0644) < 0) {
142 		if (errno != ECAPMODE)
143 			warn("test_syscalls:chmod");
144 	} else
145 		warnx("test_syscalls:chmod succeeded");
146 
147 	if (chown("/tmp/cap_test_syscalls_chown", -1, -1) < 0) {
148 		if (errno != ECAPMODE)
149 			warn("test_syscalls:chown");
150 	} else
151 		warnx("test_syscalls:chown succeeded");
152 
153 	if (chroot("/tmp/cap_test_syscalls_chroot") < 0) {
154 		if (errno != ECAPMODE)
155 			warn("test_syscalls:chroot");
156 	} else
157 		warnx("test_syscalls:chroot succeeded");
158 
159 	if (close(fd_close)) {
160 		if (errno == ECAPMODE)
161 			warnx("test_syscalls:close");
162 		else
163 			warn("test_syscalls:close");
164 	}
165 
166 	if (connect(PF_INET, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
167 		if (errno != ECAPMODE)
168 			warn("test_syscall:connect");
169 	} else
170 		warnx("test_syscall:connect succeeded");
171 
172 	fd = creat("/tmp/cap_test_syscalls_creat", 0644);
173 	if (fd >= 0) {
174 		warnx("test_syscalls:creat succeeded");
175 		close(fd);
176 	} else if (errno != ECAPMODE)
177 		warn("test_syscalls:creat");
178 
179 	fd = dup(fd_file);
180 	if (fd < 0) {
181 		if (errno == ECAPMODE)
182 			warnx("test_syscalls:dup");
183 	} else
184 		close(fd);
185 
186 	if (fchdir(fd_dir) < 0) {
187 		if (errno != ECAPMODE)
188 			warn("test_syscall:fchdir");
189 	} else
190 		warnx("test_syscalls:fchdir succeeded");
191 
192 	if (fchflags(fd_file, UF_NODUMP) < 0) {
193 		if (errno == ECAPMODE)
194 			warnx("test_syscall:fchflags");
195 	}
196 
197 	pid = fork();
198 	if (pid >= 0) {
199 		if (pid == 0) {
200 			exit(0);
201 		} else if (pid > 0) {
202 			wpid = waitpid(pid, NULL, 0);
203 			if (wpid < 0) {
204 				if (errno != ECAPMODE)
205 					warn("test_syscalls:waitpid");
206 			} else
207 				warnx("test_syscalls:waitpid succeeded");
208 		}
209 	} else
210 		warn("test_syscalls:fork");
211 
212 	if (fstat(fd_file, &sb) < 0) {
213 		if (errno == ECAPMODE)
214 			warnx("test_syscalls:fstat");
215 	}
216 
217 	/*
218 	 * getegid() can't return an error but check for it anyway.
219 	 */
220 	errno = 0;
221 	(void)getegid();
222 	if (errno == ECAPMODE)
223 		warnx("test_syscalls:getegid");
224 
225 	/*
226 	 * geteuid() can't return an error but check for it anyway.
227 	 */
228 	errno = 0;
229 	geteuid();
230 	if (errno == ECAPMODE)
231 		warnx("test_syscalls:geteuid");
232 
233 	if (getfsstat(&statfs, sizeof(statfs), MNT_NOWAIT) < 0) {
234 		if (errno != ECAPMODE)
235 			warn("test_syscalls:getfsstat");
236 	} else
237 		warnx("test_syscalls:getfsstat succeeded");
238 
239 	/*
240 	 * getgid() can't return an error but check for it anyway.
241 	 */
242 	errno = 0;
243 	getgid();
244 	if (errno == ECAPMODE)
245 		warnx("test_syscalls:getgid");
246 
247 	if (getpeername(fd_socket, NULL, NULL) < 0) {
248 		if (errno == ECAPMODE)
249 			warnx("test_syscalls:getpeername");
250 	}
251 
252 	if (getlogin() == NULL)
253 		warn("test_sycalls:getlogin %d", errno);
254 
255 	/*
256 	 * getpid() can't return an error but check for it anyway.
257 	 */
258 	errno = 0;
259 	(void)getpid();
260 	if (errno == ECAPMODE)
261 		warnx("test_syscalls:getpid");
262 
263 	/*
264 	 * getppid() can't return an error but check for it anyway.
265 	 */
266 	errno = 0;
267 	(void)getppid();
268 	if (errno == ECAPMODE)
269 		warnx("test_syscalls:getppid");
270 
271 	if (getsockname(fd_socket, NULL, NULL) < 0) {
272 		if (errno == ECAPMODE)
273 			warnx("test_syscalls:getsockname");
274 	}
275 
276 	/*
277 	 * getuid() can't return an error but check for it anyway.
278 	 */
279 	errno = 0;
280 	(void)getuid();
281 	if (errno == ECAPMODE)
282 		warnx("test_syscalls:getuid");
283 
284 	/* XXXRW: ktrace */
285 
286 	if (link("/tmp/foo", "/tmp/bar") < 0) {
287 		if (errno != ECAPMODE)
288 			warn("test_syscalls:link");
289 	} else
290 		warnx("test_syscalls:link succeeded");
291 
292 	ret = lseek(fd_file, SEEK_SET, 0);
293 	if (ret < 0) {
294 		if (errno == ECAPMODE)
295 			warnx("test_syscalls:lseek");
296 		else
297 			warn("test_syscalls:lseek");
298 	}
299 
300 	if (lstat("/tmp/cap_test_syscalls_lstat", &sb) < 0) {
301 		if (errno != ECAPMODE)
302 			warn("test_syscalls:lstat");
303 	} else
304 		warnx("test_syscalls:lstat succeeded");
305 
306 	if (mknod("/tmp/test_syscalls_mknod", 06440, 0) < 0) {
307 		if (errno != ECAPMODE)
308 			warn("test_syscalls:mknod");
309 	} else
310 		warnx("test_syscalls:mknod succeeded");
311 
312 	/*
313 	 * mount() is a bit tricky but do our best.
314 	 */
315 	if (mount("procfs", "/not_mounted", 0, NULL) < 0) {
316 		if (errno != ECAPMODE)
317 			warn("test_syscalls:mount");
318 	} else
319 		warnx("test_syscalls:mount succeeded");
320 
321 	if (msync(&fd_file, 8192, MS_ASYNC) < 0) {
322 		if (errno == ECAPMODE)
323 			warnx("test_syscalls:msync");
324 	}
325 
326 	fd = open("/dev/null", O_RDWR);
327 	if (fd >= 0) {
328 		warnx("test_syscalls:open succeeded");
329 		close(fd);
330 	}
331 
332 	if (pipe(fd2) == 0) {
333 		close(fd2[0]);
334 		close(fd2[1]);
335 	} else if (errno == ECAPMODE)
336 		warnx("test_syscalls:pipe");
337 
338 	if (profil(NULL, 0, 0, 0) < 0) {
339 		if (errno == ECAPMODE)
340 			warnx("test_syscalls:profile");
341 	}
342 
343 	/* XXXRW: ptrace. */
344 
345 	len = read(fd_file, &ch, sizeof(ch));
346 	if (len < 0 && errno == ECAPMODE)
347 		warnx("test_syscalls:read");
348 
349 	if (readlink("/tmp/cap_test_syscalls_readlink", NULL, 0) < 0) {
350 		if (errno != ECAPMODE)
351 			warn("test_syscalls:readlink");
352 	} else
353 		warnx("test_syscalls:readlink succeeded");
354 
355 	len = recvfrom(fd_socket, NULL, 0, 0, NULL, NULL);
356 	if (len < 0 && errno == ECAPMODE)
357 		warnx("test_syscalls:recvfrom");
358 
359 	len = recvmsg(fd_socket, NULL, 0);
360 	if (len < 0 && errno == ECAPMODE)
361 		warnx("test_syscalls:recvmsg");
362 
363 	if (revoke("/tmp/cap_test_syscalls_revoke") < 0) {
364 		if (errno != ECAPMODE)
365 			warn("test_syscalls:revoke");
366 	} else
367 		warnx("test_syscalls:revoke succeeded");
368 
369 	len = sendmsg(fd_socket, NULL, 0);
370 	if (len < 0 && errno == ECAPMODE)
371 		warnx("test_syscalls:sendmsg");
372 
373 	len = sendto(fd_socket, NULL, 0, 0, NULL, 0);
374 	if (len < 0 && errno == ECAPMODE)
375 		warn("test_syscalls:sendto(NULL)");
376 
377 	if (setuid(getuid()) < 0) {
378 		if (errno == ECAPMODE)
379 			warnx("test_syscalls:setuid");
380 	}
381 
382 	if (stat("/tmp/cap_test_syscalls_stat", &sb) < 0) {
383 		if (errno != ECAPMODE)
384 			warn("test_syscalls:stat");
385 	} else
386 		warnx("test_syscalls:stat succeeded");
387 
388 	if (symlink("/tmp/cap_test_syscalls_symlink_from",
389 	    "/tmp/cap_test_syscalls_symlink_to") < 0) {
390 		if (errno != ECAPMODE)
391 			warn("test_syscalls:symlink");
392 	} else
393 		warnx("test_syscalls:symlink succeeded");
394 
395 	/* sysarch() is, by definition, architecture-dependent */
396 	if (ARCH_IS("i386") || ARCH_IS("amd64")) {
397 		if (sysarch(I386_SET_IOPERM, &sysarch_arg) != -1)
398 			warnx("test_syscalls:sysarch succeeded");
399 		else if (errno != ECAPMODE)
400 			warn("test_syscalls:sysarch errno != ECAPMODE");
401 
402 		/* XXXJA: write a test for arm */
403 	} else {
404 		warnx("test_syscalls:no sysarch() test for architecture '%s'", MACHINE);
405 	}
406 
407 	/* XXXRW: No error return from sync(2) to test. */
408 
409 	if (unlink("/tmp/cap_test_syscalls_unlink") < 0) {
410 		if (errno != ECAPMODE)
411 			warn("test_syscalls:unlink");
412 	} else
413 		warnx("test_syscalls:unlink succeeded");
414 
415 	if (unmount("/not_mounted", 0) < 0) {
416 		if (errno != ECAPMODE)
417 			warn("test_syscalls:unmount");
418 	} else
419 		warnx("test_syscalls:unmount succeeded");
420 
421 	len = write(fd_file, &ch, sizeof(ch));
422 	if (len < 0 && errno == ECAPMODE)
423 		warnx("test_syscalls:write");
424 
425 	exit(0);
426 }
427