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