1 // Copyright 2012 Google Inc. 2 // All rights reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions are 6 // met: 7 // 8 // * Redistributions of source code must retain the above copyright 9 // notice, this list of conditions and the following disclaimer. 10 // * Redistributions in binary form must reproduce the above copyright 11 // notice, this list of conditions and the following disclaimer in the 12 // documentation and/or other materials provided with the distribution. 13 // * Neither the name of Google Inc. nor the names of its contributors 14 // may be used to endorse or promote products derived from this software 15 // without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 #if defined(HAVE_CONFIG_H) 30 # include "config.h" 31 #endif 32 33 #include "run.h" 34 35 #include <sys/resource.h> 36 #include <sys/stat.h> 37 #include <sys/wait.h> 38 39 #include <err.h> 40 #include <errno.h> 41 #include <pwd.h> 42 #include <signal.h> 43 #include <stdbool.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <unistd.h> 47 48 #include <atf-c.h> 49 50 #include "defs.h" 51 #include "env.h" 52 #include "error.h" 53 #include "fs.h" 54 55 56 /// Evalutes an expression and ensures it does not return an error. 57 /// 58 /// \param expr A expression that must evaluate to kyua_error_t. 59 #define RE(expr) ATF_REQUIRE(!kyua_error_is_set(expr)) 60 61 62 static void check_env(const void* KYUA_DEFS_UNUSED_PARAM(cookie)) 63 KYUA_DEFS_NORETURN; 64 65 66 /// Subprocess that validates the cleanliness of the environment. 67 /// 68 /// \param unused_cookie NULL. 69 /// 70 /// \post Exits with success if the environment is clean; failure otherwise. 71 static void 72 check_env(const void* KYUA_DEFS_UNUSED_PARAM(cookie)) 73 { 74 bool failed = false; 75 76 const char* empty[] = { "LANG", "LC_ALL", "LC_COLLATE", "LC_CTYPE", 77 "LC_MESSAGES", "LC_MONETARY", "LC_NUMERIC", 78 "LC_TIME", NULL }; 79 const char** iter; 80 for (iter = empty; *iter != NULL; ++iter) { 81 if (getenv(*iter) != NULL) { 82 failed = true; 83 printf("%s was not unset\n", *iter); 84 } 85 } 86 87 if (strcmp(getenv("HOME"), ".") != 0) { 88 failed = true; 89 printf("HOME was not set to .\n"); 90 } 91 if (strcmp(getenv("TZ"), "UTC") != 0) { 92 failed = true; 93 printf("TZ was not set to UTC\n"); 94 } 95 if (strcmp(getenv("LEAVE_ME_ALONE"), "kill-some-day") != 0) { 96 failed = true; 97 printf("LEAVE_ME_ALONE was modified while it should not have been\n"); 98 } 99 100 exit(failed ? EXIT_FAILURE : EXIT_SUCCESS); 101 } 102 103 104 static void check_process_group(const void* KYUA_DEFS_UNUSED_PARAM(cookie)) 105 KYUA_DEFS_NORETURN; 106 107 108 /// Subprocess that validates that it has become the leader of a process group. 109 /// 110 /// \param unused_cookie NULL. 111 /// 112 /// \post Exits with success if the process lives in its own process group; 113 /// failure otherwise. 114 static void 115 check_process_group(const void* KYUA_DEFS_UNUSED_PARAM(cookie)) 116 { 117 exit(getpgid(getpid()) == getpid() ? EXIT_SUCCESS : EXIT_FAILURE); 118 } 119 120 121 static void check_signals(const void* KYUA_DEFS_UNUSED_PARAM(cookie)) 122 KYUA_DEFS_NORETURN; 123 124 125 /// Subprocess that validates that signals have been reset to their defaults. 126 /// 127 /// \param unused_cookie NULL. 128 /// 129 /// \post Exits with success if the process has its signals reset to their 130 /// default values; failure otherwise. 131 static void 132 check_signals(const void* KYUA_DEFS_UNUSED_PARAM(cookie)) 133 { 134 int signo; 135 for (signo = 1; signo <= LAST_SIGNO; signo++) { 136 if (signo == SIGKILL || signo == SIGSTOP) { 137 // Don't attempt to check immutable signals, as this results in 138 // an unconditional error in some systems. E.g. Mac OS X 10.8 139 // reports 'Invalid argument' when querying SIGKILL. 140 continue; 141 } 142 143 struct sigaction old_sa; 144 if (sigaction(signo, NULL, &old_sa) == -1) { 145 err(EXIT_FAILURE, "Failed to query signal information for %d", 146 signo); 147 } 148 if (old_sa.sa_handler != SIG_DFL) { 149 errx(EXIT_FAILURE, "Signal %d not reset to its default handler", 150 signo); 151 } 152 printf("Signal %d has its default handler set\n", signo); 153 } 154 155 exit(EXIT_SUCCESS); 156 } 157 158 159 static void check_umask(const void* KYUA_DEFS_UNUSED_PARAM(cookie)) 160 KYUA_DEFS_NORETURN; 161 162 163 /// Subprocess that validates that the umask has been reset. 164 /// 165 /// \param unused_cookie NULL. 166 /// 167 /// \post Exits with success if the umask matches the expected value; failure 168 /// otherwise. 169 static void 170 check_umask(const void* KYUA_DEFS_UNUSED_PARAM(cookie)) 171 { 172 exit(umask(0) == 0022 ? EXIT_SUCCESS : EXIT_FAILURE); 173 } 174 175 176 static void check_work_directory(const void* cookie) KYUA_DEFS_NORETURN; 177 178 179 /// Subprocess that validates that the umask has been reset. 180 /// 181 /// \param cookie The name of a file to expect in the current directory. 182 /// 183 /// \post Exits with success if the umask matches the expected value; failure 184 /// otherwise. 185 static void 186 check_work_directory(const void* cookie) 187 { 188 const char* exp_file = (const char*)cookie; 189 exit(atf_utils_file_exists(exp_file) ? EXIT_SUCCESS : EXIT_FAILURE); 190 } 191 192 193 static void check_uid_not_root(const void* KYUA_DEFS_UNUSED_PARAM(cookie)) 194 KYUA_DEFS_NORETURN; 195 196 197 /// Subprocess that validates that the UID is not root. 198 /// 199 /// \param unused_cookie NULL. 200 /// 201 /// \post Exits with success if the UID is not root; failure otherwise. 202 static void 203 check_uid_not_root(const void* KYUA_DEFS_UNUSED_PARAM(cookie)) 204 { 205 exit(getuid() != 0 ? EXIT_SUCCESS : EXIT_FAILURE); 206 } 207 208 209 static void check_gid_not_root(const void* KYUA_DEFS_UNUSED_PARAM(cookie)) 210 KYUA_DEFS_NORETURN; 211 212 213 /// Subprocess that validates that the GID is not root. 214 /// 215 /// \param unused_cookie NULL. 216 /// 217 /// \post Exits with success if the GID is not root; failure otherwise. 218 static void 219 check_gid_not_root(const void* KYUA_DEFS_UNUSED_PARAM(cookie)) 220 { 221 exit(getgid() != 0 ? EXIT_SUCCESS : EXIT_FAILURE); 222 } 223 224 225 static void check_not_root(const void* KYUA_DEFS_UNUSED_PARAM(cookie)) 226 KYUA_DEFS_NORETURN; 227 228 229 /// Subprocess that validates that the UID and GID are not root. 230 /// 231 /// \param unused_cookie NULL. 232 /// 233 /// \post Exits with success if the UID and GID are not root; failure otherwise. 234 static void 235 check_not_root(const void* KYUA_DEFS_UNUSED_PARAM(cookie)) 236 { 237 exit(getuid() != 0 && getgid() != 0 ? EXIT_SUCCESS : EXIT_FAILURE); 238 } 239 240 241 /// Uses kyua_fork, kyua_exec and kyua_wait to execute a subprocess. 242 /// 243 /// \param program Path to the program to run. 244 /// \param args Arguments to the program. 245 /// \param [out] exitstatus The exit status of the subprocess, if it exits 246 /// successfully without timing out nor receiving a signal. 247 /// 248 /// \return Returns the error code of kyua_run_wait (which should have the 249 /// error representation of the exec call in the subprocess). 250 static kyua_error_t 251 exec_check(const char* program, const char* const* args, int* exitstatus) 252 { 253 kyua_run_params_t run_params; 254 kyua_run_params_init(&run_params); 255 256 pid_t pid; 257 kyua_error_t error = kyua_run_fork(&run_params, &pid); 258 if (!kyua_error_is_set(error) && pid == 0) 259 kyua_run_exec(program, args); 260 ATF_REQUIRE(!kyua_error_is_set(error)); 261 int status; bool timed_out; 262 error = kyua_run_wait(pid, &status, &timed_out); 263 if (!kyua_error_is_set(error)) { 264 ATF_REQUIRE(!timed_out); 265 ATF_REQUIRE_MSG(WIFEXITED(status), 266 "Subprocess expected to exit successfully"); 267 *exitstatus = WEXITSTATUS(status); 268 } 269 return error; 270 } 271 272 273 /// Uses kyua_fork and kyua_wait to spawn a subprocess. 274 /// 275 /// \param run_params The parameters to configure the subprocess. Can be NULL 276 /// to indicate to use the default set of parameters. 277 /// \param hook Any of the check_* functions provided in this module. 278 /// \param cookie The data to pass to the hook. 279 /// 280 /// \return True if the subprocess exits successfully; false otherwise. 281 static bool 282 fork_check(const kyua_run_params_t* run_params, 283 void (*hook)(const void*), const void* cookie) 284 { 285 kyua_run_params_t default_run_params; 286 if (run_params == NULL) { 287 kyua_run_params_init(&default_run_params); 288 run_params = &default_run_params; 289 } 290 291 pid_t pid; 292 kyua_error_t error = kyua_run_fork(run_params, &pid); 293 if (!kyua_error_is_set(error) && pid == 0) 294 hook(cookie); 295 ATF_REQUIRE(!kyua_error_is_set(error)); 296 int status; bool timed_out; 297 error = kyua_run_wait(pid, &status, &timed_out); 298 if (kyua_error_is_set(error)) 299 atf_tc_fail("wait failed; unexpected problem during exec?"); 300 ATF_REQUIRE(!timed_out); 301 return WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS; 302 } 303 304 305 ATF_TC_WITHOUT_HEAD(run_params_init__defaults); 306 ATF_TC_BODY(run_params_init__defaults, tc) 307 { 308 kyua_run_params_t run_params; 309 kyua_run_params_init(&run_params); 310 311 ATF_REQUIRE_EQ(60, run_params.timeout_seconds); 312 ATF_REQUIRE_EQ(getuid(), run_params.unprivileged_user); 313 ATF_REQUIRE_EQ(getgid(), run_params.unprivileged_group); 314 ATF_REQUIRE_STREQ(".", run_params.work_directory); 315 } 316 317 318 ATF_TC_WITHOUT_HEAD(fork_exec_wait__ok); 319 ATF_TC_BODY(fork_exec_wait__ok, tc) 320 { 321 const char* const args[] = {"sh", "-c", "exit 42", NULL}; 322 int exitstatus = -1; // Shut up GCC warning. 323 const kyua_error_t error = exec_check("/bin/sh", args, &exitstatus); 324 ATF_REQUIRE(!kyua_error_is_set(error)); 325 ATF_REQUIRE_EQ(42, exitstatus); 326 } 327 328 329 ATF_TC(fork_exec_wait__eacces); 330 ATF_TC_HEAD(fork_exec_wait__eacces, tc) 331 { 332 atf_tc_set_md_var(tc, "require.user", "unprivileged"); 333 } 334 ATF_TC_BODY(fork_exec_wait__eacces, tc) 335 { 336 ATF_REQUIRE(mkdir("dir", 0000) != -1); 337 338 const char* const args[] = {"foo", NULL}; 339 int unused_exitstatus; 340 const kyua_error_t error = exec_check("./dir/foo", args, 341 &unused_exitstatus); 342 ATF_REQUIRE(kyua_error_is_set(error)); 343 ATF_REQUIRE(kyua_error_is_type(error, "libc")); 344 ATF_REQUIRE_EQ(EACCES, kyua_libc_error_errno(error)); 345 } 346 347 348 ATF_TC_WITHOUT_HEAD(fork_exec_wait__enoent); 349 ATF_TC_BODY(fork_exec_wait__enoent, tc) 350 { 351 const char* const args[] = {"foo", NULL}; 352 int unused_exitstatus; 353 const kyua_error_t error = exec_check("./foo", args, &unused_exitstatus); 354 ATF_REQUIRE(kyua_error_is_set(error)); 355 ATF_REQUIRE(kyua_error_is_type(error, "libc")); 356 ATF_REQUIRE_EQ(ENOENT, kyua_libc_error_errno(error)); 357 } 358 359 360 ATF_TC_WITHOUT_HEAD(fork_wait__core_size); 361 ATF_TC_BODY(fork_wait__core_size, tc) 362 { 363 struct rlimit rl; 364 rl.rlim_cur = 0; 365 rl.rlim_max = RLIM_INFINITY; 366 if (setrlimit(RLIMIT_CORE, &rl) == -1) 367 atf_tc_skip("Failed to lower the core size limit"); 368 369 kyua_run_params_t run_params; 370 kyua_run_params_init(&run_params); 371 372 pid_t pid; 373 kyua_error_t error = kyua_run_fork(&run_params, &pid); 374 if (!kyua_error_is_set(error) && pid == 0) 375 abort(); 376 377 ATF_REQUIRE(!kyua_error_is_set(error)); 378 int status; bool timed_out; 379 error = kyua_run_wait(pid, &status, &timed_out); 380 if (kyua_error_is_set(error)) 381 atf_tc_fail("wait failed; unexpected problem during exec?"); 382 383 ATF_REQUIRE(!timed_out); 384 ATF_REQUIRE(WIFSIGNALED(status)); 385 ATF_REQUIRE_MSG(WCOREDUMP(status), "Core not dumped as expected"); 386 } 387 388 389 ATF_TC_WITHOUT_HEAD(fork_wait__env); 390 ATF_TC_BODY(fork_wait__env, tc) 391 { 392 kyua_env_set("HOME", "/non-existent/directory"); 393 kyua_env_set("LANG", "C"); 394 kyua_env_set("LC_ALL", "C"); 395 kyua_env_set("LC_COLLATE", "C"); 396 kyua_env_set("LC_CTYPE", "C"); 397 kyua_env_set("LC_MESSAGES", "C"); 398 kyua_env_set("LC_MONETARY", "C"); 399 kyua_env_set("LC_NUMERIC", "C"); 400 kyua_env_set("LC_TIME", "C"); 401 kyua_env_set("LEAVE_ME_ALONE", "kill-some-day"); 402 kyua_env_set("TZ", "EST+5"); 403 404 ATF_REQUIRE_MSG(fork_check(NULL, check_env, NULL), 405 "Unclean environment in subprocess"); 406 } 407 408 409 ATF_TC_WITHOUT_HEAD(fork_wait__process_group); 410 ATF_TC_BODY(fork_wait__process_group, tc) 411 { 412 ATF_REQUIRE_MSG(fork_check(NULL, check_process_group, NULL), 413 "Subprocess not in its own process group"); 414 } 415 416 417 ATF_TC_WITHOUT_HEAD(fork_wait__signals); 418 ATF_TC_BODY(fork_wait__signals, tc) 419 { 420 ATF_REQUIRE_MSG(LAST_SIGNO > 10, "LAST_SIGNO as detected by configure is " 421 "suspiciously low"); 422 423 int signo; 424 for (signo = 1; signo <= LAST_SIGNO; signo++) { 425 if (signo == SIGKILL || signo == SIGSTOP) { 426 // Ignore immutable signals. 427 continue; 428 } 429 if (signo == SIGCHLD) { 430 // If we were to reset SIGCHLD to SIG_IGN (which is different than 431 // not touching the signal at all, leaving it at its default value), 432 // our child process will not become a zombie and the call to 433 // kyua_run_wait will fail. Avoid this. 434 continue; 435 } 436 437 struct sigaction sa; 438 sa.sa_handler = SIG_IGN; 439 sigemptyset(&sa.sa_mask); 440 sa.sa_flags = 0; 441 printf("Ignoring signal %d\n", signo); 442 ATF_REQUIRE(sigaction(signo, &sa, NULL) != -1); 443 } 444 445 ATF_REQUIRE_MSG(fork_check(NULL, check_signals, NULL), 446 "Signals not reset to their default state"); 447 } 448 449 450 ATF_TC_WITHOUT_HEAD(fork_wait__timeout); 451 ATF_TC_BODY(fork_wait__timeout, tc) 452 { 453 kyua_run_params_t run_params; 454 kyua_run_params_init(&run_params); 455 run_params.timeout_seconds = 1; 456 457 pid_t pid; 458 kyua_error_t error = kyua_run_fork(&run_params, &pid); 459 if (!kyua_error_is_set(error) && pid == 0) { 460 sigset_t mask; 461 sigemptyset(&mask); 462 for (;;) 463 sigsuspend(&mask); 464 } 465 ATF_REQUIRE(!kyua_error_is_set(error)); 466 int status; bool timed_out; 467 kyua_run_wait(pid, &status, &timed_out); 468 ATF_REQUIRE(timed_out); 469 ATF_REQUIRE(WIFSIGNALED(status)); 470 ATF_REQUIRE_EQ(SIGKILL, WTERMSIG(status)); 471 } 472 473 474 ATF_TC_WITHOUT_HEAD(fork_wait__umask); 475 ATF_TC_BODY(fork_wait__umask, tc) 476 { 477 (void)umask(0222); 478 ATF_REQUIRE_MSG(fork_check(NULL, check_umask, NULL), 479 "Subprocess does not have the predetermined 0022 umask"); 480 } 481 482 483 ATF_TC(fork_wait__unprivileged_user); 484 ATF_TC_HEAD(fork_wait__unprivileged_user, tc) 485 { 486 atf_tc_set_md_var(tc, "require.config", "unprivileged-user"); 487 atf_tc_set_md_var(tc, "require.user", "root"); 488 } 489 ATF_TC_BODY(fork_wait__unprivileged_user, tc) 490 { 491 const struct passwd* pw = getpwnam(atf_tc_get_config_var( 492 tc, "unprivileged-user")); 493 ATF_REQUIRE_MSG(pw != NULL, "Cannot find unprivileged user"); 494 495 kyua_run_params_t run_params; 496 kyua_run_params_init(&run_params); 497 run_params.unprivileged_user = pw->pw_uid; 498 499 ATF_REQUIRE_MSG(fork_check(&run_params, check_uid_not_root, NULL), 500 "Subprocess is still running with UID set to root"); 501 } 502 503 504 ATF_TC(fork_wait__unprivileged_group); 505 ATF_TC_HEAD(fork_wait__unprivileged_group, tc) 506 { 507 atf_tc_set_md_var(tc, "require.config", "unprivileged-user"); 508 atf_tc_set_md_var(tc, "require.user", "root"); 509 } 510 ATF_TC_BODY(fork_wait__unprivileged_group, tc) 511 { 512 const struct passwd* pw = getpwnam(atf_tc_get_config_var( 513 tc, "unprivileged-user")); 514 ATF_REQUIRE_MSG(pw != NULL, "Cannot find unprivileged user"); 515 516 kyua_run_params_t run_params; 517 kyua_run_params_init(&run_params); 518 run_params.unprivileged_group = pw->pw_gid; 519 520 ATF_REQUIRE_MSG(fork_check(&run_params, check_gid_not_root, NULL), 521 "Subprocess is still running with GID set to root"); 522 } 523 524 525 ATF_TC(fork_wait__unprivileged_both); 526 ATF_TC_HEAD(fork_wait__unprivileged_both, tc) 527 { 528 atf_tc_set_md_var(tc, "require.config", "unprivileged-user"); 529 atf_tc_set_md_var(tc, "require.user", "root"); 530 } 531 ATF_TC_BODY(fork_wait__unprivileged_both, tc) 532 { 533 const struct passwd* pw = getpwnam(atf_tc_get_config_var( 534 tc, "unprivileged-user")); 535 ATF_REQUIRE_MSG(pw != NULL, "Cannot find unprivileged user"); 536 537 kyua_run_params_t run_params; 538 kyua_run_params_init(&run_params); 539 run_params.unprivileged_user = pw->pw_uid; 540 run_params.unprivileged_group = pw->pw_gid; 541 542 ATF_REQUIRE_MSG(fork_check(&run_params, check_not_root, NULL), 543 "Subprocess is still running with root privileges"); 544 } 545 546 547 ATF_TC_WITHOUT_HEAD(fork_wait__work_directory); 548 ATF_TC_BODY(fork_wait__work_directory, tc) 549 { 550 ATF_REQUIRE(mkdir("the-work-directory", 0755) != -1); 551 atf_utils_create_file("the-work-directory/data-file", "%s", ""); 552 553 kyua_run_params_t run_params; 554 kyua_run_params_init(&run_params); 555 run_params.work_directory = "./the-work-directory"; 556 ATF_REQUIRE_MSG(fork_check(&run_params, check_work_directory, "data-file"), 557 "Subprocess not in its own process group"); 558 } 559 560 561 ATF_TC_WITHOUT_HEAD(work_directory__builtin_tmpdir); 562 ATF_TC_BODY(work_directory__builtin_tmpdir, tc) 563 { 564 char* tmpdir; 565 RE(kyua_fs_make_absolute("worktest", &tmpdir)); 566 ATF_REQUIRE(mkdir(tmpdir, 0755) != -1); 567 RE(kyua_env_unset("TMPDIR")); 568 kyua_run_tmpdir = tmpdir; 569 570 char* work_directory; 571 RE(kyua_run_work_directory_enter("template.XXXXXX", getuid(), getgid(), 572 &work_directory)); 573 574 { 575 char* template_test; 576 RE(kyua_fs_concat(&template_test, atf_tc_get_config_var(tc, "srcdir"), 577 "worktest", "template.XXXXXX", NULL)); 578 ATF_REQUIRE(access(template_test, X_OK) == -1); 579 free(template_test); 580 } 581 582 ATF_REQUIRE(access(work_directory, X_OK) != -1); 583 584 ATF_REQUIRE(rmdir(tmpdir) == -1); // Not yet empty. 585 RE(kyua_run_work_directory_leave(&work_directory)); 586 ATF_REQUIRE(rmdir(tmpdir) != -1); 587 free(tmpdir); 588 } 589 590 591 ATF_TC_WITHOUT_HEAD(work_directory__env_tmpdir); 592 ATF_TC_BODY(work_directory__env_tmpdir, tc) 593 { 594 char* tmpdir; 595 RE(kyua_fs_make_absolute("worktest", &tmpdir)); 596 ATF_REQUIRE(mkdir(tmpdir, 0755) != -1); 597 RE(kyua_env_set("TMPDIR", tmpdir)); 598 599 char* work_directory; 600 RE(kyua_run_work_directory_enter("template.XXXXXX", getuid(), getgid(), 601 &work_directory)); 602 603 { 604 char* template_test; 605 RE(kyua_fs_concat(&template_test, atf_tc_get_config_var(tc, "srcdir"), 606 "worktest", "template.XXXXXX", NULL)); 607 ATF_REQUIRE(access(template_test, X_OK) == -1); 608 free(template_test); 609 } 610 611 ATF_REQUIRE(access(work_directory, X_OK) != -1); 612 613 ATF_REQUIRE(rmdir(tmpdir) == -1); // Not yet empty. 614 RE(kyua_run_work_directory_leave(&work_directory)); 615 ATF_REQUIRE(rmdir(tmpdir) != -1); 616 free(tmpdir); 617 } 618 619 620 ATF_TC(work_directory__permissions); 621 ATF_TC_HEAD(work_directory__permissions, tc) 622 { 623 atf_tc_set_md_var(tc, "require.config", "unprivileged-user"); 624 atf_tc_set_md_var(tc, "require.user", "root"); 625 } 626 ATF_TC_BODY(work_directory__permissions, tc) 627 { 628 const struct passwd* pw = getpwnam(atf_tc_get_config_var( 629 tc, "unprivileged-user")); 630 631 printf("%d %d %d %d\n", getuid(), getgid(), pw->pw_uid, pw->pw_gid); 632 633 char* work_directory; 634 RE(kyua_run_work_directory_enter("template.XXXXXX", pw->pw_uid, pw->pw_gid, 635 &work_directory)); 636 637 struct stat sb; 638 ATF_REQUIRE(stat(work_directory, &sb) != -1); 639 ATF_REQUIRE_EQ(pw->pw_uid, sb.st_uid); 640 ATF_REQUIRE_EQ(pw->pw_gid, sb.st_gid); 641 642 RE(kyua_run_work_directory_leave(&work_directory)); 643 } 644 645 646 ATF_TC(work_directory__mkdtemp_error); 647 ATF_TC_HEAD(work_directory__mkdtemp_error, tc) 648 { 649 atf_tc_set_md_var(tc, "require.user", "unprivileged"); 650 } 651 ATF_TC_BODY(work_directory__mkdtemp_error, tc) 652 { 653 char* tmpdir; 654 RE(kyua_fs_make_absolute("worktest", &tmpdir)); 655 ATF_REQUIRE(mkdir(tmpdir, 0555) != -1); 656 RE(kyua_env_set("TMPDIR", tmpdir)); 657 658 char* work_directory; 659 const kyua_error_t error = kyua_run_work_directory_enter( 660 "template.XXXXXX", getuid(), getgid(), &work_directory); 661 ATF_REQUIRE(kyua_error_is_set(error)); 662 ATF_REQUIRE(kyua_error_is_type(error, "libc")); 663 ATF_REQUIRE_EQ(EACCES, kyua_libc_error_errno(error)); 664 kyua_error_free(error); 665 666 ATF_REQUIRE(rmdir(tmpdir) != -1); // Empty; subdirectory not created. 667 free(tmpdir); 668 } 669 670 671 ATF_TC(work_directory__permissions_error); 672 ATF_TC_HEAD(work_directory__permissions_error, tc) 673 { 674 atf_tc_set_md_var(tc, "require.user", "unprivileged"); 675 } 676 ATF_TC_BODY(work_directory__permissions_error, tc) 677 { 678 char* tmpdir; 679 RE(kyua_fs_make_absolute("worktest", &tmpdir)); 680 ATF_REQUIRE(mkdir(tmpdir, 0755) != -1); 681 RE(kyua_env_set("TMPDIR", tmpdir)); 682 683 char* work_directory; 684 const kyua_error_t error = kyua_run_work_directory_enter( 685 "template.XXXXXX", getuid() + 1, getgid(), &work_directory); 686 ATF_REQUIRE(kyua_error_is_set(error)); 687 ATF_REQUIRE(kyua_error_is_type(error, "libc")); 688 ATF_REQUIRE_EQ(EPERM, kyua_libc_error_errno(error)); 689 kyua_error_free(error); 690 691 ATF_REQUIRE(rmdir(tmpdir) != -1); // Empty; subdirectory not created. 692 free(tmpdir); 693 } 694 695 696 /// Performs a signal delivery test to the work directory handling code. 697 /// 698 /// \param signo The signal to deliver. 699 static void 700 work_directory_signal_check(const int signo) 701 { 702 char* tmpdir; 703 RE(kyua_fs_make_absolute("worktest", &tmpdir)); 704 ATF_REQUIRE(mkdir(tmpdir, 0755) != -1); 705 RE(kyua_env_set("TMPDIR", tmpdir)); 706 707 char* work_directory; 708 RE(kyua_run_work_directory_enter("template.XXXXXX", getuid(), getgid(), 709 &work_directory)); 710 711 kyua_run_params_t run_params; 712 kyua_run_params_init(&run_params); 713 run_params.work_directory = work_directory; 714 715 pid_t pid; 716 RE(kyua_run_fork(&run_params, &pid)); 717 if (pid == 0) { 718 sleep(run_params.timeout_seconds * 2); 719 abort(); 720 } 721 722 // This should cause the handled installed by the work_directory management 723 // code to terminate the subprocess so that we get a chance to run the 724 // cleanup code ourselves. 725 kill(getpid(), signo); 726 727 int status; bool timed_out; 728 RE(kyua_run_wait(pid, &status, &timed_out)); 729 ATF_REQUIRE(!timed_out); 730 ATF_REQUIRE(WIFSIGNALED(status)); 731 ATF_REQUIRE_EQ(SIGKILL, WTERMSIG(status)); 732 733 ATF_REQUIRE(rmdir(tmpdir) == -1); // Not yet empty. 734 RE(kyua_run_work_directory_leave(&work_directory)); 735 ATF_REQUIRE(rmdir(tmpdir) != -1); 736 free(tmpdir); 737 } 738 739 740 ATF_TC_WITHOUT_HEAD(work_directory__sighup); 741 ATF_TC_BODY(work_directory__sighup, tc) 742 { 743 work_directory_signal_check(SIGHUP); 744 } 745 746 747 ATF_TC_WITHOUT_HEAD(work_directory__sigint); 748 ATF_TC_BODY(work_directory__sigint, tc) 749 { 750 work_directory_signal_check(SIGINT); 751 } 752 753 754 ATF_TC_WITHOUT_HEAD(work_directory__sigterm); 755 ATF_TC_BODY(work_directory__sigterm, tc) 756 { 757 work_directory_signal_check(SIGTERM); 758 } 759 760 761 ATF_TP_ADD_TCS(tp) 762 { 763 ATF_TP_ADD_TC(tp, run_params_init__defaults); 764 765 ATF_TP_ADD_TC(tp, fork_exec_wait__ok); 766 ATF_TP_ADD_TC(tp, fork_exec_wait__eacces); 767 ATF_TP_ADD_TC(tp, fork_exec_wait__enoent); 768 769 ATF_TP_ADD_TC(tp, fork_wait__core_size); 770 ATF_TP_ADD_TC(tp, fork_wait__env); 771 ATF_TP_ADD_TC(tp, fork_wait__process_group); 772 ATF_TP_ADD_TC(tp, fork_wait__signals); 773 ATF_TP_ADD_TC(tp, fork_wait__timeout); 774 ATF_TP_ADD_TC(tp, fork_wait__umask); 775 ATF_TP_ADD_TC(tp, fork_wait__unprivileged_user); 776 ATF_TP_ADD_TC(tp, fork_wait__unprivileged_group); 777 ATF_TP_ADD_TC(tp, fork_wait__unprivileged_both); 778 ATF_TP_ADD_TC(tp, fork_wait__work_directory); 779 780 ATF_TP_ADD_TC(tp, work_directory__builtin_tmpdir); 781 ATF_TP_ADD_TC(tp, work_directory__env_tmpdir); 782 ATF_TP_ADD_TC(tp, work_directory__permissions); 783 ATF_TP_ADD_TC(tp, work_directory__permissions_error); 784 ATF_TP_ADD_TC(tp, work_directory__mkdtemp_error); 785 ATF_TP_ADD_TC(tp, work_directory__sighup); 786 ATF_TP_ADD_TC(tp, work_directory__sigint); 787 ATF_TP_ADD_TC(tp, work_directory__sigterm); 788 789 return atf_no_error(); 790 } 791