Home | History | Annotate | Line # | Download | only in dist
      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