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