1 // Copyright 2014 The Chromium 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 #include "sandbox/linux/syscall_broker/broker_file_permission.h"
6
7 #include <fcntl.h>
8 #include <string.h>
9 #include <sys/stat.h>
10 #include <sys/types.h>
11 #include <unistd.h>
12
13 #include "base/check.h"
14 #include "base/macros.h"
15 #include "base/notreached.h"
16 #include "sandbox/linux/tests/test_utils.h"
17 #include "sandbox/linux/tests/unit_tests.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19
20 namespace sandbox {
21
22 namespace syscall_broker {
23
24 class BrokerFilePermissionTester {
25 public:
ValidatePath(const char * path)26 static bool ValidatePath(const char* path) {
27 return BrokerFilePermission::ValidatePath(path);
28 }
GetErrorMessage()29 static const char* GetErrorMessage() {
30 return BrokerFilePermission::GetErrorMessageForTests();
31 }
32
33 private:
34 DISALLOW_COPY_AND_ASSIGN(BrokerFilePermissionTester);
35 };
36
37 namespace {
38
39 // Creation tests are DEATH tests as a bad permission causes termination.
SANDBOX_TEST(BrokerFilePermission,CreateGood)40 SANDBOX_TEST(BrokerFilePermission, CreateGood) {
41 const char kPath[] = "/tmp/good";
42 BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
43 }
44
SANDBOX_TEST(BrokerFilePermission,CreateGoodRecursive)45 SANDBOX_TEST(BrokerFilePermission, CreateGoodRecursive) {
46 const char kPath[] = "/tmp/good/";
47 BrokerFilePermission perm = BrokerFilePermission::ReadOnlyRecursive(kPath);
48 }
49
50 // In official builds, CHECK(x) causes a SIGTRAP on the architectures where the
51 // sanbox is enabled (that are x86, x86_64, arm64 and 32-bit arm processes
52 // running on a arm64 kernel).
53 #if defined(OFFICIAL_BUILD)
54 #define DEATH_BY_CHECK(msg) DEATH_BY_SIGNAL(SIGTRAP)
55 #else
56 #define DEATH_BY_CHECK(msg) DEATH_MESSAGE(msg)
57 #endif
58
SANDBOX_DEATH_TEST(BrokerFilePermission,CreateBad,DEATH_BY_CHECK (BrokerFilePermissionTester::GetErrorMessage ()))59 SANDBOX_DEATH_TEST(
60 BrokerFilePermission,
61 CreateBad,
62 DEATH_BY_CHECK(BrokerFilePermissionTester::GetErrorMessage())) {
63 const char kPath[] = "/tmp/bad/";
64 BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
65 }
66
SANDBOX_DEATH_TEST(BrokerFilePermission,CreateBadRecursive,DEATH_BY_CHECK (BrokerFilePermissionTester::GetErrorMessage ()))67 SANDBOX_DEATH_TEST(
68 BrokerFilePermission,
69 CreateBadRecursive,
70 DEATH_BY_CHECK(BrokerFilePermissionTester::GetErrorMessage())) {
71 const char kPath[] = "/tmp/bad";
72 BrokerFilePermission perm = BrokerFilePermission::ReadOnlyRecursive(kPath);
73 }
74
SANDBOX_DEATH_TEST(BrokerFilePermission,CreateBadNotAbs,DEATH_BY_CHECK (BrokerFilePermissionTester::GetErrorMessage ()))75 SANDBOX_DEATH_TEST(
76 BrokerFilePermission,
77 CreateBadNotAbs,
78 DEATH_BY_CHECK(BrokerFilePermissionTester::GetErrorMessage())) {
79 const char kPath[] = "tmp/bad";
80 BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
81 }
82
SANDBOX_DEATH_TEST(BrokerFilePermission,CreateBadEmpty,DEATH_BY_CHECK (BrokerFilePermissionTester::GetErrorMessage ()))83 SANDBOX_DEATH_TEST(
84 BrokerFilePermission,
85 CreateBadEmpty,
86 DEATH_BY_CHECK(BrokerFilePermissionTester::GetErrorMessage())) {
87 const char kPath[] = "";
88 BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
89 }
90
91 // CheckPerm tests |path| against |perm| given |access_flags|.
92 // If |create| is true then file creation is tested for success.
CheckPerm(const BrokerFilePermission & perm,const char * path,int access_flags,bool create)93 void CheckPerm(const BrokerFilePermission& perm,
94 const char* path,
95 int access_flags,
96 bool create) {
97 const char* file_to_open = NULL;
98
99 ASSERT_FALSE(perm.CheckAccess(path, X_OK, NULL));
100 ASSERT_TRUE(perm.CheckAccess(path, F_OK, NULL));
101 // check bad perms
102 switch (access_flags) {
103 case O_RDONLY:
104 ASSERT_TRUE(perm.CheckOpen(path, O_RDONLY, &file_to_open, NULL));
105 ASSERT_FALSE(perm.CheckOpen(path, O_WRONLY, &file_to_open, NULL));
106 ASSERT_FALSE(perm.CheckOpen(path, O_RDWR, &file_to_open, NULL));
107 ASSERT_TRUE(perm.CheckAccess(path, R_OK, NULL));
108 ASSERT_FALSE(perm.CheckAccess(path, W_OK, NULL));
109 break;
110 case O_WRONLY:
111 ASSERT_FALSE(perm.CheckOpen(path, O_RDONLY, &file_to_open, NULL));
112 ASSERT_TRUE(perm.CheckOpen(path, O_WRONLY, &file_to_open, NULL));
113 ASSERT_FALSE(perm.CheckOpen(path, O_RDWR, &file_to_open, NULL));
114 ASSERT_FALSE(perm.CheckAccess(path, R_OK, NULL));
115 ASSERT_TRUE(perm.CheckAccess(path, W_OK, NULL));
116 break;
117 case O_RDWR:
118 ASSERT_TRUE(perm.CheckOpen(path, O_RDONLY, &file_to_open, NULL));
119 ASSERT_TRUE(perm.CheckOpen(path, O_WRONLY, &file_to_open, NULL));
120 ASSERT_TRUE(perm.CheckOpen(path, O_RDWR, &file_to_open, NULL));
121 ASSERT_TRUE(perm.CheckAccess(path, R_OK, NULL));
122 ASSERT_TRUE(perm.CheckAccess(path, W_OK, NULL));
123 break;
124 default:
125 // Bad test case
126 NOTREACHED();
127 }
128
129 // O_SYNC can be defined as (__O_SYNC|O_DSYNC)
130 #ifdef O_DSYNC
131 const int kSyncFlag = O_SYNC & ~O_DSYNC;
132 #else
133 const int kSyncFlag = O_SYNC;
134 #endif
135
136 const int kNumberOfBitsInOAccMode = 2;
137 static_assert(O_ACCMODE == ((1 << kNumberOfBitsInOAccMode) - 1),
138 "incorrect number of bits");
139 // check every possible flag and act accordingly.
140 // Skipping AccMode bits as they are present in every case.
141 for (int i = kNumberOfBitsInOAccMode; i < 32; i++) {
142 int flag = 1 << i;
143 switch (flag) {
144 case O_APPEND:
145 case O_ASYNC:
146 case O_DIRECT:
147 case O_DIRECTORY:
148 #ifdef O_DSYNC
149 case O_DSYNC:
150 #endif
151 case O_EXCL:
152 case O_LARGEFILE:
153 case O_NOATIME:
154 case O_NOCTTY:
155 case O_NOFOLLOW:
156 case O_NONBLOCK:
157 #if (O_NONBLOCK != O_NDELAY)
158 case O_NDELAY:
159 #endif
160 case kSyncFlag:
161 ASSERT_TRUE(
162 perm.CheckOpen(path, access_flags | flag, &file_to_open, NULL));
163 break;
164 case O_TRUNC: {
165 // The effect of (O_RDONLY | O_TRUNC) is undefined, and in some cases it
166 // actually truncates, so deny.
167 bool result =
168 perm.CheckOpen(path, access_flags | flag, &file_to_open, NULL);
169 if (access_flags == O_RDONLY) {
170 ASSERT_FALSE(result);
171 } else {
172 ASSERT_TRUE(result);
173 }
174 break;
175 }
176 case O_CREAT:
177 continue; // Handled below.
178 case O_CLOEXEC:
179 default:
180 ASSERT_FALSE(
181 perm.CheckOpen(path, access_flags | flag, &file_to_open, NULL));
182 }
183 }
184 if (create) {
185 bool unlink;
186 ASSERT_TRUE(perm.CheckOpen(path, O_CREAT | O_EXCL | access_flags,
187 &file_to_open, &unlink));
188 ASSERT_FALSE(unlink);
189 } else {
190 ASSERT_FALSE(perm.CheckOpen(path, O_CREAT | O_EXCL | access_flags,
191 &file_to_open, NULL));
192 }
193 }
194
TEST(BrokerFilePermission,ReadOnly)195 TEST(BrokerFilePermission, ReadOnly) {
196 const char kPath[] = "/tmp/good";
197 BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
198 CheckPerm(perm, kPath, O_RDONLY, false);
199 // Don't do anything here, so that ASSERT works in the subfunction as
200 // expected.
201 }
202
TEST(BrokerFilePermission,ReadOnlyRecursive)203 TEST(BrokerFilePermission, ReadOnlyRecursive) {
204 const char kPath[] = "/tmp/good/";
205 const char kPathFile[] = "/tmp/good/file";
206 BrokerFilePermission perm = BrokerFilePermission::ReadOnlyRecursive(kPath);
207 CheckPerm(perm, kPathFile, O_RDONLY, false);
208 // Don't do anything here, so that ASSERT works in the subfunction as
209 // expected.
210 }
211
212 // Explicit test for O_RDONLY|O_TRUNC, which should be denied due to
213 // undefined behavior.
TEST(BrokerFilePermission,ReadOnlyTruncate)214 TEST(BrokerFilePermission, ReadOnlyTruncate) {
215 const char kPath[] = "/tmp/good";
216 BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
217 ASSERT_FALSE(perm.CheckOpen(kPath, O_RDONLY | O_TRUNC, nullptr, nullptr));
218 }
219
TEST(BrokerFilePermission,WriteOnly)220 TEST(BrokerFilePermission, WriteOnly) {
221 const char kPath[] = "/tmp/good";
222 BrokerFilePermission perm = BrokerFilePermission::WriteOnly(kPath);
223 CheckPerm(perm, kPath, O_WRONLY, false);
224 // Don't do anything here, so that ASSERT works in the subfunction as
225 // expected.
226 }
227
TEST(BrokerFilePermission,ReadWrite)228 TEST(BrokerFilePermission, ReadWrite) {
229 const char kPath[] = "/tmp/good";
230 BrokerFilePermission perm = BrokerFilePermission::ReadWrite(kPath);
231 CheckPerm(perm, kPath, O_RDWR, false);
232 // Don't do anything here, so that ASSERT works in the subfunction as
233 // expected.
234 }
235
TEST(BrokerFilePermission,ReadWriteCreate)236 TEST(BrokerFilePermission, ReadWriteCreate) {
237 const char kPath[] = "/tmp/good";
238 BrokerFilePermission perm = BrokerFilePermission::ReadWriteCreate(kPath);
239 CheckPerm(perm, kPath, O_RDWR, true);
240 // Don't do anything here, so that ASSERT works in the subfunction as
241 // expected.
242 }
243
CheckUnlink(BrokerFilePermission & perm,const char * path,int access_flags)244 void CheckUnlink(BrokerFilePermission& perm,
245 const char* path,
246 int access_flags) {
247 bool unlink;
248 ASSERT_FALSE(perm.CheckOpen(path, access_flags, NULL, &unlink));
249 ASSERT_TRUE(
250 perm.CheckOpen(path, access_flags | O_CREAT | O_EXCL, NULL, &unlink));
251 ASSERT_TRUE(unlink);
252 }
253
TEST(BrokerFilePermission,ReadWriteCreateTemporaryRecursive)254 TEST(BrokerFilePermission, ReadWriteCreateTemporaryRecursive) {
255 const char kPath[] = "/tmp/good/";
256 const char kPathFile[] = "/tmp/good/file";
257 BrokerFilePermission perm =
258 BrokerFilePermission::ReadWriteCreateTemporaryRecursive(kPath);
259 CheckUnlink(perm, kPathFile, O_RDWR);
260 // Don't do anything here, so that ASSERT works in the subfunction as
261 // expected.
262 }
263
TEST(BrokerFilePermission,StatOnlyWithIntermediateDirs)264 TEST(BrokerFilePermission, StatOnlyWithIntermediateDirs) {
265 const char kPath[] = "/tmp/good/path";
266 const char kLeading1[] = "/";
267 const char kLeading2[] = "/tmp";
268 const char kLeading3[] = "/tmp/good/path";
269 const char kTrailing[] = "/tmp/good/path/bad";
270
271 BrokerFilePermission perm =
272 BrokerFilePermission::StatOnlyWithIntermediateDirs(kPath);
273 // No open or access permission.
274 ASSERT_FALSE(perm.CheckOpen(kPath, O_RDONLY, NULL, NULL));
275 ASSERT_FALSE(perm.CheckOpen(kPath, O_WRONLY, NULL, NULL));
276 ASSERT_FALSE(perm.CheckOpen(kPath, O_RDWR, NULL, NULL));
277 ASSERT_FALSE(perm.CheckAccess(kPath, R_OK, NULL));
278 ASSERT_FALSE(perm.CheckAccess(kPath, W_OK, NULL));
279
280 // Stat for all leading paths, but not trailing paths.
281 ASSERT_TRUE(perm.CheckStat(kPath, NULL));
282 ASSERT_TRUE(perm.CheckStat(kLeading1, NULL));
283 ASSERT_TRUE(perm.CheckStat(kLeading2, NULL));
284 ASSERT_TRUE(perm.CheckStat(kLeading3, NULL));
285 ASSERT_FALSE(perm.CheckStat(kTrailing, NULL));
286 }
287
TEST(BrokerFilePermission,ValidatePath)288 TEST(BrokerFilePermission, ValidatePath) {
289 EXPECT_TRUE(BrokerFilePermissionTester::ValidatePath("/path"));
290 EXPECT_TRUE(BrokerFilePermissionTester::ValidatePath("/"));
291 EXPECT_TRUE(BrokerFilePermissionTester::ValidatePath("/..path"));
292
293 EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath(""));
294 EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("bad"));
295 EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("/bad/"));
296 EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("bad/"));
297 EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("/bad/.."));
298 EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("/bad/../bad"));
299 EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("/../bad"));
300 }
301
302 } // namespace
303
304 } // namespace syscall_broker
305
306 } // namespace sandbox
307