1 /* Copyright 2018 The ChromiumOS Authors
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 *
5 * Test the minijail0 CLI using gtest.
6 *
7 * Note: We don't verify that the minijail struct was set correctly from these
8 * flags as only libminijail.c knows that definition. If we wanted to improve
9 * this test, we'd have to pull that struct into a common (internal) header.
10 */
11
12 #include <cstdio>
13 #include <fstream>
14 #include <stdio.h>
15 #include <stdlib.h>
16
17 #include <gtest/gtest.h>
18
19 #include "config_parser.h"
20 #include "libminijail.h"
21 #include "minijail0_cli.h"
22 #include "test_util.h"
23
24 namespace {
25
26 constexpr char kValidUser[] = "nobody";
27 constexpr char kValidUid[] = "100";
28 constexpr char kValidGroup[] = "users";
29 constexpr char kValidGid[] = "100";
30
31 class CliTest : public ::testing::Test {
32 protected:
SetUp()33 virtual void SetUp() {
34 // Most tests do not care about this logic. For the few that do, make
35 // them opt into it so they can validate specifically.
36 elftype_ = ELFDYNAMIC;
37 }
TearDown()38 virtual void TearDown() {}
39
40 // We use a vector of strings rather than const char * pointers because we
41 // need the backing memory to be writable. The CLI might mutate the strings
42 // as it parses things (which is normally permissible with argv).
parse_args_(const std::vector<std::string> & argv,bool * exit_immediately,ElfType * elftype)43 int parse_args_(const std::vector<std::string>& argv,
44 bool* exit_immediately,
45 ElfType* elftype) {
46 // Make sure we reset the getopts state when scanning a new argv. Setting
47 // this to 0 is a GNU extension, but AOSP/BSD also checks this (as an alias
48 // to their "optreset").
49 optind = 0;
50
51 // We create & destroy this for every parse_args call because some API
52 // calls can dupe memory which confuses LSAN. https://crbug.com/844615
53 struct minijail* j = minijail_new();
54
55 std::vector<const char*> pargv;
56 pargv.push_back("minijail0");
57 for (const std::string& arg : argv)
58 pargv.push_back(arg.c_str());
59
60 // We grab stdout from parse_args itself as it might dump things we don't
61 // usually care about like help output.
62 testing::internal::CaptureStdout();
63
64 const char* preload_path = PRELOADPATH;
65 char** envp = NULL;
66 int ret =
67 parse_args(j, pargv.size(), const_cast<char* const*>(pargv.data()),
68 NULL, exit_immediately, elftype, &preload_path, &envp);
69 testing::internal::GetCapturedStdout();
70
71 minijail_destroy(j);
72
73 return ret;
74 }
75
parse_args_(const std::vector<std::string> & argv)76 int parse_args_(const std::vector<std::string>& argv) {
77 return parse_args_(argv, &exit_immediately_, &elftype_);
78 }
79
80 ElfType elftype_;
81 bool exit_immediately_;
82 };
83
84 } // namespace
85
86 // Should exit non-zero when there's no arguments.
TEST_F(CliTest,no_args)87 TEST_F(CliTest, no_args) {
88 std::vector<std::string> argv = {};
89 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
90 }
91
92 // Should exit zero when we asked for help.
TEST_F(CliTest,help)93 TEST_F(CliTest, help) {
94 std::vector<std::string> argv = {"-h"};
95 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(0), "");
96
97 argv = {"--help"};
98 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(0), "");
99
100 argv = {"-H"};
101 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(0), "");
102 }
103
104 // Just a simple program to run.
TEST_F(CliTest,valid_program)105 TEST_F(CliTest, valid_program) {
106 std::vector<std::string> argv = {"/bin/sh"};
107 ASSERT_TRUE(parse_args_(argv));
108 }
109
110 // Valid calls to the change user option.
TEST_F(CliTest,valid_set_user)111 TEST_F(CliTest, valid_set_user) {
112 std::vector<std::string> argv = {"-u", "", "/bin/sh"};
113
114 argv[1] = kValidUser;
115 ASSERT_TRUE(parse_args_(argv));
116
117 argv[1] = kValidUid;
118 ASSERT_TRUE(parse_args_(argv));
119 }
120
121 // Invalid calls to the change user option.
TEST_F(CliTest,invalid_set_user)122 TEST_F(CliTest, invalid_set_user) {
123 std::vector<std::string> argv = {"-u", "", "/bin/sh"};
124 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
125
126 argv[1] = "j;lX:J*Pj;oijfs;jdlkjC;j";
127 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
128
129 argv[1] = "1000x";
130 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
131
132 // Supplying -u more than once is bad.
133 argv = {"-u", kValidUser, "-u", kValidUid, "/bin/sh"};
134 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1),
135 "-u provided multiple times");
136 }
137
138 // Valid calls to the change group option.
TEST_F(CliTest,valid_set_group)139 TEST_F(CliTest, valid_set_group) {
140 std::vector<std::string> argv = {"-g", "", "/bin/sh"};
141
142 argv[1] = kValidGroup;
143 ASSERT_TRUE(parse_args_(argv));
144
145 argv[1] = kValidGid;
146 ASSERT_TRUE(parse_args_(argv));
147 }
148
149 // Invalid calls to the change group option.
TEST_F(CliTest,invalid_set_group)150 TEST_F(CliTest, invalid_set_group) {
151 std::vector<std::string> argv = {"-g", "", "/bin/sh"};
152 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
153
154 argv[1] = "j;lX:J*Pj;oijfs;jdlkjC;j";
155 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
156
157 argv[1] = "1000x";
158 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
159
160 // Supplying -g more than once is bad.
161 argv = {"-g", kValidGroup, "-g", kValidGid, "/bin/sh"};
162 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1),
163 "-g provided multiple times");
164 }
165
166 // Valid calls to the add-suppl-group option.
TEST_F(CliTest,valid_add_supp_group)167 TEST_F(CliTest, valid_add_supp_group) {
168 std::vector<std::string> argv = {"--add-suppl-group", "", "/bin/sh"};
169
170 argv[1] = kValidGroup;
171 ASSERT_TRUE(parse_args_(argv));
172
173 argv[1] = kValidGid;
174 ASSERT_TRUE(parse_args_(argv));
175
176 std::vector<std::string> argv2 = {"--add-suppl-group", "",
177 "--add-suppl-group", "", "/bin/sh"};
178 argv[1] = kValidGroup;
179 argv[2] = kValidGid;
180 ASSERT_TRUE(parse_args_(argv));
181 }
182
183 // Invalid calls to the add-suppl-group option.
TEST_F(CliTest,invalid_add_supp_group)184 TEST_F(CliTest, invalid_add_supp_group) {
185 std::vector<std::string> argv = {"--add-suppl-group", "", "/bin/sh"};
186
187 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
188
189 argv[1] = "j;lX:J*Pj;oijfs;jdlkjC;j";
190 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
191
192 argv[1] = "1000x";
193 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
194 }
195
196 // Valid calls to the skip securebits option.
TEST_F(CliTest,valid_skip_securebits)197 TEST_F(CliTest, valid_skip_securebits) {
198 // An empty string is the same as 0.
199 std::vector<std::string> argv = {"-B", "", "/bin/sh"};
200 ASSERT_TRUE(parse_args_(argv));
201
202 argv[1] = "0xAB";
203 ASSERT_TRUE(parse_args_(argv));
204
205 argv[1] = "1234";
206 ASSERT_TRUE(parse_args_(argv));
207 }
208
209 // Invalid calls to the skip securebits option.
TEST_F(CliTest,invalid_skip_securebits)210 TEST_F(CliTest, invalid_skip_securebits) {
211 std::vector<std::string> argv = {"-B", "", "/bin/sh"};
212
213 argv[1] = "xja";
214 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
215 }
216
217 // Valid calls to the caps option.
TEST_F(CliTest,valid_caps)218 TEST_F(CliTest, valid_caps) {
219 // An empty string is the same as 0.
220 std::vector<std::string> argv = {"-c", "", "/bin/sh"};
221 ASSERT_TRUE(parse_args_(argv));
222
223 argv[1] = "0xAB";
224 ASSERT_TRUE(parse_args_(argv));
225
226 argv[1] = "1234";
227 ASSERT_TRUE(parse_args_(argv));
228 }
229
230 // Invalid calls to the caps option.
TEST_F(CliTest,invalid_caps)231 TEST_F(CliTest, invalid_caps) {
232 std::vector<std::string> argv = {"-c", "", "/bin/sh"};
233
234 argv[1] = "xja";
235 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
236 }
237
238 // Valid calls to the logging option.
TEST_F(CliTest,valid_logging)239 TEST_F(CliTest, valid_logging) {
240 std::vector<std::string> argv = {"--logging", "", "/bin/sh"};
241
242 // This should list all valid logging targets.
243 const std::vector<std::string> profiles = {
244 "stderr",
245 "syslog",
246 };
247
248 for (const auto& profile : profiles) {
249 argv[1] = profile;
250 ASSERT_TRUE(parse_args_(argv));
251 }
252 }
253
254 // Invalid calls to the logging option.
TEST_F(CliTest,invalid_logging)255 TEST_F(CliTest, invalid_logging) {
256 std::vector<std::string> argv = {"--logging", "", "/bin/sh"};
257 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
258
259 argv[1] = "stdout";
260 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
261 }
262
263 // Valid calls to the rlimit option.
TEST_F(CliTest,valid_rlimit)264 TEST_F(CliTest, valid_rlimit) {
265 std::vector<std::string> argv = {"-R", "", "/bin/sh"};
266
267 argv[1] = "0,1,2";
268 ASSERT_TRUE(parse_args_(argv));
269
270 argv[1] = "0,0x100,4";
271 ASSERT_TRUE(parse_args_(argv));
272
273 argv[1] = "1,1,unlimited";
274 ASSERT_TRUE(parse_args_(argv));
275
276 argv[1] = "2,unlimited,2";
277 ASSERT_TRUE(parse_args_(argv));
278
279 argv[1] = "RLIMIT_AS,unlimited,unlimited";
280 ASSERT_TRUE(parse_args_(argv));
281 }
282
283 // Invalid calls to the rlimit option.
TEST_F(CliTest,invalid_rlimit)284 TEST_F(CliTest, invalid_rlimit) {
285 std::vector<std::string> argv = {"-R", "", "/bin/sh"};
286 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
287
288 // Missing cur & max.
289 argv[1] = "0";
290 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
291
292 // Missing max.
293 argv[1] = "0,0";
294 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
295
296 // Too many options.
297 argv[1] = "0,0,0,0";
298 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
299
300 // Non-numeric limits
301 argv[1] = "0,0,invalid-limit";
302 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
303
304 // Invalid number.
305 argv[1] = "0,0,0j";
306 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
307
308 // Invalid hex number.
309 argv[1] = "0,0x1jf,0";
310 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
311
312 // Invalid rlimit constant.
313 argv[1] = "RLIMIT_GOGOOGOG,0,0";
314 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
315 }
316
317 // Valid calls to the profile option.
TEST_F(CliTest,valid_profile)318 TEST_F(CliTest, valid_profile) {
319 std::vector<std::string> argv = {"--profile", "", "/bin/sh"};
320
321 // This should list all valid profiles.
322 const std::vector<std::string> profiles = {
323 "minimalistic-mountns",
324 "minimalistic-mountns-nodev",
325 };
326
327 for (const auto& profile : profiles) {
328 argv[1] = profile;
329 ASSERT_TRUE(parse_args_(argv));
330 }
331 }
332
333 // Invalid calls to the profile option.
TEST_F(CliTest,invalid_profile)334 TEST_F(CliTest, invalid_profile) {
335 std::vector<std::string> argv = {"--profile", "", "/bin/sh"};
336 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
337
338 argv[1] = "random-unknown-profile";
339 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
340 }
341
342 // Valid usage of the no-fs-restrictions option.
TEST_F(CliTest,valid_no_fs_restrictions)343 TEST_F(CliTest, valid_no_fs_restrictions) {
344 std::vector<std::string> argv = {"--profile", "minimalistic-mountns",
345 "--no-fs-restrictions", "/bin/sh"};
346
347 ASSERT_TRUE(parse_args_(argv));
348 }
349
350 // Invalid usage of the no-fs-restrictions option.
TEST_F(CliTest,invalid_no_fs_restrictions)351 TEST_F(CliTest, invalid_no_fs_restrictions) {
352 // Using an fs-path-* flag at the same time shouldn't be allowed.
353 std::vector<std::string> argv = {"--fs-path-rx", "/", "--no-fs-restrictions",
354 "/bin/sh"};
355
356 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
357 }
358
359 // Valid calls to the chroot option.
TEST_F(CliTest,valid_chroot)360 TEST_F(CliTest, valid_chroot) {
361 std::vector<std::string> argv = {"-C", "/", "/bin/sh"};
362 ASSERT_TRUE(parse_args_(argv));
363 }
364
365 // Valid calls to the pivot root option.
TEST_F(CliTest,valid_pivot_root)366 TEST_F(CliTest, valid_pivot_root) {
367 std::vector<std::string> argv = {"-P", "/", "/bin/sh"};
368 ASSERT_TRUE(parse_args_(argv));
369 }
370
371 // We cannot handle multiple options with chroot/profile/pivot root.
TEST_F(CliTest,conflicting_roots)372 TEST_F(CliTest, conflicting_roots) {
373 std::vector<std::string> argv;
374
375 // Chroot & pivot root.
376 argv = {"-C", "/", "-P", "/", "/bin/sh"};
377 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
378
379 // Chroot & minimalistic-mountns profile.
380 argv = {"-C", "/", "--profile", "minimalistic-mountns", "/bin/sh"};
381 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
382
383 // Pivot root & minimalistic-mountns profile.
384 argv = {"-P", "/", "--profile", "minimalistic-mountns", "/bin/sh"};
385 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
386 }
387
388 // Valid calls to the uidmap option.
TEST_F(CliTest,valid_uidmap)389 TEST_F(CliTest, valid_uidmap) {
390 std::vector<std::string> argv = {"-m", "/bin/sh"};
391 // Use a default map (no option from user).
392 ASSERT_TRUE(parse_args_(argv));
393
394 // Use a single map.
395 argv = {"-m0 0 1", "/bin/sh"};
396 ASSERT_TRUE(parse_args_(argv));
397
398 // Multiple maps.
399 argv = {"-m0 0 1,100 100 1", "/bin/sh"};
400 ASSERT_TRUE(parse_args_(argv));
401 }
402
403 // Valid calls to the gidmap option.
TEST_F(CliTest,valid_gidmap)404 TEST_F(CliTest, valid_gidmap) {
405 std::vector<std::string> argv = {"-M", "/bin/sh"};
406 // Use a default map (no option from user).
407 ASSERT_TRUE(parse_args_(argv));
408
409 // Use a single map.
410 argv = {"-M0 0 1", "/bin/sh"};
411 ASSERT_TRUE(parse_args_(argv));
412
413 // Multiple maps.
414 argv = {"-M0 0 1,100 100 1", "/bin/sh"};
415 ASSERT_TRUE(parse_args_(argv));
416 }
417
418 // Invalid calls to the uidmap/gidmap options.
419 // Note: Can't really test these as all validation is delayed/left to the
420 // runtime kernel. Minijail will simply write verbatim what the user gave
421 // it to the corresponding /proc/.../[ug]id_map.
422
423 // Valid calls to the binding option.
TEST_F(CliTest,valid_binding)424 TEST_F(CliTest, valid_binding) {
425 std::vector<std::string> argv = {"-v", "-b", "", "/bin/sh"};
426
427 // Dest & writable are optional.
428 argv[1] = "/";
429 ASSERT_TRUE(parse_args_(argv));
430
431 // Writable is optional.
432 argv[1] = "/,/";
433 ASSERT_TRUE(parse_args_(argv));
434
435 // Writable is an integer.
436 argv[1] = "/,/,0";
437 ASSERT_TRUE(parse_args_(argv));
438 argv[1] = "/,/,1";
439 ASSERT_TRUE(parse_args_(argv));
440
441 // Dest is optional.
442 argv[1] = "/,,0";
443 ASSERT_TRUE(parse_args_(argv));
444 }
445
446 // Invalid calls to the binding option.
TEST_F(CliTest,invalid_binding)447 TEST_F(CliTest, invalid_binding) {
448 std::vector<std::string> argv = {"-v", "-b", "", "/bin/sh"};
449
450 // Missing source.
451 argv[2] = "";
452 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
453
454 // Too many args.
455 argv[2] = "/,/,0,what";
456 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
457
458 // Missing mount namespace/etc...
459 argv = {"-b", "/", "/bin/sh"};
460 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
461
462 // Bad value for <writable>.
463 argv = {"-b", "/,,writable", "/bin/sh"};
464 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
465 }
466
467 // Valid calls to the mount option.
TEST_F(CliTest,valid_mount)468 TEST_F(CliTest, valid_mount) {
469 std::vector<std::string> argv = {"-v", "-k", "", "/bin/sh"};
470
471 // Flags & data are optional.
472 argv[2] = "none,/,none";
473 ASSERT_TRUE(parse_args_(argv));
474
475 // Data is optional.
476 argv[2] = "none,/,none,0xe";
477 ASSERT_TRUE(parse_args_(argv));
478
479 // Flags are optional.
480 argv[2] = "none,/,none,,mode=755";
481 ASSERT_TRUE(parse_args_(argv));
482
483 // Multiple data options to the kernel.
484 argv[2] = "none,/,none,0xe,mode=755,uid=0,gid=10";
485 ASSERT_TRUE(parse_args_(argv));
486
487 // Single MS constant.
488 argv[2] = "none,/,none,MS_NODEV,mode=755";
489 ASSERT_TRUE(parse_args_(argv));
490
491 // Multiple MS constants.
492 argv[2] = "none,/,none,MS_NODEV|MS_NOEXEC,mode=755";
493 ASSERT_TRUE(parse_args_(argv));
494
495 // Mixed constant & number.
496 argv[2] = "none,/,none,MS_NODEV|0xf,mode=755";
497 ASSERT_TRUE(parse_args_(argv));
498 }
499
500 // Invalid calls to the mount option.
TEST_F(CliTest,invalid_mount)501 TEST_F(CliTest, invalid_mount) {
502 std::vector<std::string> argv = {"-v", "-k", "", "/bin/sh"};
503
504 // Missing source.
505 argv[2] = "";
506 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
507
508 // Missing dest.
509 argv[2] = "none";
510 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
511
512 // Missing type.
513 argv[2] = "none,/";
514 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
515
516 // Unknown MS constant.
517 argv[2] = "none,/,none,MS_WHOOPS";
518 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
519 }
520
521 // Valid calls to the remount mode option.
TEST_F(CliTest,valid_remount_mode)522 TEST_F(CliTest, valid_remount_mode) {
523 std::vector<std::string> argv = {"-v", "", "/bin/sh"};
524
525 // Mode is optional.
526 argv[1] = "-K";
527 ASSERT_TRUE(parse_args_(argv));
528
529 // This should list all valid modes.
530 const std::vector<std::string> modes = {
531 "shared",
532 "private",
533 "slave",
534 "unbindable",
535 };
536
537 for (const auto& mode : modes) {
538 argv[1] = "-K" + mode;
539 ASSERT_TRUE(parse_args_(argv));
540 }
541 }
542
543 // Invalid calls to the remount mode option.
TEST_F(CliTest,invalid_remount_mode)544 TEST_F(CliTest, invalid_remount_mode) {
545 std::vector<std::string> argv = {"-v", "", "/bin/sh"};
546
547 // Unknown mode.
548 argv[1] = "-Kfoo";
549 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
550 }
551
TEST_F(CliTest,invalid_L_combo)552 TEST_F(CliTest, invalid_L_combo) {
553 std::vector<std::string> argv = {"", "", "", "/bin/sh"};
554
555 // Cannot call minijail0 with -L and a pre-compiled seccomp policy.
556 argv[0] = "-L";
557 argv[1] = "--seccomp-bpf-binary";
558 argv[2] = "source";
559 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
560
561 argv[0] = "--seccomp-bpf-binary";
562 argv[1] = "source";
563 argv[2] = "-L";
564 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
565 }
566
567 // Valid calls to the clear env option.
TEST_F(CliTest,valid_clear_env)568 TEST_F(CliTest, valid_clear_env) {
569 std::vector<std::string> argv = {"--env-reset", "/bin/sh"};
570
571 ASSERT_TRUE(parse_args_(argv));
572 }
573
574 // Valid calls to the set env option.
TEST_F(CliTest,valid_set_env)575 TEST_F(CliTest, valid_set_env) {
576 std::vector<std::string> argv1 = {"--env-add", "NAME=value", "/bin/sh"};
577 ASSERT_TRUE(parse_args_(argv1));
578
579 // multiple occurences are allowed.
580 std::vector<std::string> argv2 = {"--env-add", "A=b", "--env-add", "b=C=D",
581 "/bin/sh"};
582 ASSERT_TRUE(parse_args_(argv2));
583
584 // --env-reset before any --env-add to not pass our own env.
585 std::vector<std::string> argv3 = {"--env-reset", "--env-add", "A=b",
586 "/bin/sh"};
587 ASSERT_TRUE(parse_args_(argv3));
588
589 // --env-add before an --env-reset doesn't have any effect, but is allowed.
590 std::vector<std::string> argv4 = {"--env-add", "A=b", "--env-reset",
591 "/bin/sh"};
592 ASSERT_TRUE(parse_args_(argv4));
593 }
594
595 // Invalid calls to the set env options.
TEST_F(CliTest,invalid_set_env)596 TEST_F(CliTest, invalid_set_env) {
597 // invalid env=value arguments.
598 std::vector<std::string> argv2 = {"--env-add", "", "/bin/sh"};
599
600 argv2[1] = "INVALID";
601 ASSERT_EXIT(parse_args_(argv2), testing::ExitedWithCode(1), "");
602
603 argv2[1] = "=";
604 ASSERT_EXIT(parse_args_(argv2), testing::ExitedWithCode(1), "");
605
606 argv2[1] = "=foo";
607 ASSERT_EXIT(parse_args_(argv2), testing::ExitedWithCode(1), "");
608 }
609
610 // Valid calls to the gen-config option.
TEST_F(CliTest,valid_gen_config)611 TEST_F(CliTest, valid_gen_config) {
612 std::string config_path = std::tmpnam(NULL);
613 std::vector<std::string> argv = {"--gen-config=" + config_path, "--ambient",
614 "--fs-path-rx=/", "-n"};
615 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(0), "");
616 }
617
TEST_F(CliTest,valid_gen_config_with_users_mounts_not_found)618 TEST_F(CliTest, valid_gen_config_with_users_mounts_not_found) {
619 std::string config_path = std::tmpnam(NULL);
620 std::vector<std::string> argv = {"--gen-config=" + config_path,
621 "--ambient",
622 "-u fake-user",
623 "-g fake-group",
624 "-b /fake-mount",
625 "-k /fake-mount",
626 "-e /fake-path",
627 "-f /fake-path",
628 "-V /fake-path",
629 "-n"};
630 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(0), "");
631 }
632
633 // Invalid calls to the gen-config option.
TEST_F(CliTest,invalid_gen_config)634 TEST_F(CliTest, invalid_gen_config) {
635 std::vector<std::string> argv = {"--gen-config=/", "--ambient",
636 "--fs-path-rx=/", "-n"};
637 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
638 }
639
TEST_F(CliTest,invalid_gen_config_not_writable)640 TEST_F(CliTest, invalid_gen_config_not_writable) {
641 std::vector<std::string> argv = {"--gen-config=/sys/foo", "--ambient",
642 "--fs-path-rx=/", "-n"};
643 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
644 }
645
646 // Android unit tests do not support data file yet.
647 #if !defined(__ANDROID__)
648
TEST_F(CliTest,conf_parsing_invalid_key)649 TEST_F(CliTest, conf_parsing_invalid_key) {
650 std::vector<std::string> argv = {"--config", source_path("test/invalid.conf"),
651 "/bin/sh"};
652
653 ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), "");
654 }
655
TEST_F(CliTest,conf_parsing)656 TEST_F(CliTest, conf_parsing) {
657 std::vector<std::string> argv = {"--config", source_path("test/valid.conf"),
658 "/bin/sh"};
659
660 ASSERT_TRUE(parse_args_(argv));
661 }
662
TEST_F(CliTest,conf_parsing_seccomp)663 TEST_F(CliTest, conf_parsing_seccomp) {
664 std::string seccomp_path = std::tmpnam(NULL);
665 std::ofstream seccomp_stream(seccomp_path);
666 // Intentionally empty policy.
667 std::string config_path = std::tmpnam(NULL);
668 std::ofstream config_stream(config_path);
669 config_stream << "% minijail-config-file v0\n"
670 "S = " +
671 seccomp_path;
672 config_stream.flush();
673
674 std::vector<std::string> argv = {"--config", config_path, "/bin/sh"};
675
676 ASSERT_TRUE(parse_args_(argv));
677 }
678
TEST_F(CliTest,conf_parsing_with_dac_override)679 TEST_F(CliTest, conf_parsing_with_dac_override) {
680 std::vector<std::string> argv = {"-c 2", "--config",
681 source_path("test/valid.conf"), "/bin/sh"};
682
683 ASSERT_TRUE(parse_args_(argv));
684 }
685
TEST_F(CliTest,conf_fs_path)686 TEST_F(CliTest, conf_fs_path) {
687 std::vector<std::string> argv = {
688 "-c 2", "--config", source_path("test/landlock.conf"), "/bin/sh"};
689
690 ASSERT_TRUE(parse_args_(argv));
691 }
692
693 #endif // !__ANDROID__
694