test_sandbox.c (12318B)
1 /* Copyright (c) 2021, The Tor Project, Inc. */ 2 /* See LICENSE for licensing information */ 3 4 #ifndef _LARGEFILE64_SOURCE 5 /** 6 * Temporarily required for O_LARGEFILE flag. Needs to be removed 7 * with the libevent fix. 8 */ 9 #define _LARGEFILE64_SOURCE 10 #endif /* !defined(_LARGEFILE64_SOURCE) */ 11 12 #include "orconfig.h" 13 14 #include "lib/sandbox/sandbox.h" 15 #include "lib/crypt_ops/crypto_rand.h" 16 #include "ext/equix/include/equix.h" 17 18 #ifdef USE_LIBSECCOMP 19 20 #include <dirent.h> 21 #ifdef HAVE_FCNTL_H 22 #include <fcntl.h> 23 #endif 24 #ifdef HAVE_SYS_STAT_H 25 #include <sys/stat.h> 26 #endif 27 #ifdef HAVE_UNISTD_H 28 #include <unistd.h> 29 #endif 30 31 #include "core/or/or.h" 32 33 #include "test/test.h" 34 #include "test/log_test_helpers.h" 35 36 typedef struct { 37 sandbox_cfg_t *cfg; 38 39 char *file_ops_allowed; 40 char *file_ops_blocked; 41 42 char *file_rename_target_allowed; 43 44 char *dir_ops_allowed; 45 char *dir_ops_blocked; 46 } sandbox_data_t; 47 48 /* All tests are skipped when coverage support is enabled (see further below) 49 * as the sandbox interferes with the use of gcov. Prevent a compiler warning 50 * by omitting these definitions in that case. */ 51 #ifndef ENABLE_COVERAGE 52 static void * 53 setup_sandbox(const struct testcase_t *testcase) 54 { 55 sandbox_data_t *data = tor_malloc_zero(sizeof(*data)); 56 57 (void)testcase; 58 59 /* Establish common file and directory names within the test suite's 60 * temporary directory. */ 61 data->file_ops_allowed = tor_strdup(get_fname("file_ops_allowed")); 62 data->file_ops_blocked = tor_strdup(get_fname("file_ops_blocked")); 63 64 data->file_rename_target_allowed = 65 tor_strdup(get_fname("file_rename_target_allowed")); 66 67 data->dir_ops_allowed = tor_strdup(get_fname("dir_ops_allowed")); 68 data->dir_ops_blocked = tor_strdup(get_fname("dir_ops_blocked")); 69 70 /* Create the corresponding filesystem objects. */ 71 creat(data->file_ops_allowed, S_IRWXU); 72 creat(data->file_ops_blocked, S_IRWXU); 73 mkdir(data->dir_ops_allowed, S_IRWXU); 74 mkdir(data->dir_ops_blocked, S_IRWXU); 75 76 /* Create the sandbox configuration. */ 77 data->cfg = sandbox_cfg_new(); 78 79 sandbox_cfg_allow_open_filename(&data->cfg, 80 tor_strdup(data->file_ops_allowed)); 81 sandbox_cfg_allow_open_filename(&data->cfg, 82 tor_strdup(data->dir_ops_allowed)); 83 84 sandbox_cfg_allow_chmod_filename(&data->cfg, 85 tor_strdup(data->file_ops_allowed)); 86 sandbox_cfg_allow_chmod_filename(&data->cfg, 87 tor_strdup(data->dir_ops_allowed)); 88 sandbox_cfg_allow_chown_filename(&data->cfg, 89 tor_strdup(data->file_ops_allowed)); 90 sandbox_cfg_allow_chown_filename(&data->cfg, 91 tor_strdup(data->dir_ops_allowed)); 92 93 sandbox_cfg_allow_rename(&data->cfg, tor_strdup(data->file_ops_allowed), 94 tor_strdup(data->file_rename_target_allowed)); 95 96 sandbox_cfg_allow_openat_filename(&data->cfg, 97 tor_strdup(data->dir_ops_allowed)); 98 99 sandbox_cfg_allow_opendir_dirname(&data->cfg, 100 tor_strdup(data->dir_ops_allowed)); 101 102 sandbox_cfg_allow_stat_filename(&data->cfg, 103 tor_strdup(data->file_ops_allowed)); 104 sandbox_cfg_allow_stat_filename(&data->cfg, 105 tor_strdup(data->dir_ops_allowed)); 106 107 /* Activate the sandbox, which will remain in effect until the process 108 * terminates. */ 109 sandbox_init(data->cfg); 110 111 return data; 112 } 113 114 static int 115 cleanup_sandbox(const struct testcase_t *testcase, void *data_) 116 { 117 sandbox_data_t *data = data_; 118 119 (void)testcase; 120 121 tor_free(data->dir_ops_blocked); 122 tor_free(data->dir_ops_allowed); 123 tor_free(data->file_rename_target_allowed); 124 tor_free(data->file_ops_blocked); 125 tor_free(data->file_ops_allowed); 126 127 tor_free(data); 128 129 return 1; 130 } 131 132 static const struct testcase_setup_t sandboxed_testcase_setup = { 133 .setup_fn = setup_sandbox, 134 .cleanup_fn = cleanup_sandbox 135 }; 136 #endif /* !defined(ENABLE_COVERAGE) */ 137 138 static void 139 test_sandbox_is_active(void *ignored) 140 { 141 (void)ignored; 142 143 tt_assert(!sandbox_is_active()); 144 145 sandbox_init(sandbox_cfg_new()); 146 tt_assert(sandbox_is_active()); 147 148 done: 149 (void)0; 150 } 151 152 static void 153 test_sandbox_open_filename(void *arg) 154 { 155 sandbox_data_t *data = arg; 156 int fd, errsv; 157 158 fd = open(sandbox_intern_string(data->file_ops_allowed), O_RDONLY); 159 if (fd == -1) 160 tt_abort_perror("open"); 161 close(fd); 162 163 /* It might be nice to use sandbox_intern_string() in the line below as well 164 * (and likewise in the test cases that follow) but this would require 165 * capturing the warning message it logs, and the mechanism for doing so 166 * relies on system calls that are normally blocked by the sandbox and may 167 * vary across architectures. */ 168 fd = open(data->file_ops_blocked, O_RDONLY); 169 errsv = errno; 170 tt_int_op(fd, OP_EQ, -1); 171 tt_int_op(errsv, OP_EQ, EPERM); 172 173 done: 174 if (fd >= 0) 175 close(fd); 176 } 177 178 static void 179 test_sandbox_chmod_filename(void *arg) 180 { 181 sandbox_data_t *data = arg; 182 int rc, errsv; 183 184 if (chmod(sandbox_intern_string(data->file_ops_allowed), 185 S_IRUSR | S_IWUSR) != 0) 186 tt_abort_perror("chmod"); 187 188 rc = chmod(data->file_ops_blocked, S_IRUSR | S_IWUSR); 189 errsv = errno; 190 tt_int_op(rc, OP_EQ, -1); 191 tt_int_op(errsv, OP_EQ, EPERM); 192 193 done: 194 (void)0; 195 } 196 197 static void 198 test_sandbox_chown_filename(void *arg) 199 { 200 sandbox_data_t *data = arg; 201 int rc, errsv; 202 203 if (chown(sandbox_intern_string(data->file_ops_allowed), -1, -1) != 0) 204 tt_abort_perror("chown"); 205 206 rc = chown(data->file_ops_blocked, -1, -1); 207 errsv = errno; 208 tt_int_op(rc, OP_EQ, -1); 209 tt_int_op(errsv, OP_EQ, EPERM); 210 211 done: 212 (void)0; 213 } 214 215 static void 216 test_sandbox_rename_filename(void *arg) 217 { 218 sandbox_data_t *data = arg; 219 const char *fname_old = sandbox_intern_string(data->file_ops_allowed), 220 *fname_new = sandbox_intern_string(data->file_rename_target_allowed); 221 int rc, errsv; 222 223 if (rename(fname_old, fname_new) != 0) 224 tt_abort_perror("rename"); 225 226 rc = rename(fname_new, fname_old); 227 errsv = errno; 228 tt_int_op(rc, OP_EQ, -1); 229 tt_int_op(errsv, OP_EQ, EPERM); 230 231 done: 232 (void)0; 233 } 234 235 static void 236 test_sandbox_openat_filename(void *arg) 237 { 238 sandbox_data_t *data = arg; 239 int flags = O_RDONLY | O_NONBLOCK | O_LARGEFILE | O_DIRECTORY | O_CLOEXEC; 240 int fd, errsv; 241 242 fd = openat(AT_FDCWD, sandbox_intern_string(data->dir_ops_allowed), flags); 243 if (fd < 0) 244 tt_abort_perror("openat"); 245 close(fd); 246 247 fd = openat(AT_FDCWD, data->dir_ops_blocked, flags); 248 errsv = errno; 249 tt_int_op(fd, OP_EQ, -1); 250 tt_int_op(errsv, OP_EQ, EPERM); 251 252 done: 253 if (fd >= 0) 254 close(fd); 255 } 256 257 static void 258 test_sandbox_opendir_dirname(void *arg) 259 { 260 sandbox_data_t *data = arg; 261 DIR *dir; 262 int errsv; 263 264 dir = opendir(sandbox_intern_string(data->dir_ops_allowed)); 265 if (dir == NULL) 266 tt_abort_perror("opendir"); 267 closedir(dir); 268 269 dir = opendir(data->dir_ops_blocked); 270 errsv = errno; 271 tt_ptr_op(dir, OP_EQ, NULL); 272 tt_int_op(errsv, OP_EQ, EPERM); 273 274 done: 275 if (dir) 276 closedir(dir); 277 } 278 279 static void 280 test_sandbox_stat_filename(void *arg) 281 { 282 sandbox_data_t *data = arg; 283 struct stat st; 284 285 if (stat(sandbox_intern_string(data->file_ops_allowed), &st) != 0) 286 tt_abort_perror("stat"); 287 288 int rc = stat(data->file_ops_blocked, &st); 289 int errsv = errno; 290 tt_int_op(rc, OP_EQ, -1); 291 tt_int_op(errsv, OP_EQ, EPERM); 292 293 done: 294 (void)0; 295 } 296 297 /** This is a simplified subset of test_crypto_equix(), running one solve 298 * and one verify from inside the sandbox. The sandbox restricts mprotect, and 299 * hashx will experience a failure at runtime which this test case exercises. 300 * The result of the solve and verify should both still be correct, since we 301 * expect it to cleanly fall back on an interpreted implementation which has 302 * no operating system dependencies. */ 303 static void 304 test_sandbox_crypto_equix(void *arg) 305 { 306 (void)arg; 307 308 const char *challenge_literal = "abce"; 309 const size_t challenge_len = strlen(challenge_literal); 310 const size_t num_sols = 4; 311 static const equix_solution sols_expected[EQUIX_MAX_SOLS] = { 312 {{ 0x4fca, 0x72eb, 0x101f, 0xafab, 0x1add, 0x2d71, 0x75a3, 0xc978 }}, 313 {{ 0x17f1, 0x7aa6, 0x23e3, 0xab00, 0x7e2f, 0x917e, 0x16da, 0xda9e }}, 314 {{ 0x70ee, 0x7757, 0x8a54, 0xbd2b, 0x90e4, 0xe31e, 0x2085, 0xe47e }}, 315 {{ 0x62c5, 0x86d1, 0x5752, 0xe1f0, 0x12da, 0x8f33, 0x7336, 0xf161 }}, 316 }; 317 318 equix_solutions_buffer output; 319 equix_ctx *solve_ctx = NULL, *verify_ctx = NULL; 320 321 solve_ctx = equix_alloc(EQUIX_CTX_SOLVE | EQUIX_CTX_TRY_COMPILE); 322 tt_ptr_op(solve_ctx, OP_NE, NULL); 323 324 equix_result result; 325 memset(&output, 0xEE, sizeof output); 326 result = equix_solve(solve_ctx, challenge_literal, challenge_len, &output); 327 tt_int_op(result, OP_EQ, EQUIX_OK); 328 tt_int_op(output.count, OP_EQ, num_sols); 329 tt_int_op(output.flags, OP_EQ, 0); /* EQUIX_SOLVER_DID_USE_COMPILER unset */ 330 tt_mem_op(output.sols, OP_EQ, sols_expected, 331 num_sols * sizeof(equix_solution)); 332 333 verify_ctx = equix_alloc(EQUIX_CTX_VERIFY | EQUIX_CTX_TRY_COMPILE); 334 tt_ptr_op(verify_ctx, OP_NE, NULL); 335 336 /* Test one of the solutions randomly */ 337 const unsigned sol_i = crypto_rand_int(num_sols); 338 equix_solution *sol = &output.sols[sol_i]; 339 340 result = equix_verify(verify_ctx, challenge_literal, 341 challenge_len, sol); 342 tt_int_op(EQUIX_OK, OP_EQ, result); 343 344 done: 345 equix_free(solve_ctx); 346 equix_free(verify_ctx); 347 } 348 349 #define SANDBOX_TEST_SKIPPED(name) \ 350 { #name, test_sandbox_ ## name, TT_SKIP, NULL, NULL } 351 352 /* Skip all tests when coverage support is enabled, as the sandbox interferes 353 * with gcov and prevents it from producing any results. */ 354 #ifdef ENABLE_COVERAGE 355 #define SANDBOX_TEST(name, flags) SANDBOX_TEST_SKIPPED(name) 356 #define SANDBOX_TEST_IN_SANDBOX(name) SANDBOX_TEST_SKIPPED(name) 357 #else 358 #define SANDBOX_TEST(name, flags) \ 359 { #name, test_sandbox_ ## name, flags, NULL, NULL } 360 #define SANDBOX_TEST_IN_SANDBOX(name) \ 361 { #name, test_sandbox_ ## name, TT_FORK, &sandboxed_testcase_setup, NULL } 362 #endif /* defined(ENABLE_COVERAGE) */ 363 364 struct testcase_t sandbox_tests[] = { 365 SANDBOX_TEST(is_active, TT_FORK), 366 367 /* When Tor is built with fragile compiler-hardening the sandbox is usually 368 * unable to filter requests to open files or directories, as doing so would 369 * interfere with the address sanitizer as it retrieves information about the 370 * running process via the filesystem. Skip these tests in that case as the 371 * corresponding functions are likely to have no effect and this will cause the 372 * tests to fail. */ 373 #ifdef ENABLE_FRAGILE_HARDENING 374 SANDBOX_TEST_SKIPPED(open_filename), 375 SANDBOX_TEST_SKIPPED(openat_filename), 376 SANDBOX_TEST_SKIPPED(opendir_dirname), 377 #else 378 SANDBOX_TEST_IN_SANDBOX(open_filename), 379 SANDBOX_TEST_IN_SANDBOX(openat_filename), 380 #endif /* defined(ENABLE_FRAGILE_HARDENING) */ 381 382 /* Ok why... Quick answer is #40918. This has been failing on Debian SID 383 * making us unable to have nightly packages which is a problem as we have 384 * several relay operators using them and actively reporting us issues with 385 * them. This test fails due to the sandbox denying it. 386 * 387 * We are deprecating C-tor slowly and honestly, the Sandbox feature has 388 * always been a source of pain and unhappiness. Disable this as finding why, 389 * fixing it and hoping it doesn't come back will mostly be a waste of our 390 * time at this point. */ 391 SANDBOX_TEST_SKIPPED(opendir_dirname), 392 393 SANDBOX_TEST_IN_SANDBOX(chmod_filename), 394 SANDBOX_TEST_IN_SANDBOX(chown_filename), 395 SANDBOX_TEST_IN_SANDBOX(rename_filename), 396 397 /* Currently the sandbox is unable to filter stat() calls on systems where 398 * glibc implements this function using either of the legacy "stat" or "stat64" 399 * system calls, or (in glibc version 2.33 and later) either of the newer 400 * "newfstatat" or "statx" syscalls. 401 * 402 * Skip testing sandbox_cfg_allow_stat_filename() if it seems the likely the 403 * function will have no effect and the test will therefore not succeed. */ 404 #if !defined(__NR_stat) && !defined(__NR_stat64) && !defined(__NR_newfstatat) \ 405 && !(defined(__i386__) && defined(__NR_statx)) 406 SANDBOX_TEST_IN_SANDBOX(stat_filename), 407 #else 408 SANDBOX_TEST_SKIPPED(stat_filename), 409 #endif 410 411 SANDBOX_TEST_IN_SANDBOX(crypto_equix), 412 END_OF_TESTCASES 413 }; 414 415 #endif /* defined(USE_SECCOMP) */