blob: 8d2a87376caf5249eaa39f24408d0e1c86d2d9c0 [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
18#include "system.h"
19
20namespace {
21
22// A random path that really really should not exist on the host.
23const char kNoSuchDir[] = "/.x/..x/...x/path/should/not/exist/";
24
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -040025// A random file that should exist on both Linux and Android.
26const char kValidFile[] = "/etc/hosts";
Mike Frysingereaab4202017-08-14 14:57:21 -040027
28// A random directory that should exist.
29const char kValidDir[] = "/";
30
31// A random character device that should exist.
32const char kValidCharDev[] = "/dev/null";
33
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -040034inline bool is_android() {
35#if defined(__ANDROID__)
36 return true;
37#else
38 return false;
39#endif
40}
41
42// Return a temp filename that this test can manipulate.
Mike Frysinger0b5cffa2017-08-15 18:06:18 -040043// It will not exist when it returns, and the user has to free the memory.
44char *get_temp_path() {
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -040045 char *path = nullptr;
46 if (is_android()) {
47 path = strdup("/data/local/tmp/minijail.tests.XXXXXX");
48 } else {
49 path = strdup("minijail.tests.XXXXXX");
50 }
51
Mike Frysinger0b5cffa2017-08-15 18:06:18 -040052 if (!path)
53 return nullptr;
54
55 // Just create the temp path.
56 int fd = mkstemp(path);
57 if (fd < 0)
58 return nullptr;
59 close(fd);
60 unlink(path);
61
62 return path;
63}
64
65} // namespace
66
Jorge Lucangeli Obes54234212018-04-26 11:52:15 -040067TEST(secure_noroot_set_and_locked, zero_mask) {
68 ASSERT_EQ(secure_noroot_set_and_locked(0), 0);
69}
70
71TEST(secure_noroot_set_and_locked, set) {
72 ASSERT_EQ(secure_noroot_set_and_locked(issecure_mask(SECURE_NOROOT) |
73 issecure_mask(SECURE_NOROOT_LOCKED)),
74 1);
75}
76
77TEST(secure_noroot_set_and_locked, not_set) {
78 ASSERT_EQ(secure_noroot_set_and_locked(issecure_mask(SECURE_KEEP_CAPS) |
79 issecure_mask(SECURE_NOROOT_LOCKED)),
80 0);
81}
82
Mike Frysinger0b5cffa2017-08-15 18:06:18 -040083// Sanity check for the cap range.
84TEST(get_last_valid_cap, basic) {
85 unsigned int cap = get_last_valid_cap();
86
87 // We pick 35 as it's been that since at least v3.0.
88 // If this test is run on older kernels, it might fail.
89 EXPECT_GE(cap, 35u);
90
91 // Pick a really large number that we probably won't hit for a long time.
92 // It helps that caps are bitfields.
93 EXPECT_LT(cap, 128u);
94}
95
96// Might be useful to figure out the return value, but for now,
97// just make sure it doesn't crash?
98TEST(cap_ambient_supported, smoke) {
99 cap_ambient_supported();
100}
101
102// Invalid indexes should return errors, not crash.
103TEST(setup_pipe_end, bad_index) {
104 EXPECT_LT(setup_pipe_end(nullptr, 2), 0);
105 EXPECT_LT(setup_pipe_end(nullptr, 3), 0);
106 EXPECT_LT(setup_pipe_end(nullptr, 4), 0);
107}
108
109// Verify getting the first fd works.
110TEST(setup_pipe_end, index0) {
111 int fds[2];
112 EXPECT_EQ(0, pipe(fds));
113 // This should close fds[1] and return fds[0].
114 EXPECT_EQ(fds[0], setup_pipe_end(fds, 0));
115 // Use close() to verify open/close state.
116 EXPECT_EQ(-1, close(fds[1]));
117 EXPECT_EQ(0, close(fds[0]));
118}
119
120// Verify getting the second fd works.
121TEST(setup_pipe_end, index1) {
122 int fds[2];
123 EXPECT_EQ(0, pipe(fds));
124 // This should close fds[0] and return fds[1].
125 EXPECT_EQ(fds[1], setup_pipe_end(fds, 1));
126 // Use close() to verify open/close state.
127 EXPECT_EQ(-1, close(fds[0]));
128 EXPECT_EQ(0, close(fds[1]));
129}
130
131// Invalid indexes should return errors, not crash.
132TEST(setup_and_dupe_pipe_end, bad_index) {
133 EXPECT_LT(setup_and_dupe_pipe_end(nullptr, 2, -1), 0);
134 EXPECT_LT(setup_and_dupe_pipe_end(nullptr, 3, -1), 0);
135 EXPECT_LT(setup_and_dupe_pipe_end(nullptr, 4, -1), 0);
136}
137
138// An invalid path should return an error.
139TEST(write_pid_to_path, bad_path) {
140 EXPECT_NE(0, write_pid_to_path(0, kNoSuchDir));
141}
142
143// Make sure we can write a pid to the file.
144TEST(write_pid_to_path, basic) {
145 char *path = get_temp_path();
146 ASSERT_NE(nullptr, path);
147
148 EXPECT_EQ(0, write_pid_to_path(1234, path));
149 FILE *fp = fopen(path, "re");
150 unlink(path);
151 EXPECT_NE(nullptr, fp);
lhchavez24b64c22017-09-01 03:52:13 +0000152 char data[6] = {};
Mike Frysinger0b5cffa2017-08-15 18:06:18 -0400153 EXPECT_EQ(5u, fread(data, 1, sizeof(data), fp));
154 fclose(fp);
155 EXPECT_EQ(0, strcmp(data, "1234\n"));
Mike Frysingereaab4202017-08-14 14:57:21 -0400156
157 free(path);
158}
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) {
173 char *path = get_temp_path();
174 ASSERT_NE(nullptr, path);
175 unlink(path);
176
177 // Run `mkdir -p <path>/a/b/c`.
178 char *path_a, *path_a_b, *path_a_b_c;
179 ASSERT_NE(-1, asprintf(&path_a, "%s/a", path));
180 ASSERT_NE(-1, asprintf(&path_a_b, "%s/b", path_a));
181 ASSERT_NE(-1, asprintf(&path_a_b_c, "%s/c", path_a_b));
182
183 // First try creating it as a file.
184 EXPECT_EQ(0, mkdir_p(path_a_b_c, 0700, false));
185
186 // Make sure the final path doesn't exist yet.
187 struct stat st;
188 EXPECT_EQ(0, stat(path_a_b, &st));
189 EXPECT_EQ(true, S_ISDIR(st.st_mode));
190 EXPECT_EQ(-1, stat(path_a_b_c, &st));
191
192 // Then create it as a complete dir.
193 EXPECT_EQ(0, mkdir_p(path_a_b_c, 0700, true));
194
195 // Make sure the final dir actually exists.
196 EXPECT_EQ(0, stat(path_a_b_c, &st));
197 EXPECT_EQ(true, S_ISDIR(st.st_mode));
198
199 // Clean up.
200 ASSERT_EQ(0, rmdir(path_a_b_c));
201 ASSERT_EQ(0, rmdir(path_a_b));
202 ASSERT_EQ(0, rmdir(path_a));
203 ASSERT_EQ(0, rmdir(path));
204
205 free(path_a_b_c);
206 free(path_a_b);
207 free(path_a);
208 free(path);
209}
210
211// If the destination exists, there's nothing to do.
Mike Frysingereaab4202017-08-14 14:57:21 -0400212TEST(setup_mount_destination, dest_exists) {
213 // Pick some paths that should always exist. We pass in invalid pointers
214 // for other args so we crash if the dest check doesn't short circuit.
215 EXPECT_EQ(0, setup_mount_destination(nullptr, kValidDir, 0, 0, false));
216 EXPECT_EQ(0, setup_mount_destination(nullptr, "/proc", 0, 0, true));
217 EXPECT_EQ(0, setup_mount_destination(nullptr, "/dev", 0, 0, false));
218}
219
220// When given a bind mount where the source is relative, reject it.
221TEST(setup_mount_destination, reject_relative_bind) {
222 // Pick a destination we know doesn't exist.
223 EXPECT_NE(0, setup_mount_destination("foo", kNoSuchDir, 0, 0, true));
224}
225
226// A mount of a pseudo filesystem should make the destination dir.
227TEST(setup_mount_destination, create_pseudo_fs) {
228 char *path = get_temp_path();
229 ASSERT_NE(nullptr, path);
230
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -0400231 // Passing -1 for user ID/group ID tells chown to make no changes.
Mike Frysingereaab4202017-08-14 14:57:21 -0400232 EXPECT_EQ(0, setup_mount_destination("none", path, -1, -1, false));
233 // We check it's a directory by deleting it as such.
234 EXPECT_EQ(0, rmdir(path));
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -0400235
236 // Confirm that a bad user ID/group ID fails the function as expected.
237 // On Android, Bionic manages user IDs directly: there is no /etc/passwd file.
238 // This results in most user IDs being valid. Instead of trying to find an
239 // invalid user ID, just skip this check.
240 if (!is_android()) {
241 EXPECT_NE(0, setup_mount_destination("none", path,
242 UINT_MAX / 2, UINT_MAX / 2, false));
243 }
Mike Frysingereaab4202017-08-14 14:57:21 -0400244
245 free(path);
246}
247
248// If the source path does not exist, we should error out.
249TEST(setup_mount_destination, missing_source) {
250 // The missing dest path is so we can exercise the source logic.
251 EXPECT_NE(0, setup_mount_destination(kNoSuchDir, kNoSuchDir, 0, 0, false));
252 EXPECT_NE(0, setup_mount_destination(kNoSuchDir, kNoSuchDir, 0, 0, true));
253}
254
255// A bind mount of a directory should create the destination dir.
256TEST(setup_mount_destination, create_bind_dir) {
257 char *path = get_temp_path();
258 ASSERT_NE(nullptr, path);
259
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -0400260 // Passing -1 for user ID/group ID tells chown to make no changes.
Mike Frysingereaab4202017-08-14 14:57:21 -0400261 EXPECT_EQ(0, setup_mount_destination(kValidDir, path, -1, -1, true));
262 // We check it's a directory by deleting it as such.
263 EXPECT_EQ(0, rmdir(path));
264
265 free(path);
266}
267
268// A bind mount of a file should create the destination file.
269TEST(setup_mount_destination, create_bind_file) {
270 char *path = get_temp_path();
271 ASSERT_NE(nullptr, path);
272
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -0400273 // Passing -1 for user ID/group ID tells chown to make no changes.
Mike Frysingereaab4202017-08-14 14:57:21 -0400274 EXPECT_EQ(0, setup_mount_destination(kValidFile, path, -1, -1, true));
275 // We check it's a file by deleting it as such.
276 EXPECT_EQ(0, unlink(path));
277
278 free(path);
279}
280
281// A mount of a character device should create the destination char.
282TEST(setup_mount_destination, create_char_dev) {
283 char *path = get_temp_path();
284 ASSERT_NE(nullptr, path);
285
Jorge Lucangeli Obes1fc05182018-04-05 10:17:01 -0400286 // Passing -1 for user ID/group ID tells chown to make no changes.
Mike Frysingereaab4202017-08-14 14:57:21 -0400287 EXPECT_EQ(0, setup_mount_destination(kValidCharDev, path, -1, -1, false));
288 // We check it's a directory by deleting it as such.
289 EXPECT_EQ(0, rmdir(path));
290
291 free(path);
Mike Frysinger0b5cffa2017-08-15 18:06:18 -0400292}