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 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
check_signals(const void * KYUA_DEFS_UNUSED_PARAM (cookie))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
check_umask(const void * KYUA_DEFS_UNUSED_PARAM (cookie))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
check_work_directory(const void * cookie)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
check_uid_not_root(const void * KYUA_DEFS_UNUSED_PARAM (cookie))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
check_gid_not_root(const void * KYUA_DEFS_UNUSED_PARAM (cookie))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
check_not_root(const void * KYUA_DEFS_UNUSED_PARAM (cookie))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
exec_check(const char * program,const char * const * args,int * exitstatus)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
fork_check(const kyua_run_params_t * run_params,void (* hook)(const void *),const void * cookie)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);
ATF_TC_BODY(run_params_init__defaults,tc)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);
ATF_TC_BODY(fork_exec_wait__ok,tc)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);
ATF_TC_HEAD(fork_exec_wait__eacces,tc)330 ATF_TC_HEAD(fork_exec_wait__eacces, tc)
331 {
332 atf_tc_set_md_var(tc, "require.user", "unprivileged");
333 }
ATF_TC_BODY(fork_exec_wait__eacces,tc)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);
ATF_TC_BODY(fork_exec_wait__enoent,tc)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);
ATF_TC_BODY(fork_wait__core_size,tc)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);
ATF_TC_BODY(fork_wait__env,tc)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);
ATF_TC_BODY(fork_wait__process_group,tc)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);
ATF_TC_BODY(fork_wait__signals,tc)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);
ATF_TC_BODY(fork_wait__timeout,tc)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);
ATF_TC_BODY(fork_wait__umask,tc)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);
ATF_TC_HEAD(fork_wait__unprivileged_user,tc)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 }
ATF_TC_BODY(fork_wait__unprivileged_user,tc)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);
ATF_TC_HEAD(fork_wait__unprivileged_group,tc)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 }
ATF_TC_BODY(fork_wait__unprivileged_group,tc)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);
ATF_TC_HEAD(fork_wait__unprivileged_both,tc)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 }
ATF_TC_BODY(fork_wait__unprivileged_both,tc)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);
ATF_TC_BODY(fork_wait__work_directory,tc)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);
ATF_TC_BODY(work_directory__builtin_tmpdir,tc)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);
ATF_TC_BODY(work_directory__env_tmpdir,tc)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);
ATF_TC_HEAD(work_directory__permissions,tc)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 }
ATF_TC_BODY(work_directory__permissions,tc)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);
ATF_TC_HEAD(work_directory__mkdtemp_error,tc)647 ATF_TC_HEAD(work_directory__mkdtemp_error, tc)
648 {
649 atf_tc_set_md_var(tc, "require.user", "unprivileged");
650 }
ATF_TC_BODY(work_directory__mkdtemp_error,tc)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);
ATF_TC_HEAD(work_directory__permissions_error,tc)672 ATF_TC_HEAD(work_directory__permissions_error, tc)
673 {
674 atf_tc_set_md_var(tc, "require.user", "unprivileged");
675 }
ATF_TC_BODY(work_directory__permissions_error,tc)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
work_directory_signal_check(const int signo)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);
ATF_TC_BODY(work_directory__sighup,tc)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);
ATF_TC_BODY(work_directory__sigint,tc)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);
ATF_TC_BODY(work_directory__sigterm,tc)755 ATF_TC_BODY(work_directory__sigterm, tc)
756 {
757 work_directory_signal_check(SIGTERM);
758 }
759
760
ATF_TP_ADD_TCS(tp)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