1 /* $OpenBSD: main.c,v 1.8 2015/10/30 07:24:20 semarie Exp $ */ 2 /* 3 * Copyright (c) 2015 Sebastien Marie <semarie@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/mman.h> 19 #include <sys/resource.h> 20 #include <sys/socket.h> 21 #include <sys/stat.h> 22 #include <sys/time.h> 23 #include <sys/types.h> 24 #include <sys/wait.h> 25 26 #include <err.h> 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <limits.h> 30 #include <signal.h> 31 #include <stdarg.h> 32 #include <stdlib.h> 33 #include <stdio.h> 34 #include <string.h> 35 #include <time.h> 36 #include <unistd.h> 37 38 #include "manager.h" 39 40 void test_request_stdio(void); 41 void test_request_tty(void); 42 43 static void 44 test_nop() 45 { 46 /* nop */ 47 } 48 49 static void 50 test_inet() 51 { 52 int fd = socket(AF_INET, SOCK_STREAM, 0); 53 int saved_errno = errno; 54 close(fd); 55 errno = saved_errno ? saved_errno : errno; 56 } 57 58 static void 59 test_kill() 60 { 61 kill(0, SIGINT); 62 } 63 64 static void 65 open_close(const char *filename) 66 { 67 int fd; 68 int saved_errno; 69 70 errno = 0; 71 printf("\n open_close(\"%s\")", filename); 72 fd = open(filename, O_RDONLY); 73 saved_errno = errno; 74 printf(" fd=%d errno=%d", fd, errno); 75 if (fd != -1) 76 close(fd); 77 errno = saved_errno; 78 } 79 80 static void 81 test_wpaths() 82 { 83 /* absolute file */ 84 open_close("/etc/passwd"); 85 86 /* relative */ 87 open_close("generic"); 88 89 /* relative */ 90 open_close("../../../../../../../../../../../../../../../etc/passwd"); 91 92 /* ENOENT */ 93 open_close("/nonexistent"); 94 95 /* calling exit to flush stdout */ 96 printf("\n"); 97 exit(EXIT_SUCCESS); 98 } 99 100 static void 101 test_pledge() 102 { 103 const char *wpaths[] = { "/sbin", NULL }; 104 105 if (pledge("stdio rpath", wpaths) != 0) 106 _exit(errno); 107 } 108 109 static void 110 do_stat(const char *path) 111 { 112 char resolved[PATH_MAX]; 113 struct stat sb; 114 115 printf("\n stat(\"%s\"):", path); 116 117 /* call realpath(3) */ 118 errno = 0; 119 if (realpath(path, resolved) != NULL) 120 printf(" realpath=\"%s\"", resolved); 121 else 122 printf(" realpath=failed(%d)", errno); 123 124 /* call stat(2) */ 125 errno = 0; 126 if (stat(path, &sb) == 0) 127 printf(" uid=%d gid=%d mode=%04o", sb.st_uid, sb.st_gid, 128 sb.st_mode); 129 else 130 printf(" errno=%d", errno); 131 } 132 133 static void 134 test_stat() 135 { 136 /* in whitelisted path */ 137 do_stat("/usr/share/man/man8/afterboot.8"); 138 do_stat("/usr/share/man/man8/"); 139 do_stat("/usr/share/man"); 140 141 /* parent of whitelisted path */ 142 do_stat("/usr/share"); 143 do_stat("/usr"); 144 do_stat("/"); 145 146 /* outside whitelisted path */ 147 do_stat("/usr/bin/gzip"); 148 149 /* calling exit to flush stdout */ 150 printf("\n"); 151 exit(EXIT_SUCCESS); 152 } 153 154 static void 155 test_mmap() 156 { 157 int fd; 158 void * data; 159 160 if ((fd = open("/dev/zero", O_RDONLY, 0)) == -1) 161 _exit(errno); 162 163 data = mmap(NULL, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, 164 MAP_FILE|MAP_SHARED, fd, 0); 165 166 if (data == MAP_FAILED) 167 _exit(errno); 168 169 munmap(data, 4096); 170 close(fd); 171 } 172 173 static void 174 test_rpath() 175 { 176 int fd; 177 char data[512]; 178 179 if ((fd = open("/dev/zero", O_RDONLY, 0)) == -1) 180 _exit(errno); 181 182 if (read(fd, data, sizeof(data)) == -1) 183 _exit(errno); 184 185 close(fd); 186 } 187 188 static void 189 test_wpath() 190 { 191 int fd; 192 char data[] = { 0x01, 0x02, 0x03, 0x04, 0x05 }; 193 194 if ((fd = open("/dev/null", O_WRONLY, 0)) == -1) 195 _exit(errno); 196 197 if (write(fd, data, sizeof(data)) == -1) 198 _exit(errno); 199 200 close(fd); 201 } 202 203 static void 204 test_cpath() 205 { 206 const char filename[] = "/tmp/generic-test-cpath"; 207 208 if (mkdir(filename, S_IRWXU) == -1) 209 _exit(errno); 210 211 if (rmdir(filename) == -1) 212 _exit(errno); 213 } 214 215 int 216 main(int argc, char *argv[]) 217 { 218 int ret = EXIT_SUCCESS; 219 220 if (argc != 1) 221 errx(1, "usage: %s", argv[0]); 222 223 /* 224 * testsuite 225 */ 226 227 /* _exit is always allowed, and nothing else under flags=0 */ 228 start_test(&ret, "", NULL, test_nop); 229 start_test(&ret, "", NULL, test_inet); 230 231 /* test coredump */ 232 start_test(&ret, "abort", NULL, test_inet); 233 234 /* inet under inet is ok (stdio is needed of close(2)) */ 235 start_test(&ret, "stdio", NULL, test_inet); 236 start_test(&ret, "inet", NULL, test_inet); 237 start_test(&ret, "stdio inet", NULL, test_inet); 238 239 /* kill under fattr is forbidden */ 240 start_test(&ret, "fattr", NULL, test_kill); 241 242 /* kill under stdio is allowed */ 243 start_test(&ret, "stdio", NULL, test_kill); 244 245 /* stdio for open(2) */ 246 start_test(&ret, "stdio rpath", NULL, test_rpath); 247 start_test(&ret, "stdio wpath", NULL, test_wpath); 248 start_test(&ret, "cpath", NULL, test_cpath); 249 250 /* 251 * test whitelist path 252 */ 253 start_test(&ret, "stdio rpath", NULL, test_wpaths); 254 start_test1(&ret, "stdio rpath", NULL, test_wpaths); 255 start_test1(&ret, "stdio rpath", "/", test_wpaths); 256 start_test1(&ret, "stdio rpath", "/etc", test_wpaths); 257 start_test1(&ret, "stdio rpath", "/etc/", test_wpaths); 258 start_test1(&ret, "stdio rpath", "/etc/passwd", test_wpaths); 259 // XXX start_test1(&ret, "stdio rpath", "/etc/passwd/", test_wpaths); 260 start_test1(&ret, "stdio rpath", "/bin", test_wpaths); 261 start_test1(&ret, "stdio rpath", "generic", test_wpaths); 262 start_test1(&ret, "stdio rpath", "", test_wpaths); 263 start_test1(&ret, "stdio rpath", ".", test_wpaths); 264 265 /* 266 * test pledge(2) arguments 267 */ 268 /* same request */ 269 start_test(&ret, "stdio rpath", NULL, test_pledge); 270 /* reduce request */ 271 start_test(&ret, "stdio rpath wpath", NULL, test_pledge); 272 /* reduce request (with same/other wpaths) */ 273 start_test1(&ret, "stdio rpath wpath", "/sbin", test_pledge); 274 start_test1(&ret, "stdio rpath wpath", "/", test_pledge); 275 /* add request */ 276 start_test(&ret, "stdio", NULL, test_pledge); 277 /* change request */ 278 start_test(&ret, "stdio unix", NULL, test_pledge); 279 280 /* test stat(2) */ 281 start_test1(&ret, "stdio rpath", "/usr/share/man", test_stat); 282 283 /* mmap */ 284 start_test1(&ret, "stdio rpath prot_exec", "/dev/zero", test_mmap); 285 start_test1(&ret, "stdio rpath", "/dev/zero", test_mmap); 286 287 /* stdio */ 288 start_test(&ret, NULL, NULL, test_request_stdio); 289 290 /* tty */ 291 start_test(&ret, NULL, NULL, test_request_tty); 292 293 return (ret); 294 } 295