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