blob: e0d4d2ba1a9adc8e749f23ec13e0de101ea5cfdd [file] [log] [blame]
Mike Frysinger50e31fa2018-01-19 18:59:49 -05001/* Copyright 2017 The Chromium OS Authors. All rights reserved.
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 system.[ch] module code using gtest.
6 */
Mike Frysinger0b5cffa2017-08-15 18:06:18 -04007
yusukes76a9d742018-03-05 10:20:22 -08008#include <limits.h>
Jorge Lucangeli Obes54234212018-04-26 11:52:15 -04009#include <linux/securebits.h>
Mike Frysinger0b5cffa2017-08-15 18:06:18 -040010#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
Mike Frysinger5fdba4e2018-01-17 15:39:48 -050013#include <sys/stat.h>
Mike Frysinger0b5cffa2017-08-15 18:06:18 -040014#include <unistd.h>
15
16#include <gtest/gtest.h>
17
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -070018#include <string>
19
Mike Frysinger0b5cffa2017-08-15 18:06:18 -040020#include "system.h"
21
22namespace {
23
24// A random path that really really should not exist on the host.
25const char kNoSuchDir[] = "/.x/..x/...x/path/should/not/exist/";
26
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -040027// A random file that should exist on both Linux and Android.
28const char kValidFile[] = "/etc/hosts";
Mike Frysingereaab4202017-08-14 14:57:21 -040029
30// A random directory that should exist.
31const char kValidDir[] = "/";
32
33// A random character device that should exist.
34const char kValidCharDev[] = "/dev/null";
35
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -040036inline bool is_android() {
37#if defined(__ANDROID__)
38 return true;
39#else
40 return false;
41#endif
42}
43
44// Return a temp filename that this test can manipulate.
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -070045// It will not exist when it returns.
46std::string get_temp_path() {
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -040047 char *path = nullptr;
48 if (is_android()) {
49 path = strdup("/data/local/tmp/minijail.tests.XXXXXX");
50 } else {
51 path = strdup("minijail.tests.XXXXXX");
52 }
53
Mike Frysinger0b5cffa2017-08-15 18:06:18 -040054 if (!path)
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -070055 return std::string();
Mike Frysinger0b5cffa2017-08-15 18:06:18 -040056
57 // Just create the temp path.
58 int fd = mkstemp(path);
59 if (fd < 0)
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -070060 return std::string();
Mike Frysinger0b5cffa2017-08-15 18:06:18 -040061 close(fd);
62 unlink(path);
63
64 return path;
65}
66
67} // namespace
68
Jorge Lucangeli Obes54234212018-04-26 11:52:15 -040069TEST(secure_noroot_set_and_locked, zero_mask) {
70 ASSERT_EQ(secure_noroot_set_and_locked(0), 0);
71}
72
73TEST(secure_noroot_set_and_locked, set) {
74 ASSERT_EQ(secure_noroot_set_and_locked(issecure_mask(SECURE_NOROOT) |
75 issecure_mask(SECURE_NOROOT_LOCKED)),
76 1);
77}
78
79TEST(secure_noroot_set_and_locked, not_set) {
80 ASSERT_EQ(secure_noroot_set_and_locked(issecure_mask(SECURE_KEEP_CAPS) |
81 issecure_mask(SECURE_NOROOT_LOCKED)),
82 0);
83}
84
Mike Frysinger0b5cffa2017-08-15 18:06:18 -040085// Sanity check for the cap range.
86TEST(get_last_valid_cap, basic) {
87 unsigned int cap = get_last_valid_cap();
88
89 // We pick 35 as it's been that since at least v3.0.
90 // If this test is run on older kernels, it might fail.
91 EXPECT_GE(cap, 35u);
92
93 // Pick a really large number that we probably won't hit for a long time.
94 // It helps that caps are bitfields.
95 EXPECT_LT(cap, 128u);
96}
97
98// Might be useful to figure out the return value, but for now,
99// just make sure it doesn't crash?
100TEST(cap_ambient_supported, smoke) {
101 cap_ambient_supported();
102}
103
104// Invalid indexes should return errors, not crash.
105TEST(setup_pipe_end, bad_index) {
106 EXPECT_LT(setup_pipe_end(nullptr, 2), 0);
107 EXPECT_LT(setup_pipe_end(nullptr, 3), 0);
108 EXPECT_LT(setup_pipe_end(nullptr, 4), 0);
109}
110
111// Verify getting the first fd works.
112TEST(setup_pipe_end, index0) {
113 int fds[2];
114 EXPECT_EQ(0, pipe(fds));
115 // This should close fds[1] and return fds[0].
116 EXPECT_EQ(fds[0], setup_pipe_end(fds, 0));
117 // Use close() to verify open/close state.
118 EXPECT_EQ(-1, close(fds[1]));
119 EXPECT_EQ(0, close(fds[0]));
120}
121
122// Verify getting the second fd works.
123TEST(setup_pipe_end, index1) {
124 int fds[2];
125 EXPECT_EQ(0, pipe(fds));
126 // This should close fds[0] and return fds[1].
127 EXPECT_EQ(fds[1], setup_pipe_end(fds, 1));
128 // Use close() to verify open/close state.
129 EXPECT_EQ(-1, close(fds[0]));
130 EXPECT_EQ(0, close(fds[1]));
131}
132
133// Invalid indexes should return errors, not crash.
134TEST(setup_and_dupe_pipe_end, bad_index) {
135 EXPECT_LT(setup_and_dupe_pipe_end(nullptr, 2, -1), 0);
136 EXPECT_LT(setup_and_dupe_pipe_end(nullptr, 3, -1), 0);
137 EXPECT_LT(setup_and_dupe_pipe_end(nullptr, 4, -1), 0);
138}
139
140// An invalid path should return an error.
141TEST(write_pid_to_path, bad_path) {
142 EXPECT_NE(0, write_pid_to_path(0, kNoSuchDir));
143}
144
145// Make sure we can write a pid to the file.
146TEST(write_pid_to_path, basic) {
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700147 std::string path = get_temp_path();
148 ASSERT_NE(std::string(), path);
Mike Frysinger0b5cffa2017-08-15 18:06:18 -0400149
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700150 EXPECT_EQ(0, write_pid_to_path(1234, path.c_str()));
151 FILE *fp = fopen(path.c_str(), "re");
152 unlink(path.c_str());
Mike Frysinger0b5cffa2017-08-15 18:06:18 -0400153 EXPECT_NE(nullptr, fp);
lhchavez24b64c22017-09-01 03:52:13 +0000154 char data[6] = {};
Mike Frysinger0b5cffa2017-08-15 18:06:18 -0400155 EXPECT_EQ(5u, fread(data, 1, sizeof(data), fp));
156 fclose(fp);
157 EXPECT_EQ(0, strcmp(data, "1234\n"));
Mike Frysingereaab4202017-08-14 14:57:21 -0400158}
159
160// If the destination exists, there's nothing to do.
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500161// Also check trailing slash handling.
162TEST(mkdir_p, dest_exists) {
163 EXPECT_EQ(0, mkdir_p("/", 0, true));
164 EXPECT_EQ(0, mkdir_p("///", 0, true));
165 EXPECT_EQ(0, mkdir_p("/proc", 0, true));
166 EXPECT_EQ(0, mkdir_p("/proc/", 0, true));
167 EXPECT_EQ(0, mkdir_p("/dev", 0, true));
168 EXPECT_EQ(0, mkdir_p("/dev/", 0, true));
169}
170
171// Create a directory tree that doesn't exist.
172TEST(mkdir_p, create_tree) {
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700173 std::string path = get_temp_path();
174 ASSERT_NE(std::string(), path);
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500175
176 // Run `mkdir -p <path>/a/b/c`.
177 char *path_a, *path_a_b, *path_a_b_c;
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700178 ASSERT_NE(-1, asprintf(&path_a, "%s/a", path.c_str()));
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500179 ASSERT_NE(-1, asprintf(&path_a_b, "%s/b", path_a));
180 ASSERT_NE(-1, asprintf(&path_a_b_c, "%s/c", path_a_b));
181
182 // First try creating it as a file.
183 EXPECT_EQ(0, mkdir_p(path_a_b_c, 0700, false));
184
185 // Make sure the final path doesn't exist yet.
186 struct stat st;
187 EXPECT_EQ(0, stat(path_a_b, &st));
188 EXPECT_EQ(true, S_ISDIR(st.st_mode));
189 EXPECT_EQ(-1, stat(path_a_b_c, &st));
190
191 // Then create it as a complete dir.
192 EXPECT_EQ(0, mkdir_p(path_a_b_c, 0700, true));
193
194 // Make sure the final dir actually exists.
195 EXPECT_EQ(0, stat(path_a_b_c, &st));
196 EXPECT_EQ(true, S_ISDIR(st.st_mode));
197
198 // Clean up.
199 ASSERT_EQ(0, rmdir(path_a_b_c));
200 ASSERT_EQ(0, rmdir(path_a_b));
201 ASSERT_EQ(0, rmdir(path_a));
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700202 ASSERT_EQ(0, rmdir(path.c_str()));
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500203
204 free(path_a_b_c);
205 free(path_a_b);
206 free(path_a);
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500207}
208
209// If the destination exists, there's nothing to do.
Mike Frysingereaab4202017-08-14 14:57:21 -0400210TEST(setup_mount_destination, dest_exists) {
211 // Pick some paths that should always exist. We pass in invalid pointers
212 // for other args so we crash if the dest check doesn't short circuit.
213 EXPECT_EQ(0, setup_mount_destination(nullptr, kValidDir, 0, 0, false));
214 EXPECT_EQ(0, setup_mount_destination(nullptr, "/proc", 0, 0, true));
215 EXPECT_EQ(0, setup_mount_destination(nullptr, "/dev", 0, 0, false));
216}
217
218// When given a bind mount where the source is relative, reject it.
219TEST(setup_mount_destination, reject_relative_bind) {
220 // Pick a destination we know doesn't exist.
221 EXPECT_NE(0, setup_mount_destination("foo", kNoSuchDir, 0, 0, true));
222}
223
224// A mount of a pseudo filesystem should make the destination dir.
225TEST(setup_mount_destination, create_pseudo_fs) {
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700226 std::string path = get_temp_path();
227 ASSERT_NE(std::string(), path);
Mike Frysingereaab4202017-08-14 14:57:21 -0400228
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -0400229 // Passing -1 for user ID/group ID tells chown to make no changes.
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700230 EXPECT_EQ(0, setup_mount_destination("none", path.c_str(), -1, -1, false));
Mike Frysingereaab4202017-08-14 14:57:21 -0400231 // We check it's a directory by deleting it as such.
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700232 EXPECT_EQ(0, rmdir(path.c_str()));
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -0400233
234 // Confirm that a bad user ID/group ID fails the function as expected.
235 // On Android, Bionic manages user IDs directly: there is no /etc/passwd file.
236 // This results in most user IDs being valid. Instead of trying to find an
237 // invalid user ID, just skip this check.
238 if (!is_android()) {
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700239 EXPECT_NE(0, setup_mount_destination("none", path.c_str(),
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -0400240 UINT_MAX / 2, UINT_MAX / 2, false));
241 }
Mike Frysingereaab4202017-08-14 14:57:21 -0400242}
243
244// If the source path does not exist, we should error out.
245TEST(setup_mount_destination, missing_source) {
246 // The missing dest path is so we can exercise the source logic.
247 EXPECT_NE(0, setup_mount_destination(kNoSuchDir, kNoSuchDir, 0, 0, false));
248 EXPECT_NE(0, setup_mount_destination(kNoSuchDir, kNoSuchDir, 0, 0, true));
249}
250
251// A bind mount of a directory should create the destination dir.
252TEST(setup_mount_destination, create_bind_dir) {
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700253 std::string path = get_temp_path();
254 ASSERT_NE(std::string(), path);
Mike Frysingereaab4202017-08-14 14:57:21 -0400255
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -0400256 // Passing -1 for user ID/group ID tells chown to make no changes.
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700257 EXPECT_EQ(0, setup_mount_destination(kValidDir, path.c_str(), -1, -1, true));
Mike Frysingereaab4202017-08-14 14:57:21 -0400258 // We check it's a directory by deleting it as such.
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700259 EXPECT_EQ(0, rmdir(path.c_str()));
Mike Frysingereaab4202017-08-14 14:57:21 -0400260}
261
262// A bind mount of a file should create the destination file.
263TEST(setup_mount_destination, create_bind_file) {
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700264 std::string path = get_temp_path();
265 ASSERT_NE(std::string(), path);
Mike Frysingereaab4202017-08-14 14:57:21 -0400266
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -0400267 // Passing -1 for user ID/group ID tells chown to make no changes.
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700268 EXPECT_EQ(0, setup_mount_destination(kValidFile, path.c_str(), -1, -1, true));
Mike Frysingereaab4202017-08-14 14:57:21 -0400269 // We check it's a file by deleting it as such.
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700270 EXPECT_EQ(0, unlink(path.c_str()));
Mike Frysingereaab4202017-08-14 14:57:21 -0400271}
272
273// A mount of a character device should create the destination char.
274TEST(setup_mount_destination, create_char_dev) {
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700275 std::string path = get_temp_path();
276 ASSERT_NE(std::string(), path);
Mike Frysingereaab4202017-08-14 14:57:21 -0400277
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -0400278 // Passing -1 for user ID/group ID tells chown to make no changes.
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700279 EXPECT_EQ(0, setup_mount_destination(kValidCharDev, path.c_str(), -1, -1, false));
Mike Frysingereaab4202017-08-14 14:57:21 -0400280 // We check it's a directory by deleting it as such.
Luis Hector Chavez6bdebb02018-07-10 19:31:27 -0700281 EXPECT_EQ(0, rmdir(path.c_str()));
Mike Frysinger0b5cffa2017-08-15 18:06:18 -0400282}