1 /* $OpenBSD: nopermtest.c,v 1.1 2018/12/23 11:23:21 natano Exp $ */ 2 3 /* 4 * Copyright (c) 2018 Martin Natano <natano@natano.net> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/stat.h> 20 #include <sys/time.h> 21 22 #include <err.h> 23 #include <errno.h> 24 #include <fcntl.h> 25 #include <limits.h> 26 #include <stdio.h> 27 #include <string.h> 28 #include <stdlib.h> 29 #include <unistd.h> 30 31 #define EXPECT_OK(expr) do { \ 32 int r = (expr); \ 33 if (r == -1) { \ 34 fprintf(stderr, "FAIL:%d: %s -> r=%d errno=%d(%s)\n", \ 35 __LINE__, #expr, r, errno, strerror(errno)); \ 36 nfail++; \ 37 } \ 38 } while (0) 39 40 #define EXPECT_ERRNO(expr, expected_errno) do { \ 41 int r = (expr); \ 42 if (r != -1 || errno != expected_errno) { \ 43 fprintf(stderr, "FAIL:%d: %s -> r=%d errno=%d(%s)\n", \ 44 __LINE__, #expr, r, errno, strerror(errno)); \ 45 nfail++; \ 46 } \ 47 } while (0) 48 49 static void check_locked(const char *); 50 static void check_unlocked(const char *); 51 static void check_unlocked_vroot(void); 52 static void check_unlocked_subdir(void); 53 54 __dead static void usage(void); 55 56 static int nfail; 57 58 int 59 main(int argc, char **argv) 60 { 61 const char *mnt, *stage; 62 const char *errstr; 63 64 if (argc != 3) 65 usage(); 66 67 mnt = argv[1]; 68 stage = argv[2]; 69 70 if (strcmp(stage, "locked") == 0) 71 check_locked(mnt); 72 else if (strcmp(stage, "unlocked") == 0) 73 check_unlocked(mnt); 74 else 75 usage(); 76 77 return (nfail > 0); 78 } 79 80 static void 81 check_locked(const char *mnt) 82 { 83 char path[PATH_MAX]; 84 85 EXPECT_OK(access(mnt, F_OK)); 86 EXPECT_ERRNO(access(mnt, R_OK), EACCES); 87 EXPECT_ERRNO(access(mnt, W_OK), EACCES); 88 EXPECT_ERRNO(access(mnt, X_OK), EACCES); 89 90 (void)snprintf(path, PATH_MAX, "%s/stdin", mnt); 91 EXPECT_ERRNO(mknod(path, S_IFCHR | 0700, makedev(22, 0)), EACCES); 92 93 EXPECT_ERRNO(chown(mnt, getuid(), -1), EPERM); 94 EXPECT_ERRNO(chmod(mnt, 0700), EPERM); 95 EXPECT_ERRNO(chflags(mnt, SF_ARCHIVED), EPERM); 96 EXPECT_ERRNO(utimes(mnt, NULL), EACCES); 97 } 98 99 static void 100 check_unlocked(const char *mnt) 101 { 102 if (chdir(mnt) == -1) 103 err(1, "chdir"); 104 105 check_unlocked_vroot(); 106 check_unlocked_subdir(); 107 } 108 109 static void 110 check_unlocked_vroot(void) 111 { 112 int fd; 113 114 EXPECT_OK(access(".", R_OK | W_OK | X_OK)); 115 116 EXPECT_ERRNO(mknod("stdin", S_IFCHR | 0700, makedev(22, 0)), EPERM); 117 118 EXPECT_ERRNO(chown(".", 0, -1), EPERM); 119 EXPECT_OK(chmod(".", 0700)); 120 EXPECT_ERRNO(chflags(".", SF_ARCHIVED), EPERM); 121 EXPECT_OK(utimes(".", NULL)); 122 } 123 124 static void 125 check_unlocked_subdir(void) 126 { 127 if (mkdir("sub", 0000) == -1) 128 err(1, "mkdir"); 129 130 EXPECT_OK(access("sub", R_OK | W_OK | X_OK)); 131 132 EXPECT_OK(mknod("sub/stdin", S_IFCHR | 0700, makedev(22, 0))); 133 134 EXPECT_OK(chown("sub", 0, -1)); 135 EXPECT_OK(chmod("sub", 0000)); 136 EXPECT_OK(chflags("sub", SF_ARCHIVED)); 137 EXPECT_OK(utimes("sub", NULL)); 138 139 EXPECT_OK(chmod("sub", S_ISVTX | 0700)); 140 EXPECT_OK(chown("sub/stdin", 0, -1)); 141 EXPECT_OK(rename("sub/stdin", "sub/stdin2")); 142 EXPECT_OK(unlink("sub/stdin2")); 143 } 144 145 __dead static void 146 usage(void) 147 { 148 (void)fprintf(stderr, "usage: %s mnt stage\n", getprogname()); 149 exit(1); 150 } 151