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/logging.h"
14 #include "base/macros.h"
15 #include "sandbox/linux/tests/test_utils.h"
16 #include "sandbox/linux/tests/unit_tests.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18
19 namespace sandbox {
20
21 namespace syscall_broker {
22
23 class BrokerFilePermissionTester {
24 public:
ValidatePath(const char * path)25 static bool ValidatePath(const char* path) {
26 return BrokerFilePermission::ValidatePath(path);
27 }
GetErrorMessage()28 static const char* GetErrorMessage() {
29 return BrokerFilePermission::GetErrorMessageForTests();
30 }
31
32 private:
33 DISALLOW_COPY_AND_ASSIGN(BrokerFilePermissionTester);
34 };
35
36 namespace {
37
38 // Creation tests are DEATH tests as a bad permission causes termination.
SANDBOX_TEST(BrokerFilePermission,CreateGood)39 SANDBOX_TEST(BrokerFilePermission, CreateGood) {
40 const char kPath[] = "/tmp/good";
41 BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
42 }
43
SANDBOX_TEST(BrokerFilePermission,CreateGoodRecursive)44 SANDBOX_TEST(BrokerFilePermission, CreateGoodRecursive) {
45 const char kPath[] = "/tmp/good/";
46 BrokerFilePermission perm = BrokerFilePermission::ReadOnlyRecursive(kPath);
47 }
48
SANDBOX_DEATH_TEST(BrokerFilePermission,CreateBad,DEATH_MESSAGE (BrokerFilePermissionTester::GetErrorMessage ()))49 SANDBOX_DEATH_TEST(
50 BrokerFilePermission,
51 CreateBad,
52 DEATH_MESSAGE(BrokerFilePermissionTester::GetErrorMessage())) {
53 const char kPath[] = "/tmp/bad/";
54 BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
55 }
56
SANDBOX_DEATH_TEST(BrokerFilePermission,CreateBadRecursive,DEATH_MESSAGE (BrokerFilePermissionTester::GetErrorMessage ()))57 SANDBOX_DEATH_TEST(
58 BrokerFilePermission,
59 CreateBadRecursive,
60 DEATH_MESSAGE(BrokerFilePermissionTester::GetErrorMessage())) {
61 const char kPath[] = "/tmp/bad";
62 BrokerFilePermission perm = BrokerFilePermission::ReadOnlyRecursive(kPath);
63 }
64
SANDBOX_DEATH_TEST(BrokerFilePermission,CreateBadNotAbs,DEATH_MESSAGE (BrokerFilePermissionTester::GetErrorMessage ()))65 SANDBOX_DEATH_TEST(
66 BrokerFilePermission,
67 CreateBadNotAbs,
68 DEATH_MESSAGE(BrokerFilePermissionTester::GetErrorMessage())) {
69 const char kPath[] = "tmp/bad";
70 BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
71 }
72
SANDBOX_DEATH_TEST(BrokerFilePermission,CreateBadEmpty,DEATH_MESSAGE (BrokerFilePermissionTester::GetErrorMessage ()))73 SANDBOX_DEATH_TEST(
74 BrokerFilePermission,
75 CreateBadEmpty,
76 DEATH_MESSAGE(BrokerFilePermissionTester::GetErrorMessage())) {
77 const char kPath[] = "";
78 BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
79 }
80
81 // CheckPerm tests |path| against |perm| given |access_flags|.
82 // If |create| is true then file creation is tested for success.
CheckPerm(const BrokerFilePermission & perm,const char * path,int access_flags,bool create)83 void CheckPerm(const BrokerFilePermission& perm,
84 const char* path,
85 int access_flags,
86 bool create) {
87 const char* file_to_open = NULL;
88
89 ASSERT_FALSE(perm.CheckAccess(path, X_OK, NULL));
90 ASSERT_TRUE(perm.CheckAccess(path, F_OK, NULL));
91 // check bad perms
92 switch (access_flags) {
93 case O_RDONLY:
94 ASSERT_TRUE(perm.CheckOpen(path, O_RDONLY, &file_to_open, NULL));
95 ASSERT_FALSE(perm.CheckOpen(path, O_WRONLY, &file_to_open, NULL));
96 ASSERT_FALSE(perm.CheckOpen(path, O_RDWR, &file_to_open, NULL));
97 ASSERT_TRUE(perm.CheckAccess(path, R_OK, NULL));
98 ASSERT_FALSE(perm.CheckAccess(path, W_OK, NULL));
99 break;
100 case O_WRONLY:
101 ASSERT_FALSE(perm.CheckOpen(path, O_RDONLY, &file_to_open, NULL));
102 ASSERT_TRUE(perm.CheckOpen(path, O_WRONLY, &file_to_open, NULL));
103 ASSERT_FALSE(perm.CheckOpen(path, O_RDWR, &file_to_open, NULL));
104 ASSERT_FALSE(perm.CheckAccess(path, R_OK, NULL));
105 ASSERT_TRUE(perm.CheckAccess(path, W_OK, NULL));
106 break;
107 case O_RDWR:
108 ASSERT_TRUE(perm.CheckOpen(path, O_RDONLY, &file_to_open, NULL));
109 ASSERT_TRUE(perm.CheckOpen(path, O_WRONLY, &file_to_open, NULL));
110 ASSERT_TRUE(perm.CheckOpen(path, O_RDWR, &file_to_open, NULL));
111 ASSERT_TRUE(perm.CheckAccess(path, R_OK, NULL));
112 ASSERT_TRUE(perm.CheckAccess(path, W_OK, NULL));
113 break;
114 default:
115 // Bad test case
116 NOTREACHED();
117 }
118
119 // O_SYNC can be defined as (__O_SYNC|O_DSYNC)
120 #ifdef O_DSYNC
121 const int kSyncFlag = O_SYNC & ~O_DSYNC;
122 #else
123 const int kSyncFlag = O_SYNC;
124 #endif
125
126 const int kNumberOfBitsInOAccMode = 2;
127 static_assert(O_ACCMODE == ((1 << kNumberOfBitsInOAccMode) - 1),
128 "incorrect number of bits");
129 // check every possible flag and act accordingly.
130 // Skipping AccMode bits as they are present in every case.
131 for (int i = kNumberOfBitsInOAccMode; i < 32; i++) {
132 int flag = 1 << i;
133 switch (flag) {
134 case O_APPEND:
135 case O_ASYNC:
136 case O_DIRECT:
137 case O_DIRECTORY:
138 #ifdef O_DSYNC
139 case O_DSYNC:
140 #endif
141 case O_EXCL:
142 case O_LARGEFILE:
143 case O_NOATIME:
144 case O_NOCTTY:
145 case O_NOFOLLOW:
146 case O_NONBLOCK:
147 #if (O_NONBLOCK != O_NDELAY)
148 case O_NDELAY:
149 #endif
150 case kSyncFlag:
151 case O_TRUNC:
152 ASSERT_TRUE(
153 perm.CheckOpen(path, access_flags | flag, &file_to_open, NULL));
154 break;
155 case O_CLOEXEC:
156 case O_CREAT:
157 default:
158 ASSERT_FALSE(
159 perm.CheckOpen(path, access_flags | flag, &file_to_open, NULL));
160 }
161 }
162 if (create) {
163 bool unlink;
164 ASSERT_TRUE(perm.CheckOpen(path, O_CREAT | O_EXCL | access_flags,
165 &file_to_open, &unlink));
166 ASSERT_FALSE(unlink);
167 } else {
168 ASSERT_FALSE(perm.CheckOpen(path, O_CREAT | O_EXCL | access_flags,
169 &file_to_open, NULL));
170 }
171 }
172
TEST(BrokerFilePermission,ReadOnly)173 TEST(BrokerFilePermission, ReadOnly) {
174 const char kPath[] = "/tmp/good";
175 BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
176 CheckPerm(perm, kPath, O_RDONLY, false);
177 // Don't do anything here, so that ASSERT works in the subfunction as
178 // expected.
179 }
180
TEST(BrokerFilePermission,ReadOnlyRecursive)181 TEST(BrokerFilePermission, ReadOnlyRecursive) {
182 const char kPath[] = "/tmp/good/";
183 const char kPathFile[] = "/tmp/good/file";
184 BrokerFilePermission perm = BrokerFilePermission::ReadOnlyRecursive(kPath);
185 CheckPerm(perm, kPathFile, O_RDONLY, false);
186 // Don't do anything here, so that ASSERT works in the subfunction as
187 // expected.
188 }
189
TEST(BrokerFilePermission,WriteOnly)190 TEST(BrokerFilePermission, WriteOnly) {
191 const char kPath[] = "/tmp/good";
192 BrokerFilePermission perm = BrokerFilePermission::WriteOnly(kPath);
193 CheckPerm(perm, kPath, O_WRONLY, false);
194 // Don't do anything here, so that ASSERT works in the subfunction as
195 // expected.
196 }
197
TEST(BrokerFilePermission,ReadWrite)198 TEST(BrokerFilePermission, ReadWrite) {
199 const char kPath[] = "/tmp/good";
200 BrokerFilePermission perm = BrokerFilePermission::ReadWrite(kPath);
201 CheckPerm(perm, kPath, O_RDWR, false);
202 // Don't do anything here, so that ASSERT works in the subfunction as
203 // expected.
204 }
205
TEST(BrokerFilePermission,ReadWriteCreate)206 TEST(BrokerFilePermission, ReadWriteCreate) {
207 const char kPath[] = "/tmp/good";
208 BrokerFilePermission perm = BrokerFilePermission::ReadWriteCreate(kPath);
209 CheckPerm(perm, kPath, O_RDWR, true);
210 // Don't do anything here, so that ASSERT works in the subfunction as
211 // expected.
212 }
213
CheckUnlink(BrokerFilePermission & perm,const char * path,int access_flags)214 void CheckUnlink(BrokerFilePermission& perm,
215 const char* path,
216 int access_flags) {
217 bool unlink;
218 ASSERT_FALSE(perm.CheckOpen(path, access_flags, NULL, &unlink));
219 ASSERT_FALSE(perm.CheckOpen(path, access_flags | O_CREAT, NULL, &unlink));
220 ASSERT_TRUE(
221 perm.CheckOpen(path, access_flags | O_CREAT | O_EXCL, NULL, &unlink));
222 ASSERT_TRUE(unlink);
223 }
224
TEST(BrokerFilePermission,ReadWriteCreateUnlink)225 TEST(BrokerFilePermission, ReadWriteCreateUnlink) {
226 const char kPath[] = "/tmp/good";
227 BrokerFilePermission perm =
228 BrokerFilePermission::ReadWriteCreateUnlink(kPath);
229 CheckUnlink(perm, kPath, O_RDWR);
230 // Don't do anything here, so that ASSERT works in the subfunction as
231 // expected.
232 }
233
TEST(BrokerFilePermission,ReadWriteCreateUnlinkRecursive)234 TEST(BrokerFilePermission, ReadWriteCreateUnlinkRecursive) {
235 const char kPath[] = "/tmp/good/";
236 const char kPathFile[] = "/tmp/good/file";
237 BrokerFilePermission perm =
238 BrokerFilePermission::ReadWriteCreateUnlinkRecursive(kPath);
239 CheckUnlink(perm, kPathFile, O_RDWR);
240 // Don't do anything here, so that ASSERT works in the subfunction as
241 // expected.
242 }
243
TEST(BrokerFilePermission,ValidatePath)244 TEST(BrokerFilePermission, ValidatePath) {
245 EXPECT_TRUE(BrokerFilePermissionTester::ValidatePath("/path"));
246 EXPECT_TRUE(BrokerFilePermissionTester::ValidatePath("/"));
247 EXPECT_TRUE(BrokerFilePermissionTester::ValidatePath("/..path"));
248
249 EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath(""));
250 EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("bad"));
251 EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("/bad/"));
252 EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("bad/"));
253 EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("/bad/.."));
254 EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("/bad/../bad"));
255 EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("/../bad"));
256 }
257
258 } // namespace
259
260 } // namespace syscall_broker
261
262 } // namespace sandbox
263