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