xref: /freebsd/lib/libcasper/services/cap_fileargs/tests/fileargs_test.c (revision 19cca0b9613d7c3058e41baf0204245119732235)
1 /*-
2  * Copyright (c) 2021 Mariusz Zaborski <oshogbo@FreeBSD.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
14  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
15  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
23  * POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28 
29 #include <sys/param.h>
30 #include <sys/capsicum.h>
31 #include <sys/stat.h>
32 
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <stdio.h>
36 
37 #include <atf-c.h>
38 
39 #include <libcasper.h>
40 #include <casper/cap_fileargs.h>
41 
42 #define MAX_FILES		200
43 
44 static char *files[MAX_FILES];
45 static int fds[MAX_FILES];
46 
47 #define	TEST_FILE	"/etc/passwd"
48 
49 static void
50 prepare_files(size_t num, bool create)
51 {
52 	const char template[] = "testsfiles.XXXXXXXX";
53 	size_t i;
54 
55 	for (i = 0; i < num; i++) {
56 		files[i] = calloc(1, sizeof(template));
57 		ATF_REQUIRE(files[i] != NULL);
58 		strncpy(files[i], template, sizeof(template) - 1);
59 
60 		if (create) {
61 			fds[i] = mkstemp(files[i]);
62 			ATF_REQUIRE(fds[i] >= 0);
63 		} else {
64 			fds[i] = -1;
65 			ATF_REQUIRE(mktemp(files[i]) != NULL);
66 		}
67 	}
68 }
69 
70 static void
71 clear_files(void)
72 {
73 	size_t i;
74 
75 
76 	for (i = 0; files[i] != NULL; i++) {
77 		unlink(files[i]);
78 		free(files[i]);
79 		if (fds[i] != -1)
80 			close(fds[i]);
81 	}
82 }
83 
84 static int
85 test_file_open(fileargs_t *fa, const char *file, int *fdp)
86 {
87 	int fd;
88 
89 	fd = fileargs_open(fa, file);
90 	if (fd < 0)
91 		return (errno);
92 
93 	if (fdp != NULL) {
94 		*fdp = fd;
95 	}
96 
97 	return (0);
98 }
99 
100 static int
101 test_file_fopen(fileargs_t *fa, const char *file, const char *mode,
102     FILE **retfile)
103 {
104 	FILE *pfile;
105 
106 	pfile = fileargs_fopen(fa, file, mode);
107 	if (pfile == NULL)
108 		return (errno);
109 
110 	if (retfile != NULL) {
111 		*retfile = pfile;
112 	}
113 
114 	return (0);
115 }
116 
117 static int
118 test_file_lstat(fileargs_t *fa, const char *file)
119 {
120 	struct stat fasb, origsb;
121 	bool equals;
122 
123 	if (fileargs_lstat(fa, file, &fasb) < 0)
124 		return (errno);
125 
126 	ATF_REQUIRE(lstat(file, &origsb) == 0);
127 
128 	equals = true;
129 	equals &= (origsb.st_dev == fasb.st_dev);
130 	equals &= (origsb.st_ino == fasb.st_ino);
131 	equals &= (origsb.st_nlink == fasb.st_nlink);
132 	equals &= (origsb.st_flags == fasb.st_flags);
133 	equals &= (memcmp(&origsb.st_ctim, &fasb.st_ctim,
134 	    sizeof(fasb.st_ctim)) == 0);
135 	equals &= (memcmp(&origsb.st_birthtim, &fasb.st_birthtim,
136 	    sizeof(fasb.st_birthtim)) == 0);
137 	if (!equals) {
138 		return (EINVAL);
139 	}
140 
141 	return (0);
142 }
143 
144 static int
145 test_file_mode(int fd, int mode)
146 {
147 	int flags;
148 
149 	flags = fcntl(fd, F_GETFL, 0);
150 	if (flags < 0)
151 		return (errno);
152 
153 	if ((flags & O_ACCMODE) != mode)
154 		return (errno);
155 
156 	return (0);
157 }
158 
159 static bool
160 test_file_cap(int fd, cap_rights_t *rights)
161 {
162 	cap_rights_t fdrights;
163 
164 	ATF_REQUIRE(cap_rights_get(fd, &fdrights) == 0);
165 
166 	return (cap_rights_contains(&fdrights, rights));
167 }
168 
169 static int
170 test_file_write(int fd)
171 {
172 	char buf;
173 
174 	buf = 't';
175 	if (write(fd, &buf, sizeof(buf)) != sizeof(buf)) {
176 		return (errno);
177 	}
178 
179 	return (0);
180 }
181 
182 static int
183 test_file_read(int fd)
184 {
185 	char buf;
186 
187 	if (read(fd, &buf, sizeof(buf)) < 0) {
188 		return (errno);
189 	}
190 
191 	return (0);
192 }
193 
194 static int
195 test_file_fwrite(FILE *pfile)
196 {
197 	char buf;
198 
199 	buf = 't';
200 	if (fwrite(&buf, sizeof(buf), 1, pfile) != sizeof(buf))
201 		return (errno);
202 
203 	return (0);
204 }
205 
206 static int
207 test_file_fread(FILE *pfile)
208 {
209 	char buf;
210 	int ret, serrno;
211 
212 	errno = 0;
213 	ret = fread(&buf, sizeof(buf), 1, pfile);
214 	serrno = errno;
215 	if (ret < 0) {
216 		return (serrno);
217 	} else if (ret == 0 && feof(pfile) == 0) {
218 		return (serrno != 0 ? serrno : EINVAL);
219 	}
220 
221 	return (0);
222 }
223 
224 ATF_TC_WITH_CLEANUP(fileargs__open_read);
225 ATF_TC_HEAD(fileargs__open_read, tc) {}
226 ATF_TC_BODY(fileargs__open_read, tc)
227 {
228 	cap_rights_t rights, norights;
229 	fileargs_t *fa;
230 	size_t i;
231 	int fd;
232 
233 	prepare_files(MAX_FILES, true);
234 
235 	cap_rights_init(&rights, CAP_READ | CAP_FCNTL);
236 	cap_rights_init(&norights, CAP_WRITE);
237 	fa = fileargs_init(MAX_FILES, files, O_RDONLY, 0, &rights,
238 	    FA_OPEN);
239 	ATF_REQUIRE(fa != NULL);
240 
241 	for (i = 0; i < MAX_FILES; i++) {
242 		/* ALLOWED */
243 		/* We open file twice to check if we can. */
244 		ATF_REQUIRE(test_file_open(fa, files[i], &fd) == 0);
245 		ATF_REQUIRE(close(fd) == 0);
246 
247 		ATF_REQUIRE(test_file_open(fa, files[i], &fd) == 0);
248 		ATF_REQUIRE(test_file_mode(fd, O_RDONLY) == 0);
249 		ATF_REQUIRE(test_file_cap(fd, &rights) == true);
250 		ATF_REQUIRE(test_file_read(fd) == 0);
251 
252 		/* DISALLOWED */
253 		ATF_REQUIRE(test_file_lstat(fa, files[i]) == ENOTCAPABLE);
254 		ATF_REQUIRE(test_file_open(fa, TEST_FILE, NULL) == ENOTCAPABLE);
255 		ATF_REQUIRE(test_file_cap(fd, &norights) == false);
256 		ATF_REQUIRE(test_file_write(fd) == ENOTCAPABLE);
257 
258 		/* CLOSE */
259 		ATF_REQUIRE(close(fd) == 0);
260 	}
261 }
262 ATF_TC_CLEANUP(fileargs__open_read, tc)
263 {
264 	clear_files();
265 }
266 
267 ATF_TC_WITH_CLEANUP(fileargs__open_write);
268 ATF_TC_HEAD(fileargs__open_write, tc) {}
269 ATF_TC_BODY(fileargs__open_write, tc)
270 {
271 	cap_rights_t rights, norights;
272 	fileargs_t *fa;
273 	size_t i;
274 	int fd;
275 
276 	prepare_files(MAX_FILES, true);
277 
278 	cap_rights_init(&rights, CAP_WRITE | CAP_FCNTL);
279 	cap_rights_init(&norights, CAP_READ);
280 	fa = fileargs_init(MAX_FILES, files, O_WRONLY, 0, &rights,
281 	    FA_OPEN);
282 	ATF_REQUIRE(fa != NULL);
283 
284 	for (i = 0; i < MAX_FILES; i++) {
285 		/* ALLOWED */
286 		/* We open file twice to check if we can. */
287 		ATF_REQUIRE(test_file_open(fa, files[i], &fd) == 0);
288 		ATF_REQUIRE(close(fd) == 0);
289 
290 		ATF_REQUIRE(test_file_open(fa, files[i], &fd) == 0);
291 		ATF_REQUIRE(test_file_mode(fd, O_WRONLY) == 0);
292 		ATF_REQUIRE(test_file_cap(fd, &rights) == true);
293 		ATF_REQUIRE(test_file_write(fd) == 0);
294 
295 		/* DISALLOWED */
296 		ATF_REQUIRE(test_file_lstat(fa, files[i]) == ENOTCAPABLE);
297 		ATF_REQUIRE(test_file_open(fa, TEST_FILE, NULL) == ENOTCAPABLE);
298 		ATF_REQUIRE(test_file_cap(fd, &norights) == false);
299 		ATF_REQUIRE(test_file_read(fd) == ENOTCAPABLE);
300 
301 		/* CLOSE */
302 		ATF_REQUIRE(close(fd) == 0);
303 	}
304 }
305 ATF_TC_CLEANUP(fileargs__open_write, tc)
306 {
307 	clear_files();
308 }
309 
310 ATF_TC_WITH_CLEANUP(fileargs__open_create);
311 ATF_TC_HEAD(fileargs__open_create, tc) {}
312 ATF_TC_BODY(fileargs__open_create, tc)
313 {
314 	cap_rights_t rights, norights;
315 	fileargs_t *fa;
316 	size_t i;
317 	int fd;
318 
319 	prepare_files(MAX_FILES, false);
320 
321 	cap_rights_init(&rights, CAP_WRITE | CAP_FCNTL | CAP_READ);
322 	cap_rights_init(&norights, CAP_FCHMOD);
323 	fa = fileargs_init(MAX_FILES, files, O_RDWR | O_CREAT, 666,
324 	    &rights, FA_OPEN);
325 	ATF_REQUIRE(fa != NULL);
326 
327 	for (i = 0; i < MAX_FILES; i++) {
328 		/* ALLOWED */
329 		ATF_REQUIRE(test_file_open(fa, files[i], &fd) == 0);
330 
331 		ATF_REQUIRE(test_file_mode(fd, O_RDWR) == 0);
332 		ATF_REQUIRE(test_file_cap(fd, &rights) == true);
333 		ATF_REQUIRE(test_file_write(fd) == 0);
334 		ATF_REQUIRE(test_file_read(fd) == 0);
335 
336 		/* DISALLOWED */
337 		ATF_REQUIRE(test_file_lstat(fa, files[i]) == ENOTCAPABLE);
338 		ATF_REQUIRE(test_file_open(fa, TEST_FILE, NULL) == ENOTCAPABLE);
339 		ATF_REQUIRE(test_file_cap(fd, &norights) == false);
340 
341 		/* CLOSE */
342 		ATF_REQUIRE(close(fd) == 0);
343 	}
344 }
345 ATF_TC_CLEANUP(fileargs__open_create, tc)
346 {
347 	clear_files();
348 }
349 
350 ATF_TC_WITH_CLEANUP(fileargs__open_with_casper);
351 ATF_TC_HEAD(fileargs__open_with_casper, tc) {}
352 ATF_TC_BODY(fileargs__open_with_casper, tc)
353 {
354 	cap_channel_t *capcas;
355 	cap_rights_t rights;
356 	fileargs_t *fa;
357 	size_t i;
358 	int fd;
359 
360 	prepare_files(MAX_FILES, true);
361 
362 	capcas = cap_init();
363 	ATF_REQUIRE(capcas != NULL);
364 
365 	cap_rights_init(&rights, CAP_READ);
366 	fa = fileargs_cinit(capcas, MAX_FILES, files, O_RDONLY, 0, &rights,
367 	    FA_OPEN);
368 	ATF_REQUIRE(fa != NULL);
369 
370 	for (i = 0; i < MAX_FILES; i++) {
371 		/* ALLOWED */
372 		ATF_REQUIRE(test_file_open(fa, files[i], &fd) == 0);
373 		ATF_REQUIRE(test_file_read(fd) == 0);
374 
375 		/* CLOSE */
376 		ATF_REQUIRE(close(fd) == 0);
377 	}
378 }
379 ATF_TC_CLEANUP(fileargs__open_with_casper, tc)
380 {
381 	clear_files();
382 }
383 
384 ATF_TC_WITH_CLEANUP(fileargs__fopen_read);
385 ATF_TC_HEAD(fileargs__fopen_read, tc) {}
386 ATF_TC_BODY(fileargs__fopen_read, tc)
387 {
388 	cap_rights_t rights, norights;
389 	fileargs_t *fa;
390 	size_t i;
391 	FILE *pfile;
392 	int fd;
393 
394 	prepare_files(MAX_FILES, true);
395 
396 	cap_rights_init(&rights, CAP_READ | CAP_FCNTL);
397 	cap_rights_init(&norights, CAP_WRITE);
398 	fa = fileargs_init(MAX_FILES, files, O_RDONLY, 0, &rights,
399 	    FA_OPEN);
400 	ATF_REQUIRE(fa != NULL);
401 
402 	for (i = 0; i < MAX_FILES; i++) {
403 		/* ALLOWED */
404 		/* We fopen file twice to check if we can. */
405 		ATF_REQUIRE(test_file_fopen(fa, files[i], "r", &pfile) == 0);
406 		ATF_REQUIRE(fclose(pfile) == 0);
407 
408 		ATF_REQUIRE(test_file_fopen(fa, files[i], "r", &pfile) == 0);
409 		fd = fileno(pfile);
410 		ATF_REQUIRE(test_file_mode(fd, O_RDONLY) == 0);
411 		ATF_REQUIRE(test_file_cap(fd, &rights) == true);
412 		ATF_REQUIRE(test_file_fread(pfile) == 0);
413 
414 		/* DISALLOWED */
415 		ATF_REQUIRE(test_file_lstat(fa, files[i]) == ENOTCAPABLE);
416 		ATF_REQUIRE(test_file_fopen(fa, TEST_FILE, "r", NULL) ==
417 		    ENOTCAPABLE);
418 		ATF_REQUIRE(test_file_cap(fd, &norights) == false);
419 		ATF_REQUIRE(test_file_fwrite(pfile) == EBADF);
420 
421 		/* CLOSE */
422 		ATF_REQUIRE(fclose(pfile) == 0);
423 	}
424 }
425 ATF_TC_CLEANUP(fileargs__fopen_read, tc)
426 {
427 	clear_files();
428 }
429 
430 ATF_TC_WITH_CLEANUP(fileargs__fopen_write);
431 ATF_TC_HEAD(fileargs__fopen_write, tc) {}
432 ATF_TC_BODY(fileargs__fopen_write, tc)
433 {
434 	cap_rights_t rights, norights;
435 	fileargs_t *fa;
436 	size_t i;
437 	FILE *pfile;
438 	int fd;
439 
440 	prepare_files(MAX_FILES, true);
441 
442 	cap_rights_init(&rights, CAP_WRITE | CAP_FCNTL);
443 	cap_rights_init(&norights, CAP_READ);
444 	fa = fileargs_init(MAX_FILES, files, O_WRONLY, 0, &rights,
445 	    FA_OPEN);
446 	ATF_REQUIRE(fa != NULL);
447 
448 	for (i = 0; i < MAX_FILES; i++) {
449 		/* ALLOWED */
450 		/* We fopen file twice to check if we can. */
451 		ATF_REQUIRE(test_file_fopen(fa, files[i], "w", &pfile) == 0);
452 		ATF_REQUIRE(fclose(pfile) == 0);
453 
454 		ATF_REQUIRE(test_file_fopen(fa, files[i], "w", &pfile) == 0);
455 		fd = fileno(pfile);
456 		ATF_REQUIRE(test_file_mode(fd, O_WRONLY) == 0);
457 		ATF_REQUIRE(test_file_cap(fd, &rights) == true);
458 		ATF_REQUIRE(test_file_fwrite(pfile) == 0);
459 
460 		/* DISALLOWED */
461 		ATF_REQUIRE(test_file_lstat(fa, files[i]) == ENOTCAPABLE);
462 		ATF_REQUIRE(test_file_fopen(fa, TEST_FILE, "w", NULL) ==
463 		    ENOTCAPABLE);
464 		ATF_REQUIRE(test_file_cap(fd, &norights) == false);
465 		ATF_REQUIRE(test_file_fread(pfile) == EBADF);
466 
467 		/* CLOSE */
468 		ATF_REQUIRE(fclose(pfile) == 0);
469 	}
470 }
471 ATF_TC_CLEANUP(fileargs__fopen_write, tc)
472 {
473 	clear_files();
474 }
475 
476 ATF_TC_WITH_CLEANUP(fileargs__fopen_create);
477 ATF_TC_HEAD(fileargs__fopen_create, tc) {}
478 ATF_TC_BODY(fileargs__fopen_create, tc)
479 {
480 	cap_rights_t rights;
481 	fileargs_t *fa;
482 	size_t i;
483 	FILE *pfile;
484 	int fd;
485 
486 	prepare_files(MAX_FILES, false);
487 
488 	cap_rights_init(&rights, CAP_READ | CAP_WRITE | CAP_FCNTL);
489 	fa = fileargs_init(MAX_FILES, files, O_RDWR | O_CREAT, 0, &rights,
490 	    FA_OPEN);
491 	ATF_REQUIRE(fa != NULL);
492 
493 	for (i = 0; i < MAX_FILES; i++) {
494 		/* ALLOWED */
495 		/* We fopen file twice to check if we can. */
496 		ATF_REQUIRE(test_file_fopen(fa, files[i], "w+", &pfile) == 0);
497 		fd = fileno(pfile);
498 		ATF_REQUIRE(test_file_mode(fd, O_RDWR) == 0);
499 		ATF_REQUIRE(test_file_cap(fd, &rights) == true);
500 		ATF_REQUIRE(test_file_fwrite(pfile) == 0);
501 		ATF_REQUIRE(test_file_fread(pfile) == 0);
502 
503 		/* DISALLOWED */
504 		ATF_REQUIRE(test_file_lstat(fa, files[i]) == ENOTCAPABLE);
505 		ATF_REQUIRE(test_file_fopen(fa, TEST_FILE, "w+", NULL) ==
506 		    ENOTCAPABLE);
507 
508 		/* CLOSE */
509 		ATF_REQUIRE(fclose(pfile) == 0);
510 	}
511 }
512 ATF_TC_CLEANUP(fileargs__fopen_create, tc)
513 {
514 	clear_files();
515 }
516 
517 ATF_TC_WITH_CLEANUP(fileargs__lstat);
518 ATF_TC_HEAD(fileargs__lstat, tc) {}
519 ATF_TC_BODY(fileargs__lstat, tc)
520 {
521 	fileargs_t *fa;
522 	size_t i;
523 	int fd;
524 
525 	prepare_files(MAX_FILES, true);
526 
527 	fa = fileargs_init(MAX_FILES, files, 0, 0, NULL, FA_LSTAT);
528 	ATF_REQUIRE(fa != NULL);
529 
530 	for (i = 0; i < MAX_FILES; i++) {
531 		/* ALLOWED */
532 		ATF_REQUIRE(test_file_lstat(fa, files[i]) == 0);
533 
534 		/* DISALLOWED */
535 		ATF_REQUIRE(test_file_open(fa, files[i], &fd) == ENOTCAPABLE);
536 		ATF_REQUIRE(test_file_lstat(fa, TEST_FILE) == ENOTCAPABLE);
537 		ATF_REQUIRE(test_file_open(fa, TEST_FILE, &fd) == ENOTCAPABLE);
538 	}
539 }
540 ATF_TC_CLEANUP(fileargs__lstat, tc)
541 {
542 	clear_files();
543 }
544 
545 ATF_TC_WITH_CLEANUP(fileargs__open_lstat);
546 ATF_TC_HEAD(fileargs__open_lstat, tc) {}
547 ATF_TC_BODY(fileargs__open_lstat, tc)
548 {
549 	cap_rights_t rights, norights;
550 	fileargs_t *fa;
551 	size_t i;
552 	int fd;
553 
554 	prepare_files(MAX_FILES, true);
555 
556 	cap_rights_init(&rights, CAP_READ | CAP_FCNTL);
557 	cap_rights_init(&norights, CAP_WRITE);
558 	fa = fileargs_init(MAX_FILES, files, O_RDONLY, 0, &rights,
559 	    FA_OPEN | FA_LSTAT);
560 	ATF_REQUIRE(fa != NULL);
561 
562 	for (i = 0; i < MAX_FILES; i++) {
563 		/* ALLOWED */
564 		/* We open file twice to check if we can. */
565 		ATF_REQUIRE(test_file_lstat(fa, files[i]) == 0);
566 		ATF_REQUIRE(test_file_open(fa, files[i], &fd) == 0);
567 		ATF_REQUIRE(close(fd) == 0);
568 
569 		ATF_REQUIRE(test_file_open(fa, files[i], &fd) == 0);
570 		ATF_REQUIRE(test_file_lstat(fa, files[i]) == 0);
571 		ATF_REQUIRE(test_file_mode(fd, O_RDONLY) == 0);
572 		ATF_REQUIRE(test_file_cap(fd, &rights) == true);
573 		ATF_REQUIRE(test_file_read(fd) == 0);
574 
575 		/* DISALLOWED */
576 		ATF_REQUIRE(test_file_open(fa, TEST_FILE, NULL) == ENOTCAPABLE);
577 		ATF_REQUIRE(test_file_cap(fd, &norights) == false);
578 		ATF_REQUIRE(test_file_write(fd) == ENOTCAPABLE);
579 
580 		/* CLOSE */
581 		ATF_REQUIRE(close(fd) == 0);
582 	}
583 }
584 ATF_TC_CLEANUP(fileargs__open_lstat, tc)
585 {
586 	clear_files();
587 }
588 
589 ATF_TP_ADD_TCS(tp)
590 {
591 
592 	ATF_TP_ADD_TC(tp, fileargs__open_create);
593 	ATF_TP_ADD_TC(tp, fileargs__open_read);
594 	ATF_TP_ADD_TC(tp, fileargs__open_write);
595 	ATF_TP_ADD_TC(tp, fileargs__open_with_casper);
596 
597 	ATF_TP_ADD_TC(tp, fileargs__fopen_create);
598 	ATF_TP_ADD_TC(tp, fileargs__fopen_read);
599 	ATF_TP_ADD_TC(tp, fileargs__fopen_write);
600 
601 	ATF_TP_ADD_TC(tp, fileargs__lstat);
602 
603 	ATF_TP_ADD_TC(tp, fileargs__open_lstat);
604 
605 	return (atf_no_error());
606 }
607