/* $OpenBSD: syscalls.c,v 1.37 2024/09/03 04:59:03 anton Exp $ */ /* * Copyright (c) 2017-2019 Bob Beck * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include "unveil.h" /* all the things unless we override */ const char *uv_flags = "rwxc"; static void do_unveil(void) { if (unveil(uv_dir1, uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil(uv_file1, uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); } static void do_unveil2(void) { if (unveil(uv_dir1, uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); } static int test_openat(int do_uv) { int slashbefore; int dirfd1before; int dirfd2before; int dirfd1after; int dirfd2after; UV_SHOULD_SUCCEED(((slashbefore = open("/", O_RDONLY | O_DIRECTORY)) == -1), "open"); UV_SHOULD_SUCCEED(((dirfd1before = open(uv_dir1, O_RDONLY | O_DIRECTORY)) == -1), "open"); UV_SHOULD_SUCCEED(((dirfd2before = open(uv_dir2, O_RDONLY | O_DIRECTORY)) == -1), "open"); if (do_uv) { printf("testing openat\n"); do_unveil(); } UV_SHOULD_SUCCEED(((dirfd1after = open(uv_dir1, O_RDONLY | O_DIRECTORY)) == -1), "open"); UV_SHOULD_ENOENT(((dirfd2after = open(uv_dir2, O_RDONLY | O_DIRECTORY)) == -1), "open"); UV_SHOULD_ENOENT((openat(slashbefore, "etc/hosts", O_RDONLY) == -1), "openat"); UV_SHOULD_SUCCEED((openat(slashbefore, uv_file1, O_RDWR) == -1), "openat"); UV_SHOULD_ENOENT((openat(slashbefore, uv_file2, O_RDWR) == -1), "openat"); UV_SHOULD_ENOENT((openat(dirfd1before, "/etc/hosts", O_RDONLY) == -1), "openat"); UV_SHOULD_SUCCEED((openat(dirfd1before, "hooray", O_RDWR|O_CREAT, 0644) == -1), "openat"); UV_SHOULD_SUCCEED((openat(dirfd1before, uv_file1, O_RDWR|O_CREAT, 0644) == -1), "openat"); UV_SHOULD_ENOENT((openat(dirfd1before, uv_file2, O_RDWR|O_CREAT, 0644) == -1), "openat"); UV_SHOULD_ENOENT((openat(dirfd2before, "/etc/hosts", O_RDONLY) == -1), "openat"); UV_SHOULD_ENOENT((openat(dirfd2before, "hooray", O_RDWR|O_CREAT, 0644) == -1), "openat"); UV_SHOULD_SUCCEED((openat(dirfd2before, uv_file1, O_RDWR|O_CREAT, 0644) == -1), "openat"); UV_SHOULD_ENOENT((openat(dirfd2before, uv_file2, O_RDWR|O_CREAT, 0644) == -1), "openat"); UV_SHOULD_ENOENT((openat(dirfd1after, "/etc/hosts", O_RDONLY) == -1), "openat"); UV_SHOULD_SUCCEED((openat(dirfd1after, "hooray", O_RDWR|O_CREAT, 0644) == -1), "openat"); UV_SHOULD_SUCCEED((openat(dirfd1after, uv_file1, O_RDWR|O_CREAT, 0644) == -1), "openat"); UV_SHOULD_ENOENT((openat(dirfd1after, uv_file2, O_RDWR|O_CREAT, 0644) == -1), "openat"); UV_SHOULD_SUCCEED(((dirfd2after = openat(dirfd1after, "subdir", O_RDONLY | O_DIRECTORY)) == -1), "openat"); UV_SHOULD_SUCCEED((openat(dirfd2after, "../derp", O_RDWR|O_CREAT, 0644) == -1), "openat"); UV_SHOULD_ENOENT((openat(dirfd2after, "../../derpyluvs", O_RDWR|O_CREAT, 0644) == -1), "openat"); UV_SHOULD_ENOENT((openat(dirfd2after, "/etc/hosts", O_RDONLY) == -1), "openat"); UV_SHOULD_SUCCEED((openat(dirfd2after, "hooray", O_RDWR|O_CREAT, 0644) == -1), "openat"); UV_SHOULD_SUCCEED((openat(dirfd2after, uv_file1, O_RDWR|O_CREAT, 0644) == -1), "openat"); UV_SHOULD_ENOENT((openat(dirfd2after, uv_file2, O_RDWR|O_CREAT, 0644) == -1), "openat"); return 0; } static int test_open(int do_uv) { char filename[256]; int dirfd; int dirfd2; int dirfd3; UV_SHOULD_SUCCEED(((dirfd = open("/", O_RDONLY | O_DIRECTORY)) == -1), "open"); UV_SHOULD_SUCCEED(((dirfd2 = open(uv_dir2, O_RDONLY | O_DIRECTORY)) == -1), "open"); if (do_uv) { printf("testing open\n"); do_unveil(); if (unveil("/tmp/alpha", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/bravo", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/charlie", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/delta", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/echo", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/foxtrot", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/golf", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/hotel", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/india", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/juliet", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/kilo", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/lima", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/money", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/november", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/oscar", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/papa", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/quebec", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/romeo", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/sierra", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/tango", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/uniform", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/victor", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/whiskey", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/xray", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/yankee", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/tmp/zulu", uv_flags) == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); } UV_SHOULD_SUCCEED((pledge("unveil stdio rpath cpath wpath exec", NULL) == -1), "pledge"); UV_SHOULD_ENOENT((open(uv_file2, O_RDWR) == -1), "open"); UV_SHOULD_ENOENT(((dirfd3= open(uv_dir2, O_RDONLY | O_DIRECTORY)) == -1), "open"); UV_SHOULD_SUCCEED((open(uv_file1, O_RDWR) == -1), "open"); if (!do_uv) { /* Unlink the unveiled file and make it again */ UV_SHOULD_SUCCEED((unlink(uv_file1) == -1), "unlink"); UV_SHOULD_SUCCEED((open(uv_file1, O_RDWR|O_CREAT, 0644) == -1), "open"); } sleep(1); UV_SHOULD_SUCCEED((open(uv_file1, O_RDWR) == -1), "open"); UV_SHOULD_ENOENT((open(uv_file2, O_RDWR) == -1), "open"); (void) snprintf(filename, sizeof(filename), "%s/%s", uv_dir1, "newfile"); UV_SHOULD_SUCCEED((open(filename, O_RDWR|O_CREAT, 0644) == -1), "open"); (void) snprintf(filename, sizeof(filename), "/%s/%s", uv_dir1, "doubleslash"); UV_SHOULD_SUCCEED((open(filename, O_RDWR|O_CREAT, 0644) == -1), "open"); (void) snprintf(filename, sizeof(filename), "/%s//%s", uv_dir1, "doubleslash2"); UV_SHOULD_SUCCEED((open(filename, O_RDWR|O_CREAT, 0644) == -1), "open"); (void) snprintf(filename, sizeof(filename), "%s/%s", uv_dir2, "newfile"); UV_SHOULD_ENOENT((open(filename, O_RDWR|O_CREAT, 0644) == -1), "open"); if (do_uv) { printf("testing flag escalation\n"); if (unveil(uv_file1, "x") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil(uv_file1, "rx") == -1) if (errno != EPERM) err(1, "%s:%d - unveil", __FILE__, __LINE__); } return 0; } static int test_opendir(int do_uv) { char filename[256]; if (do_uv) { printf("testing opendir\n"); do_unveil(); } UV_SHOULD_SUCCEED((opendir(uv_dir1) == NULL), "opendir"); UV_SHOULD_ENOENT((opendir(uv_dir2) == NULL), "opendir"); (void) snprintf(filename, sizeof(filename), "/%s/.", uv_dir1); UV_SHOULD_SUCCEED((opendir(filename) == NULL), "opendir"); (void) snprintf(filename, sizeof(filename), "/%s/..", uv_dir1); UV_SHOULD_ENOENT((opendir(filename) == NULL), "opendir"); (void) snprintf(filename, sizeof(filename), "/%s/subdir", uv_dir1); UV_SHOULD_SUCCEED((opendir(filename) == NULL), "opendir"); (void) snprintf(filename, sizeof(filename), "/%s/subdir/../subdir", uv_dir1); UV_SHOULD_SUCCEED((opendir(filename) == NULL), "opendir"); (void) snprintf(filename, sizeof(filename), "/%s/../../%s/subdir", uv_dir1, uv_dir1); UV_SHOULD_SUCCEED((opendir(filename) == NULL), "opendir"); (void) snprintf(filename, sizeof(filename), "/%s/subdir", uv_dir2); UV_SHOULD_ENOENT((opendir(filename) == NULL), "opendir"); UV_SHOULD_ENOENT((opendir(filename) == NULL), "opendir"); (void) snprintf(filename, sizeof(filename), "%s/../..%s/subdir", uv_dir1, uv_dir2); UV_SHOULD_ENOENT((opendir(filename) == NULL), "opendir"); return 0; } static int test_realpath(int do_uv) { char buf[PATH_MAX]; if (do_uv) { printf("testing realpath\n"); do_unveil(); } UV_SHOULD_SUCCEED((realpath(uv_dir1, buf) == NULL), "realpath"); UV_SHOULD_ENOENT((realpath(uv_dir2, buf) == NULL), "realpath"); return 0; } static int test_r(int do_uv) { if (do_uv) { printf("testing \"r\"\n"); if (unveil(uv_file1, "r") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/", "") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); } UV_SHOULD_SUCCEED((open(uv_file1, O_RDONLY) == -1), "open"); UV_SHOULD_EACCES((open(uv_file1, O_RDWR) == -1), "open"); return 0; } static int test_rw(int do_uv) { if (do_uv) { printf("testing \"rw\"\n"); if (unveil(uv_file1, "rw") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/", "") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); } UV_SHOULD_SUCCEED((open(uv_file1, O_RDWR) == -1), "open"); UV_SHOULD_SUCCEED((open(uv_file1, O_RDONLY) == -1), "open"); return 0; } static int test_x(int do_uv) { struct stat sb; if (do_uv) { printf("testing \"x\"\n"); if (unveil(uv_file1, "x") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/", "") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); } UV_SHOULD_EACCES((lstat(uv_file1, &sb) == -1), "lstat"); UV_SHOULD_EACCES((open(uv_file1, O_RDONLY) == -1), "open"); UV_SHOULD_EACCES((open(uv_file1, O_RDONLY) == -1), "open"); UV_SHOULD_ENOENT((open(uv_file2, O_RDWR) == -1), "open"); return 0; } static int test_noflags(int do_uv) { char filename[256]; if (do_uv) { printf("testing clearing flags\n"); do_unveil(); } UV_SHOULD_SUCCEED((pledge("unveil stdio rpath cpath wpath exec", NULL) == -1), "pledge"); UV_SHOULD_SUCCEED((open(uv_file1, O_RDWR) == -1), "open"); if (do_uv) { if (unveil(uv_dir1, "") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); } (void) snprintf(filename, sizeof(filename), "%s/%s", uv_dir1, "noflagsiamboned"); UV_SHOULD_ENOENT((open(filename, O_RDWR|O_CREAT, 0644) == -1), "open"); UV_SHOULD_SUCCEED((open(uv_file1, O_RDWR) == -1), "open"); return 0; } static int test_drounveil(int do_uv) { if (do_uv) { printf("(testing unveil after pledge)\n"); do_unveil(); } UV_SHOULD_SUCCEED((pledge("unveil stdio rpath cpath wpath exec", NULL) == -1), "pledge"); if (do_uv) { do_unveil(); } UV_SHOULD_SUCCEED((pledge("stdio rpath cpath wpath", NULL) == -1), "pledge"); UV_SHOULD_ENOENT((open(uv_file2, O_RDWR) == -1), "open"); UV_SHOULD_SUCCEED((open(uv_file1, O_RDWR) == -1), "open"); return 0; } static int test_unlink(int do_uv) { char filename1[256]; char filename2[256]; char filename3[] = "/tmp/nukeme.XXXXXX"; int fd; (void) snprintf(filename1, sizeof(filename1), "%s/%s", uv_dir1, "nukeme"); (void) snprintf(filename2, sizeof(filename2), "%s/%s", uv_dir2, "nukeme"); UV_SHOULD_SUCCEED((open(filename1, O_RDWR|O_CREAT, 0644) == -1), "open"); UV_SHOULD_SUCCEED((open(filename2, O_RDWR|O_CREAT, 0644) == -1), "open"); if ((fd = mkstemp(filename3)) == -1) err(1, "%s:%d - mkstemp", __FILE__, __LINE__); if (do_uv) { printf("testing unlink\n"); do_unveil(); if (unveil(filename3, "rw") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); } UV_SHOULD_SUCCEED((pledge("unveil stdio fattr rpath cpath wpath", NULL) == -1), "pledge"); UV_SHOULD_SUCCEED((unlink(filename1) == -1), "unlink"); UV_SHOULD_ENOENT((unlink(filename2) == -1), "unlink"); UV_SHOULD_EACCES((unlink(filename3) == -1), "unlink"); return 0; } static int test_link(int do_uv) { char filename[256]; char filename2[256]; if (do_uv) { printf("testing link\n"); do_unveil(); } UV_SHOULD_SUCCEED((pledge("unveil stdio fattr rpath cpath wpath", NULL) == -1), "pledge"); (void) snprintf(filename, sizeof(filename), "%s/%s", uv_dir1, "linkuv1"); (void) snprintf(filename2, sizeof(filename2), "%s/%s", uv_dir2, "linkuv2"); unlink(filename); unlink(filename2); UV_SHOULD_SUCCEED((link(uv_file1, filename) == -1), "link"); unlink(filename); UV_SHOULD_ENOENT((link(uv_file2, filename) == -1), "link"); UV_SHOULD_ENOENT((link(uv_file1, filename2) == -1), "link"); if (do_uv) { printf("testing link without O_CREAT\n"); if (unveil(filename, "rw") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); } UV_SHOULD_EACCES((link(uv_file1, filename) == -1), "link"); unlink(filename); return 0; } static int test_chdir(int do_uv) { if (do_uv) { printf("testing chdir\n"); do_unveil2(); } UV_SHOULD_SUCCEED((pledge("stdio fattr rpath", NULL) == -1), "pledge"); UV_SHOULD_ENOENT((chdir(uv_dir2) == -1), "chdir"); UV_SHOULD_SUCCEED((chdir(uv_dir1) == -1), "chdir"); UV_SHOULD_ENOENT((chdir(uv_dir2) == -1), "chdir"); return 0; } static int test_parent_dir(int do_uv) { char filename[255]; if (do_uv) { printf("testing parent dir\n"); do_unveil2(); } else { (void) snprintf(filename, sizeof(filename), "/%s/doof", uv_dir1); UV_SHOULD_SUCCEED((mkdir(filename, 0777) == -1), "mkdir"); (void) snprintf(filename, sizeof(filename), "/%s/doof/subdir2", uv_dir1); UV_SHOULD_SUCCEED((mkdir(filename, 0777) == -1), "mkdir"); (void) snprintf(filename, sizeof(filename), "/%s/doof/subdir1", uv_dir1); UV_SHOULD_SUCCEED((mkdir(filename, 0777) == -1), "mkdir"); (void) snprintf(filename, sizeof(filename), "/%s/doof/subdir1/poop", uv_dir1); UV_SHOULD_SUCCEED((open(filename, O_RDWR|O_CREAT, 0644) == -1), "open"); (void) snprintf(filename, sizeof(filename), "/%s/doof/subdir1/link", uv_dir1); UV_SHOULD_SUCCEED((symlink("../subdir1/poop", filename) == -1), "symlink"); } sleep(1); (void) snprintf(filename, sizeof(filename), "/%s/doof/subdir1/link", uv_dir1); UV_SHOULD_SUCCEED((access(filename, R_OK) == -1), "access"); (void) snprintf(filename, sizeof(filename), "/%s/doof/subdir1/poop", uv_dir1); UV_SHOULD_SUCCEED((access(filename, R_OK) == -1), "access"); UV_SHOULD_SUCCEED((chdir(uv_dir1) == -1), "chdir"); (void) snprintf(filename, sizeof(filename), "/%s/doof/subdir1", uv_dir1); UV_SHOULD_SUCCEED((chdir(filename) == -1), "chdir"); UV_SHOULD_SUCCEED((access("poop", R_OK) == -1), "access"); UV_SHOULD_SUCCEED((chdir("../subdir2") == -1), "chdir"); UV_SHOULD_SUCCEED((chdir("../subdir1") == -1), "chdir"); UV_SHOULD_SUCCEED((chdir(filename) == -1), "chdir"); UV_SHOULD_SUCCEED((chdir("../../doof/subdir2") == -1), "chdir"); UV_SHOULD_SUCCEED((chdir("../../doof/subdir1") == -1), "chdir"); UV_SHOULD_SUCCEED((chdir("../../doof/subdir2") == -1), "chdir"); UV_SHOULD_SUCCEED((chdir("../../doof/subdir1") == -1), "chdir"); UV_SHOULD_SUCCEED((access("poop", R_OK) == -1), "access"); UV_SHOULD_SUCCEED((access("../subdir1/poop", R_OK) == -1), "access"); UV_SHOULD_ENOENT((chdir("../../..") == -1), "chdir"); UV_SHOULD_ENOENT((chdir(uv_dir2) == -1), "chdir"); return(0); } static int test_rename(int do_uv) { char filename1[256]; char filename2[256]; char rfilename1[256]; char rfilename2[256]; int dirfd1, dirfd2; if ((dirfd1 = open(uv_dir1, O_RDONLY | O_DIRECTORY)) == -1) err(1, "%s:%d - open of dir1", __FILE__, __LINE__); if ((dirfd2 = open(uv_dir2, O_RDONLY | O_DIRECTORY)) == -1) err(1, "%s:%d - open of dir2", __FILE__, __LINE__); (void) snprintf(filename1, sizeof(filename1), "%s/%s", uv_dir1, "file1"); UV_SHOULD_SUCCEED((open(filename1, O_RDWR|O_CREAT, 0644) == -1), "open"); (void) snprintf(filename2, sizeof(filename2), "%s/%s", uv_dir2, "file2"); UV_SHOULD_SUCCEED((open(filename2, O_RDWR|O_CREAT, 0644) == -1), "open"); (void) snprintf(rfilename1, sizeof(rfilename1), "%s/%s", uv_dir1, "rfile1"); (void) snprintf(rfilename2, sizeof(rfilename2), "%s/%s", uv_dir2, "rfile2"); if (do_uv) { printf("testing rename\n"); do_unveil(); } UV_SHOULD_SUCCEED((pledge("stdio fattr rpath wpath cpath", NULL) == -1), "pledge"); UV_SHOULD_SUCCEED((rename(filename1, rfilename1) == -1), "rename"); UV_SHOULD_ENOENT((rename(filename2, rfilename2) == -1), "rename"); UV_SHOULD_SUCCEED((open(filename1, O_RDWR|O_CREAT, 0644) == -1), "open"); UV_SHOULD_ENOENT((rename(filename1, rfilename2) == -1), "rename"); UV_SHOULD_SUCCEED((open(filename1, O_RDWR|O_CREAT, 0644) == -1), "open"); UV_SHOULD_ENOENT((rename(filename1, uv_file2) == -1), "rename"); UV_SHOULD_SUCCEED((open(filename1, O_RDWR|O_CREAT, 0644) == -1), "open"); UV_SHOULD_ENOENT((renameat(dirfd1, "file1", dirfd2, "rfile2") == -1), "renameat"); UV_SHOULD_SUCCEED((open(filename1, O_RDWR|O_CREAT, 0644) == -1), "open"); UV_SHOULD_ENOENT((renameat(dirfd1, "file1", dirfd2, rfilename2) == -1), "renameat"); return (0); } static int test_access(int do_uv) { if (do_uv) { printf("testing access\n"); do_unveil(); } UV_SHOULD_SUCCEED((access(uv_file1, R_OK) == -1), "access"); UV_SHOULD_ENOENT((access(uv_file2, R_OK) == -1), "access"); UV_SHOULD_ENOENT((access("/etc/passwd", R_OK) == -1), "access"); UV_SHOULD_SUCCEED((access(uv_dir1, R_OK) == -1), "access"); UV_SHOULD_ENOENT((access(uv_dir2, R_OK) == -1), "access"); UV_SHOULD_ENOENT((access("/", R_OK) == -1), "access"); UV_SHOULD_ENOENT((access("/home", F_OK) == -1), "access"); UV_SHOULD_SUCCEED((pledge("stdio fattr rpath", NULL) == -1), "pledge"); UV_SHOULD_SUCCEED((access(uv_file1, R_OK) == -1), "access"); UV_SHOULD_ENOENT((access(uv_file2, R_OK) == -1), "access"); UV_SHOULD_SUCCEED((access(uv_dir1, R_OK) == -1), "access"); UV_SHOULD_ENOENT((access(uv_dir2, R_OK) == -1), "access"); UV_SHOULD_ENOENT((access("/", R_OK) == -1), "access"); UV_SHOULD_ENOENT((access("/home", F_OK) == -1), "access"); return 0; } static int test_chflags(int do_uv) { if (do_uv) { printf("testing chflags\n"); do_unveil(); } UV_SHOULD_SUCCEED((pledge("stdio fattr rpath", NULL) == -1), "pledge"); UV_SHOULD_SUCCEED((chflags(uv_file1, UF_NODUMP) == -1), "chflags"); UV_SHOULD_ENOENT((chflags(uv_file2, UF_NODUMP) == -1), "chflags"); return 0; } static int test_stat(int do_uv) { if (do_uv) { printf("testing stat\n"); do_unveil(); } struct stat sb; UV_SHOULD_SUCCEED((pledge("stdio fattr rpath", NULL) == -1), "pledge"); UV_SHOULD_SUCCEED((stat(uv_file1, &sb) == -1), "stat"); UV_SHOULD_ENOENT((stat(uv_file2, &sb) == -1), "stat"); UV_SHOULD_SUCCEED((stat(uv_dir1, &sb) == -1), "stat"); UV_SHOULD_ENOENT((stat(uv_dir2, &sb) == -1), "stat"); UV_SHOULD_ENOENT((stat("/", &sb) == -1), "stat"); return 0; } static int test_stat2(int do_uv) { if (do_uv) { printf("testing stat components to nonexistent \"rw\"\n"); if (unveil("/usr/share/man/nonexistent", "rw") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); } struct stat sb; UV_SHOULD_SUCCEED((pledge("stdio fattr rpath", NULL) == -1), "pledge"); UV_SHOULD_ENOENT((stat("/", &sb) == -1), "stat"); UV_SHOULD_ENOENT((stat("/usr", &sb) == -1), "stat"); UV_SHOULD_ENOENT((stat("/usr/share", &sb) == -1), "stat"); UV_SHOULD_ENOENT((stat("/usr/share/man", &sb) == -1), "stat"); UV_SHOULD_ENOENT((stat("/usr/share/man/nonexistent", &sb) == -1), "stat"); return 0; } static int test_statfs(int do_uv) { if (do_uv) { printf("testing statfs\n"); do_unveil(); } struct statfs sb; UV_SHOULD_SUCCEED((statfs("/home", &sb) == -1), "statfs"); UV_SHOULD_SUCCEED((statfs("/", &sb) == -1), "statfs"); UV_SHOULD_SUCCEED((statfs(uv_file1, &sb) == -1), "statfs"); UV_SHOULD_SUCCEED((statfs(uv_file2, &sb) == -1), "statfs"); UV_SHOULD_SUCCEED((statfs(uv_dir1, &sb) == -1), "statfs"); UV_SHOULD_SUCCEED((statfs(uv_dir2, &sb) == -1), "statfs"); UV_SHOULD_SUCCEED((pledge("stdio fattr rpath", NULL) == -1), "pledge"); UV_SHOULD_SUCCEED((statfs(uv_file1, &sb) == -1), "statfs"); UV_SHOULD_SUCCEED((statfs(uv_file2, &sb) == -1), "statfs"); UV_SHOULD_SUCCEED((statfs(uv_dir1, &sb) == -1), "statfs"); UV_SHOULD_SUCCEED((statfs(uv_dir2, &sb) == -1), "statfs"); return 0; } static int test_symlink(int do_uv) { char filename[256]; char filename2[256]; char buf[256]; struct stat sb; if (do_uv) { printf("testing symlink and lstat and readlink\n"); do_unveil(); } UV_SHOULD_SUCCEED((pledge("unveil stdio fattr rpath cpath wpath", NULL) == -1), "pledge"); (void) snprintf(filename, sizeof(filename), "%s/%s", uv_dir1, "slinkuv1"); (void) snprintf(filename2, sizeof(filename2), "%s/%s", uv_dir2, "slinkuv2"); unlink(filename); unlink(filename2); UV_SHOULD_SUCCEED((symlink(uv_file1, filename) == -1), "symlink"); UV_SHOULD_SUCCEED((lstat(filename, &sb) == -1), "lstat"); UV_SHOULD_SUCCEED((lstat(uv_file1, &sb) == -1), "lstat"); UV_SHOULD_SUCCEED((readlink(filename, buf, sizeof(buf)) == -1), "readlink"); unlink(filename); UV_SHOULD_SUCCEED((symlink(uv_file2, filename) == -1), "symlink"); UV_SHOULD_SUCCEED((lstat(filename, &sb) == -1), "lstat"); UV_SHOULD_SUCCEED((readlink(filename, buf, sizeof(buf)) == -1), "readlink"); UV_SHOULD_ENOENT((lstat(uv_file2, &sb) == -1), "lstat"); UV_SHOULD_ENOENT((symlink(uv_file1, filename2) == -1), "symlink"); UV_SHOULD_ENOENT((readlink(filename2, buf, sizeof(buf)) == -1), "readlink"); unlink(filename); if (do_uv) { printf("testing symlink with \"rw\"\n"); if (unveil(filename, "rw") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); } UV_SHOULD_EACCES((symlink(uv_file1, filename) == -1), "symlink"); return 0; } static int test_chmod(int do_uv) { if (do_uv) { printf("testing chmod\n"); do_unveil(); } UV_SHOULD_SUCCEED((pledge("stdio fattr rpath unveil", NULL) == -1), "pledge"); UV_SHOULD_SUCCEED((chmod(uv_file1, S_IRWXU) == -1), "chmod"); UV_SHOULD_ENOENT((chmod(uv_file2, S_IRWXU) == -1), "chmod"); UV_SHOULD_SUCCEED((chmod(uv_dir1, S_IRWXU) == -1), "chmod"); UV_SHOULD_ENOENT((chmod(uv_dir2, S_IRWXU) == -1), "chmod"); if (do_uv) { printf("testing chmod should fail for read\n"); if (unveil(uv_file1, "r") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); } UV_SHOULD_EACCES((chmod(uv_file1, S_IRWXU) == -1), "chmod"); return 0; } static int test_fork_body(int do_uv) { UV_SHOULD_SUCCEED((open(uv_file1, O_RDWR|O_CREAT, 0644) == -1), "open after fork"); UV_SHOULD_SUCCEED((opendir(uv_dir1) == NULL), "opendir after fork"); UV_SHOULD_ENOENT((opendir(uv_dir2) == NULL), "opendir after fork"); UV_SHOULD_ENOENT((open(uv_file2, O_RDWR|O_CREAT, 0644) == -1), "open after fork"); return 0; } static int test_fork(int do_uv) { printf("testing fork inhertiance\n"); do_unveil(); return runcompare_internal(test_fork_body, 0); } static int test_exec(int do_uv) { char *argv[] = {"/usr/bin/true", NULL}; extern char **environ; if (do_uv) { printf("testing execve with \"x\"\n"); if (unveil("/usr/bin/true", "x") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); /* dynamic linking requires this */ if (unveil("/usr/lib", "r") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/usr/libexec/ld.so", "r") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); } UV_SHOULD_SUCCEED((pledge("unveil stdio fattr exec", NULL) == -1), "pledge"); UV_SHOULD_SUCCEED((execve(argv[0], argv, environ) == -1), "execve"); return 0; } static int test_exec2(int do_uv) { char *argv[] = {"/usr/bin/true", NULL}; extern char **environ; if (do_uv) { printf("testing execve with \"rw\"\n"); if (unveil("/usr/bin/true", "rw") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); /* dynamic linking requires this */ if (unveil("/usr/lib", "r") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/usr/libexec/ld.so", "r") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); } UV_SHOULD_SUCCEED((pledge("unveil stdio fattr exec", NULL) == -1), "pledge"); UV_SHOULD_EACCES((execve(argv[0], argv, environ) == -1), "execve"); return 0; } static int test_slash(int do_uv) { extern char **environ; if (do_uv) { printf("testing unveil(\"/\")\n"); if (unveil("/bin/sh", "x") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/", "r") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); } return 0; } static int test_dot(int do_uv) { extern char **environ; if (do_uv) { printf("testing dot(\".\")\n"); if (unveil(".", "rwxc") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if ((unlink(".") == -1) && errno != EPERM) err(1, "%s:%d - unlink", __FILE__, __LINE__); printf("testing dot flags(\".\")\n"); if (unveil(".", "r") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if ((unlink(".") == -1) && errno != EACCES) warn("%s:%d - unlink", __FILE__, __LINE__); } return 0; } static int test_bypassunveil(int do_uv) { if (do_uv) { printf("testing BYPASSUNVEIL\n"); do_unveil2(); } char filename3[] = "/tmp/nukeme.XXXXXX"; UV_SHOULD_SUCCEED((pledge("stdio tmppath", NULL) == -1), "pledge"); UV_SHOULD_SUCCEED((mkstemp(filename3) == -1), "mkstemp"); return 0; } static int test_dotdotup(int do_uv) { UV_SHOULD_SUCCEED((open("/tmp/hello", O_RDWR|O_CREAT, 0644) == -1), "open"); if (do_uv) { printf("testing dotdotup\n"); do_unveil2(); } if ((chdir(uv_dir1) == -1)) { err(1, "chdir"); } UV_SHOULD_SUCCEED((open("./derp", O_RDWR|O_CREAT, 0644) == -1), "open"); UV_SHOULD_SUCCEED((open("derp", O_RDWR|O_CREAT, 0644) == -1), "open"); UV_SHOULD_ENOENT((open("../hello", O_RDWR|O_CREAT, 0644) == -1), "open"); UV_SHOULD_ENOENT((open(".././hello", O_RDWR|O_CREAT, 0644) == -1), "open"); return 0; } static int test_kn(int do_uv) { if (do_uv) { printf("testing read only with one writeable file\n"); if (unveil("/", "r") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); if (unveil("/dev/null", "rw") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); } UV_SHOULD_SUCCEED((open("/dev/null", O_RDWR) == -1), "open"); UV_SHOULD_SUCCEED((open("/dev/zero", O_RDONLY) == -1), "open"); UV_SHOULD_EACCES((open("/dev/zero", O_RDWR) == -1), "open"); return 0; } static int test_pathdiscover(int do_uv) { struct stat sb; if (do_uv) { printf("testing path discovery\n"); if (unveil("/usr/share/man", "rx") == -1) err(1, "%s:%d - unveil", __FILE__, __LINE__); } UV_SHOULD_SUCCEED((lstat("/usr/share/man", &sb) == -1), "lstat"); UV_SHOULD_SUCCEED((lstat("/usr/share/man/../../share/man", &sb) == -1), "lstat"); /* XXX XXX XXX This should fail */ UV_SHOULD_SUCCEED((lstat("/usr/share/man/../../local/../share/man", &sb) == -1), "lstat"); return 0; } static int test_fchdir(int do_uv) { int fd2, fd; UV_SHOULD_SUCCEED(((fd2 = open(uv_dir2, O_RDONLY | O_DIRECTORY)) == -1), "open"); if (do_uv) { printf("testing fchdir\n"); do_unveil2(); } UV_SHOULD_SUCCEED((pledge("stdio fattr rpath", NULL) == -1), "pledge"); UV_SHOULD_SUCCEED((chdir(uv_dir1) == -1), "chdir"); UV_SHOULD_SUCCEED((fchdir(fd2) == -1), "fchdir"); UV_SHOULD_ENOENT((fd = (open("subdir", O_RDONLY | O_DIRECTORY)) == -1), "open"); return 0; } static int test_fork_locked(int do_uv) { int status; pid_t pid; if (do_uv) { printf("testing unveil locked fork\n"); unveil(NULL, NULL); } pid = fork(); if (pid == 0) { UV_SHOULD_EPERM((unveil("/", "rwx") == -1), "unveil"); exit(0); } status = 0; waitpid(pid, &status, 0); if (WIFSIGNALED(status)) errx(1, "child exited with signal %d", WTERMSIG(status)); if (WEXITSTATUS(status) == 0) return 0; else return 1; } static int test_intermediate_node(int do_uv) { struct stat st; if (do_uv) { printf("testing unveil on intermediate node\n"); UV_SHOULD_SUCCEED((unveil("/", "r") == -1), "unveil"); UV_SHOULD_SUCCEED((unveil("/usr/bin/id", "rx") == -1), "unveil"); UV_SHOULD_SUCCEED((unveil(NULL, NULL) == -1), "unveil"); } UV_SHOULD_SUCCEED((stat("/usr/bin", &st) == -1), "stat"); return 0; } static int test_noaccess_node(int do_uv) { struct stat st; if (do_uv) { printf("testing unveil on noaccess node\n"); UV_SHOULD_SUCCEED((unveil("/", "r") == -1), "unveil"); UV_SHOULD_SUCCEED((unveil("/usr/bin/id", "rx") == -1), "unveil"); UV_SHOULD_SUCCEED((unveil("/usr/bin", "") == -1), "unveil"); UV_SHOULD_SUCCEED((unveil(NULL, NULL) == -1), "unveil"); } UV_SHOULD_ENOENT((stat("/usr/bin", &st) == -1), "stat"); return 0; } int main(int argc, char *argv[]) { int failures = 0; test_setup(); failures += runcompare(test_open); failures += runcompare(test_openat); failures += runcompare(test_opendir); failures += runcompare(test_noflags); failures += runcompare(test_drounveil); failures += runcompare(test_r); failures += runcompare(test_rw); failures += runcompare(test_x); failures += runcompare(test_unlink); failures += runcompare(test_link); failures += runcompare(test_chdir); failures += runcompare(test_rename); failures += runcompare(test_access); failures += runcompare(test_chflags); failures += runcompare(test_stat); failures += runcompare(test_stat2); failures += runcompare(test_statfs); failures += runcompare(test_symlink); failures += runcompare(test_chmod); failures += runcompare(test_exec); failures += runcompare(test_exec2); failures += runcompare(test_realpath); failures += runcompare(test_parent_dir); failures += runcompare(test_slash); failures += runcompare(test_dot); failures += runcompare(test_bypassunveil); failures += runcompare_internal(test_fork, 0); failures += runcompare(test_dotdotup); failures += runcompare(test_kn); failures += runcompare(test_pathdiscover); failures += runcompare(test_fchdir); failures += runcompare(test_fork_locked); failures += runcompare(test_intermediate_node); failures += runcompare(test_noaccess_node); exit(failures); }