1 // Copyright 2014 The Kyua Authors.
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 #include "utils/process/isolation.hpp"
30
31 extern "C" {
32 #include <sys/types.h>
33 #include <sys/resource.h>
34 #include <sys/stat.h>
35
36 #include <unistd.h>
37 }
38
39 #include <cerrno>
40 #include <cstdlib>
41 #include <fstream>
42 #include <iostream>
43
44 #include <atf-c++.hpp>
45
46 #include "utils/defs.hpp"
47 #include "utils/env.hpp"
48 #include "utils/format/macros.hpp"
49 #include "utils/fs/operations.hpp"
50 #include "utils/fs/path.hpp"
51 #include "utils/optional.ipp"
52 #include "utils/passwd.hpp"
53 #include "utils/process/child.ipp"
54 #include "utils/process/status.hpp"
55 #include "utils/sanity.hpp"
56 #include "utils/test_utils.ipp"
57
58 namespace fs = utils::fs;
59 namespace passwd = utils::passwd;
60 namespace process = utils::process;
61
62 using utils::none;
63 using utils::optional;
64
65
66 namespace {
67
68
69 /// Runs the given hook in a subprocess.
70 ///
71 /// \param hook The code to run in the subprocess.
72 ///
73 /// \return The status of the subprocess for further validation.
74 ///
75 /// \post The subprocess.stdout and subprocess.stderr files, created in the
76 /// current directory, contain the output of the subprocess.
77 template< typename Hook >
78 static process::status
fork_and_run(Hook hook)79 fork_and_run(Hook hook)
80 {
81 std::auto_ptr< process::child > child = process::child::fork_files(
82 hook, fs::path("subprocess.stdout"), fs::path("subprocess.stderr"));
83 const process::status status = child->wait();
84
85 atf::utils::cat_file("subprocess.stdout", "isolated child stdout: ");
86 atf::utils::cat_file("subprocess.stderr", "isolated child stderr: ");
87
88 return status;
89 }
90
91
92 /// Subprocess that validates the cleanliness of the environment.
93 ///
94 /// \post Exits with success if the environment is clean; failure otherwise.
95 static void
check_clean_environment(void)96 check_clean_environment(void)
97 {
98 fs::mkdir(fs::path("some-directory"), 0755);
99 process::isolate_child(none, fs::path("some-directory"));
100
101 bool failed = false;
102
103 const char* empty[] = { "LANG", "LC_ALL", "LC_COLLATE", "LC_CTYPE",
104 "LC_MESSAGES", "LC_MONETARY", "LC_NUMERIC",
105 "LC_TIME", NULL };
106 const char** iter;
107 for (iter = empty; *iter != NULL; ++iter) {
108 if (utils::getenv(*iter)) {
109 failed = true;
110 std::cout << F("%s was not unset\n") % *iter;
111 }
112 }
113
114 if (utils::getenv_with_default("HOME", "") != "some-directory") {
115 failed = true;
116 std::cout << "HOME was not set to the work directory\n";
117 }
118
119 if (utils::getenv_with_default("TMPDIR", "") != "some-directory") {
120 failed = true;
121 std::cout << "TMPDIR was not set to the work directory\n";
122 }
123
124 if (utils::getenv_with_default("TZ", "") != "UTC") {
125 failed = true;
126 std::cout << "TZ was not set to UTC\n";
127 }
128
129 if (utils::getenv_with_default("LEAVE_ME_ALONE", "") != "kill-some-day") {
130 failed = true;
131 std::cout << "LEAVE_ME_ALONE was modified while it should not have "
132 "been\n";
133 }
134
135 std::exit(failed ? EXIT_FAILURE : EXIT_SUCCESS);
136 }
137
138
139 /// Subprocess that checks if user privileges are dropped.
140 class check_drop_privileges {
141 /// The user to drop the privileges to.
142 const passwd::user _unprivileged_user;
143
144 public:
145 /// Constructor.
146 ///
147 /// \param unprivileged_user The user to drop the privileges to.
check_drop_privileges(const passwd::user & unprivileged_user)148 check_drop_privileges(const passwd::user& unprivileged_user) :
149 _unprivileged_user(unprivileged_user)
150 {
151 }
152
153 /// Body of the subprocess.
154 ///
155 /// \post Exits with success if the process has dropped privileges as
156 /// expected.
157 void
operator ()(void) const158 operator()(void) const
159 {
160 fs::mkdir(fs::path("subdir"), 0755);
161 process::isolate_child(utils::make_optional(_unprivileged_user),
162 fs::path("subdir"));
163
164 if (::getuid() == 0) {
165 std::cout << "UID is still 0\n";
166 std::exit(EXIT_FAILURE);
167 }
168
169 if (::getgid() == 0) {
170 std::cout << "GID is still 0\n";
171 std::exit(EXIT_FAILURE);
172 }
173
174 ::gid_t groups[1];
175 if (::getgroups(1, groups) == -1) {
176 // Should only fail if we get more than one group notifying about
177 // not enough space in the groups variable to store the whole
178 // result.
179 INV(errno == EINVAL);
180 std::exit(EXIT_FAILURE);
181 }
182 if (groups[0] == 0) {
183 std::cout << "Primary group is still 0\n";
184 std::exit(EXIT_FAILURE);
185 }
186
187 std::ofstream output("file.txt");
188 if (!output) {
189 std::cout << "Cannot write to isolated directory; owner not "
190 "changed?\n";
191 std::exit(EXIT_FAILURE);
192 }
193
194 std::exit(EXIT_SUCCESS);
195 }
196 };
197
198
199 /// Subprocess that dumps core to validate core dumping abilities.
200 static void
check_enable_core_dumps(void)201 check_enable_core_dumps(void)
202 {
203 process::isolate_child(none, fs::path("."));
204 std::abort();
205 }
206
207
208 /// Subprocess that checks if the work directory is entered.
209 class check_enter_work_directory {
210 /// Directory to enter. May be releative.
211 const fs::path _directory;
212
213 public:
214 /// Constructor.
215 ///
216 /// \param directory Directory to enter.
check_enter_work_directory(const fs::path & directory)217 check_enter_work_directory(const fs::path& directory) :
218 _directory(directory)
219 {
220 }
221
222 /// Body of the subprocess.
223 ///
224 /// \post Exits with success if the process has entered the given work
225 /// directory; false otherwise.
226 void
operator ()(void) const227 operator()(void) const
228 {
229 const fs::path exp_subdir = fs::current_path() / _directory;
230 process::isolate_child(none, _directory);
231 std::exit(fs::current_path() == exp_subdir ?
232 EXIT_SUCCESS : EXIT_FAILURE);
233 }
234 };
235
236
237 /// Subprocess that validates that it owns a session.
238 ///
239 /// \post Exits with success if the process lives in its own session;
240 /// failure otherwise.
241 static void
check_new_session(void)242 check_new_session(void)
243 {
244 process::isolate_child(none, fs::path("."));
245 std::exit(::getsid(::getpid()) == ::getpid() ? EXIT_SUCCESS : EXIT_FAILURE);
246 }
247
248
249 /// Subprocess that validates the disconnection from any terminal.
250 ///
251 /// \post Exits with success if the environment is clean; failure otherwise.
252 static void
check_no_terminal(void)253 check_no_terminal(void)
254 {
255 process::isolate_child(none, fs::path("."));
256
257 const char* const args[] = {
258 "/bin/sh",
259 "-i",
260 "-c",
261 "echo success",
262 NULL
263 };
264 ::execv("/bin/sh", UTILS_UNCONST(char*, args));
265 std::abort();
266 }
267
268
269 /// Subprocess that validates that it has become the leader of a process group.
270 ///
271 /// \post Exits with success if the process lives in its own process group;
272 /// failure otherwise.
273 static void
check_process_group(void)274 check_process_group(void)
275 {
276 process::isolate_child(none, fs::path("."));
277 std::exit(::getpgid(::getpid()) == ::getpid() ?
278 EXIT_SUCCESS : EXIT_FAILURE);
279 }
280
281
282 /// Subprocess that validates that the umask has been reset.
283 ///
284 /// \post Exits with success if the umask matches the expected value; failure
285 /// otherwise.
286 static void
check_umask(void)287 check_umask(void)
288 {
289 process::isolate_child(none, fs::path("."));
290 std::exit(::umask(0) == 0022 ? EXIT_SUCCESS : EXIT_FAILURE);
291 }
292
293
294 } // anonymous namespace
295
296
297 ATF_TEST_CASE_WITHOUT_HEAD(isolate_child__clean_environment);
ATF_TEST_CASE_BODY(isolate_child__clean_environment)298 ATF_TEST_CASE_BODY(isolate_child__clean_environment)
299 {
300 utils::setenv("HOME", "/non-existent/directory");
301 utils::setenv("TMPDIR", "/non-existent/directory");
302 utils::setenv("LANG", "C");
303 utils::setenv("LC_ALL", "C");
304 utils::setenv("LC_COLLATE", "C");
305 utils::setenv("LC_CTYPE", "C");
306 utils::setenv("LC_MESSAGES", "C");
307 utils::setenv("LC_MONETARY", "C");
308 utils::setenv("LC_NUMERIC", "C");
309 utils::setenv("LC_TIME", "C");
310 utils::setenv("LEAVE_ME_ALONE", "kill-some-day");
311 utils::setenv("TZ", "EST+5");
312
313 const process::status status = fork_and_run(check_clean_environment);
314 ATF_REQUIRE(status.exited());
315 ATF_REQUIRE_EQ(EXIT_SUCCESS, status.exitstatus());
316 }
317
318
319 ATF_TEST_CASE(isolate_child__other_user_when_unprivileged);
ATF_TEST_CASE_HEAD(isolate_child__other_user_when_unprivileged)320 ATF_TEST_CASE_HEAD(isolate_child__other_user_when_unprivileged)
321 {
322 set_md_var("require.user", "unprivileged");
323 }
ATF_TEST_CASE_BODY(isolate_child__other_user_when_unprivileged)324 ATF_TEST_CASE_BODY(isolate_child__other_user_when_unprivileged)
325 {
326 const passwd::user user = passwd::current_user();
327
328 passwd::user other_user = user;
329 other_user.uid += 1;
330 other_user.gid += 1;
331 process::isolate_child(utils::make_optional(other_user), fs::path("."));
332
333 ATF_REQUIRE_EQ(user.uid, ::getuid());
334 ATF_REQUIRE_EQ(user.gid, ::getgid());
335 }
336
337
338 ATF_TEST_CASE(isolate_child__drop_privileges);
ATF_TEST_CASE_HEAD(isolate_child__drop_privileges)339 ATF_TEST_CASE_HEAD(isolate_child__drop_privileges)
340 {
341 set_md_var("require.config", "unprivileged-user");
342 set_md_var("require.user", "root");
343 }
ATF_TEST_CASE_BODY(isolate_child__drop_privileges)344 ATF_TEST_CASE_BODY(isolate_child__drop_privileges)
345 {
346 const passwd::user unprivileged_user = passwd::find_user_by_name(
347 get_config_var("unprivileged-user"));
348
349 const process::status status = fork_and_run(check_drop_privileges(
350 unprivileged_user));
351 ATF_REQUIRE(status.exited());
352 ATF_REQUIRE_EQ(EXIT_SUCCESS, status.exitstatus());
353 }
354
355
356 ATF_TEST_CASE(isolate_child__drop_privileges_fail_uid);
ATF_TEST_CASE_HEAD(isolate_child__drop_privileges_fail_uid)357 ATF_TEST_CASE_HEAD(isolate_child__drop_privileges_fail_uid)
358 {
359 set_md_var("require.user", "unprivileged");
360 }
ATF_TEST_CASE_BODY(isolate_child__drop_privileges_fail_uid)361 ATF_TEST_CASE_BODY(isolate_child__drop_privileges_fail_uid)
362 {
363 // Fake the current user as root so that we bypass the protections in
364 // isolate_child that prevent us from attempting a user switch when we are
365 // not root. We do this so we can trigger the setuid failure.
366 passwd::user root = passwd::user("root", 0, 0);
367 ATF_REQUIRE(root.is_root());
368 passwd::set_current_user_for_testing(root);
369
370 passwd::user unprivileged_user = passwd::current_user();
371 unprivileged_user.uid += 1;
372
373 const process::status status = fork_and_run(check_drop_privileges(
374 unprivileged_user));
375 ATF_REQUIRE(status.exited());
376 ATF_REQUIRE_EQ(process::exit_isolation_failure, status.exitstatus());
377 ATF_REQUIRE(atf::utils::grep_file("(chown|setuid).*failed",
378 "subprocess.stderr"));
379 }
380
381
382 ATF_TEST_CASE(isolate_child__drop_privileges_fail_gid);
ATF_TEST_CASE_HEAD(isolate_child__drop_privileges_fail_gid)383 ATF_TEST_CASE_HEAD(isolate_child__drop_privileges_fail_gid)
384 {
385 set_md_var("require.user", "unprivileged");
386 }
ATF_TEST_CASE_BODY(isolate_child__drop_privileges_fail_gid)387 ATF_TEST_CASE_BODY(isolate_child__drop_privileges_fail_gid)
388 {
389 // Fake the current user as root so that we bypass the protections in
390 // isolate_child that prevent us from attempting a user switch when we are
391 // not root. We do this so we can trigger the setgid failure.
392 passwd::user root = passwd::user("root", 0, 0);
393 ATF_REQUIRE(root.is_root());
394 passwd::set_current_user_for_testing(root);
395
396 passwd::user unprivileged_user = passwd::current_user();
397 unprivileged_user.gid += 1;
398
399 const process::status status = fork_and_run(check_drop_privileges(
400 unprivileged_user));
401 ATF_REQUIRE(status.exited());
402 ATF_REQUIRE_EQ(process::exit_isolation_failure, status.exitstatus());
403 ATF_REQUIRE(atf::utils::grep_file("(chown|setgid).*failed",
404 "subprocess.stderr"));
405 }
406
407
408 ATF_TEST_CASE_WITHOUT_HEAD(isolate_child__enable_core_dumps);
ATF_TEST_CASE_BODY(isolate_child__enable_core_dumps)409 ATF_TEST_CASE_BODY(isolate_child__enable_core_dumps)
410 {
411 utils::require_run_coredump_tests(this);
412
413 struct ::rlimit rl;
414 if (::getrlimit(RLIMIT_CORE, &rl) == -1)
415 fail("Failed to query the core size limit");
416 if (rl.rlim_cur == 0 || rl.rlim_max == 0)
417 skip("Maximum core size is zero; cannot run test");
418 rl.rlim_cur = 0;
419 if (::setrlimit(RLIMIT_CORE, &rl) == -1)
420 fail("Failed to lower the core size limit");
421
422 const process::status status = fork_and_run(check_enable_core_dumps);
423 ATF_REQUIRE(status.signaled());
424 ATF_REQUIRE(status.coredump());
425 }
426
427
428 ATF_TEST_CASE_WITHOUT_HEAD(isolate_child__enter_work_directory);
ATF_TEST_CASE_BODY(isolate_child__enter_work_directory)429 ATF_TEST_CASE_BODY(isolate_child__enter_work_directory)
430 {
431 const fs::path directory("some/sub/directory");
432 fs::mkdir_p(directory, 0755);
433 const process::status status = fork_and_run(
434 check_enter_work_directory(directory));
435 ATF_REQUIRE(status.exited());
436 ATF_REQUIRE_EQ(EXIT_SUCCESS, status.exitstatus());
437 }
438
439
440 ATF_TEST_CASE_WITHOUT_HEAD(isolate_child__enter_work_directory_failure);
ATF_TEST_CASE_BODY(isolate_child__enter_work_directory_failure)441 ATF_TEST_CASE_BODY(isolate_child__enter_work_directory_failure)
442 {
443 const fs::path directory("some/sub/directory");
444 const process::status status = fork_and_run(
445 check_enter_work_directory(directory));
446 ATF_REQUIRE(status.exited());
447 ATF_REQUIRE_EQ(process::exit_isolation_failure, status.exitstatus());
448 ATF_REQUIRE(atf::utils::grep_file("chdir\\(some/sub/directory\\) failed",
449 "subprocess.stderr"));
450 }
451
452
453 ATF_TEST_CASE_WITHOUT_HEAD(isolate_child__new_session);
ATF_TEST_CASE_BODY(isolate_child__new_session)454 ATF_TEST_CASE_BODY(isolate_child__new_session)
455 {
456 const process::status status = fork_and_run(check_new_session);
457 ATF_REQUIRE(status.exited());
458 ATF_REQUIRE_EQ(EXIT_SUCCESS, status.exitstatus());
459 }
460
461
462 ATF_TEST_CASE_WITHOUT_HEAD(isolate_child__no_terminal);
ATF_TEST_CASE_BODY(isolate_child__no_terminal)463 ATF_TEST_CASE_BODY(isolate_child__no_terminal)
464 {
465 const process::status status = fork_and_run(check_no_terminal);
466 ATF_REQUIRE(status.exited());
467 ATF_REQUIRE_EQ(EXIT_SUCCESS, status.exitstatus());
468 }
469
470
471 ATF_TEST_CASE_WITHOUT_HEAD(isolate_child__process_group);
ATF_TEST_CASE_BODY(isolate_child__process_group)472 ATF_TEST_CASE_BODY(isolate_child__process_group)
473 {
474 const process::status status = fork_and_run(check_process_group);
475 ATF_REQUIRE(status.exited());
476 ATF_REQUIRE_EQ(EXIT_SUCCESS, status.exitstatus());
477 }
478
479
480 ATF_TEST_CASE_WITHOUT_HEAD(isolate_child__reset_umask);
ATF_TEST_CASE_BODY(isolate_child__reset_umask)481 ATF_TEST_CASE_BODY(isolate_child__reset_umask)
482 {
483 const process::status status = fork_and_run(check_umask);
484 ATF_REQUIRE(status.exited());
485 ATF_REQUIRE_EQ(EXIT_SUCCESS, status.exitstatus());
486 }
487
488
489 /// Executes isolate_path() and compares the on-disk changes to expected values.
490 ///
491 /// \param unprivileged_user The user to pass to isolate_path; may be none.
492 /// \param exp_uid Expected UID or none to expect the old value.
493 /// \param exp_gid Expected GID or none to expect the old value.
494 static void
do_isolate_path_test(const optional<passwd::user> & unprivileged_user,const optional<uid_t> & exp_uid,const optional<gid_t> & exp_gid)495 do_isolate_path_test(const optional< passwd::user >& unprivileged_user,
496 const optional< uid_t >& exp_uid,
497 const optional< gid_t >& exp_gid)
498 {
499 const fs::path dir("dir");
500 fs::mkdir(dir, 0755);
501 struct ::stat old_sb;
502 ATF_REQUIRE(::stat(dir.c_str(), &old_sb) != -1);
503
504 process::isolate_path(unprivileged_user, dir);
505
506 struct ::stat new_sb;
507 ATF_REQUIRE(::stat(dir.c_str(), &new_sb) != -1);
508
509 if (exp_uid)
510 ATF_REQUIRE_EQ(exp_uid.get(), new_sb.st_uid);
511 else
512 ATF_REQUIRE_EQ(old_sb.st_uid, new_sb.st_uid);
513
514 if (exp_gid)
515 ATF_REQUIRE_EQ(exp_gid.get(), new_sb.st_gid);
516 else
517 ATF_REQUIRE_EQ(old_sb.st_gid, new_sb.st_gid);
518 }
519
520
521 ATF_TEST_CASE_WITHOUT_HEAD(isolate_path__no_user);
ATF_TEST_CASE_BODY(isolate_path__no_user)522 ATF_TEST_CASE_BODY(isolate_path__no_user)
523 {
524 do_isolate_path_test(none, none, none);
525 }
526
527
528 ATF_TEST_CASE_WITHOUT_HEAD(isolate_path__same_user);
ATF_TEST_CASE_BODY(isolate_path__same_user)529 ATF_TEST_CASE_BODY(isolate_path__same_user)
530 {
531 do_isolate_path_test(utils::make_optional(passwd::current_user()),
532 none, none);
533 }
534
535
536 ATF_TEST_CASE(isolate_path__other_user_when_unprivileged);
ATF_TEST_CASE_HEAD(isolate_path__other_user_when_unprivileged)537 ATF_TEST_CASE_HEAD(isolate_path__other_user_when_unprivileged)
538 {
539 set_md_var("require.user", "unprivileged");
540 }
ATF_TEST_CASE_BODY(isolate_path__other_user_when_unprivileged)541 ATF_TEST_CASE_BODY(isolate_path__other_user_when_unprivileged)
542 {
543 passwd::user user = passwd::current_user();
544 user.uid += 1;
545 user.gid += 1;
546
547 do_isolate_path_test(utils::make_optional(user), none, none);
548 }
549
550
551 ATF_TEST_CASE(isolate_path__drop_privileges);
ATF_TEST_CASE_HEAD(isolate_path__drop_privileges)552 ATF_TEST_CASE_HEAD(isolate_path__drop_privileges)
553 {
554 set_md_var("require.config", "unprivileged-user");
555 set_md_var("require.user", "root");
556 }
ATF_TEST_CASE_BODY(isolate_path__drop_privileges)557 ATF_TEST_CASE_BODY(isolate_path__drop_privileges)
558 {
559 const passwd::user unprivileged_user = passwd::find_user_by_name(
560 get_config_var("unprivileged-user"));
561 do_isolate_path_test(utils::make_optional(unprivileged_user),
562 utils::make_optional(unprivileged_user.uid),
563 utils::make_optional(unprivileged_user.gid));
564 }
565
566
567 ATF_TEST_CASE(isolate_path__drop_privileges_only_uid);
ATF_TEST_CASE_HEAD(isolate_path__drop_privileges_only_uid)568 ATF_TEST_CASE_HEAD(isolate_path__drop_privileges_only_uid)
569 {
570 set_md_var("require.config", "unprivileged-user");
571 set_md_var("require.user", "root");
572 }
ATF_TEST_CASE_BODY(isolate_path__drop_privileges_only_uid)573 ATF_TEST_CASE_BODY(isolate_path__drop_privileges_only_uid)
574 {
575 passwd::user unprivileged_user = passwd::find_user_by_name(
576 get_config_var("unprivileged-user"));
577 unprivileged_user.gid = ::getgid();
578 do_isolate_path_test(utils::make_optional(unprivileged_user),
579 utils::make_optional(unprivileged_user.uid),
580 none);
581 }
582
583
584 ATF_TEST_CASE(isolate_path__drop_privileges_only_gid);
ATF_TEST_CASE_HEAD(isolate_path__drop_privileges_only_gid)585 ATF_TEST_CASE_HEAD(isolate_path__drop_privileges_only_gid)
586 {
587 set_md_var("require.config", "unprivileged-user");
588 set_md_var("require.user", "root");
589 }
ATF_TEST_CASE_BODY(isolate_path__drop_privileges_only_gid)590 ATF_TEST_CASE_BODY(isolate_path__drop_privileges_only_gid)
591 {
592 passwd::user unprivileged_user = passwd::find_user_by_name(
593 get_config_var("unprivileged-user"));
594 unprivileged_user.uid = ::getuid();
595 do_isolate_path_test(utils::make_optional(unprivileged_user),
596 none,
597 utils::make_optional(unprivileged_user.gid));
598 }
599
600
ATF_INIT_TEST_CASES(tcs)601 ATF_INIT_TEST_CASES(tcs)
602 {
603 ATF_ADD_TEST_CASE(tcs, isolate_child__clean_environment);
604 ATF_ADD_TEST_CASE(tcs, isolate_child__other_user_when_unprivileged);
605 ATF_ADD_TEST_CASE(tcs, isolate_child__drop_privileges);
606 ATF_ADD_TEST_CASE(tcs, isolate_child__drop_privileges_fail_uid);
607 ATF_ADD_TEST_CASE(tcs, isolate_child__drop_privileges_fail_gid);
608 ATF_ADD_TEST_CASE(tcs, isolate_child__enable_core_dumps);
609 ATF_ADD_TEST_CASE(tcs, isolate_child__enter_work_directory);
610 ATF_ADD_TEST_CASE(tcs, isolate_child__enter_work_directory_failure);
611 ATF_ADD_TEST_CASE(tcs, isolate_child__new_session);
612 ATF_ADD_TEST_CASE(tcs, isolate_child__no_terminal);
613 ATF_ADD_TEST_CASE(tcs, isolate_child__process_group);
614 ATF_ADD_TEST_CASE(tcs, isolate_child__reset_umask);
615
616 ATF_ADD_TEST_CASE(tcs, isolate_path__no_user);
617 ATF_ADD_TEST_CASE(tcs, isolate_path__same_user);
618 ATF_ADD_TEST_CASE(tcs, isolate_path__other_user_when_unprivileged);
619 ATF_ADD_TEST_CASE(tcs, isolate_path__drop_privileges);
620 ATF_ADD_TEST_CASE(tcs, isolate_path__drop_privileges_only_uid);
621 ATF_ADD_TEST_CASE(tcs, isolate_path__drop_privileges_only_gid);
622 }
623