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