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.
5 #include "sandbox/linux/syscall_broker/broker_file_permission.h"
12 #include "base/logging.h"
13 #include "sandbox/linux/syscall_broker/broker_common.h"
17 namespace syscall_broker
{
20 bool BrokerFilePermission::ValidatePath(const char* path
) {
24 const size_t len
= strlen(path
);
28 // Paths must be absolute and not relative
31 // No trailing / (but "/" is valid)
32 if (len
> 1 && path
[len
- 1] == '/')
35 if (len
>= 3 && path
[len
- 3] == '/' && path
[len
- 2] == '.' &&
39 for (size_t i
= 0; i
< len
; i
++) {
40 if (path
[i
] == '/' && (len
- i
) > 3) {
41 if (path
[i
+ 1] == '.' && path
[i
+ 2] == '.' && path
[i
+ 3] == '/') {
50 // Calls std::string::c_str(), strncmp and strlen. All these
51 // methods are async signal safe in common standard libs.
52 // TODO(leecam): remove dependency on std::string
53 bool BrokerFilePermission::MatchPath(const char* requested_filename
) const {
54 const char* path
= path_
.c_str();
55 if ((recursive_
&& strncmp(requested_filename
, path
, strlen(path
)) == 0)) {
56 // Note: This prefix match will allow any path under the whitelisted
57 // path, for any number of directory levels. E.g. if the whitelisted
58 // path is /good/ then the following will be permitted by the policy.
61 // /good/folder/folder2/file3
62 // If an attacker could make 'folder' a symlink to ../../ they would have
63 // access to the entire filesystem.
64 // Whitelisting with multiple depths is useful, e.g /proc/ but
65 // the system needs to ensure symlinks can not be created!
66 // That said if an attacker can convert any of the absolute paths
67 // to a symlink they can control any file on the system also.
69 } else if (strcmp(requested_filename
, path
) == 0) {
76 // External call to std::string::c_str() is
77 // called in MatchPath.
78 // TODO(leecam): remove dependency on std::string
79 bool BrokerFilePermission::CheckAccess(const char* requested_filename
,
81 const char** file_to_access
) const {
82 // First, check if |mode| is existence, ability to read or ability
83 // to write. We do not support X_OK.
84 if (mode
!= F_OK
&& mode
& ~(R_OK
| W_OK
)) {
88 if (!ValidatePath(requested_filename
))
91 if (!MatchPath(requested_filename
)) {
97 if (allow_read_
|| allow_write_
)
109 if (allow_read_
&& allow_write_
)
116 if (allowed
&& file_to_access
) {
118 *file_to_access
= path_
.c_str();
120 *file_to_access
= requested_filename
;
125 // Async signal safe.
126 // External call to std::string::c_str() is
127 // called in MatchPath.
128 // TODO(leecam): remove dependency on std::string
129 bool BrokerFilePermission::CheckOpen(const char* requested_filename
,
131 const char** file_to_open
,
132 bool* unlink_after_open
) const {
133 if (!ValidatePath(requested_filename
))
136 if (!MatchPath(requested_filename
)) {
140 // First, check the access mode is valid.
141 const int access_mode
= flags
& O_ACCMODE
;
142 if (access_mode
!= O_RDONLY
&& access_mode
!= O_WRONLY
&&
143 access_mode
!= O_RDWR
) {
147 // Check if read is allowed
148 if (!allow_read_
&& (access_mode
== O_RDONLY
|| access_mode
== O_RDWR
)) {
152 // Check if write is allowed
153 if (!allow_write_
&& (access_mode
== O_WRONLY
|| access_mode
== O_RDWR
)) {
157 // Check if file creation is allowed.
158 if (!allow_create_
&& (flags
& O_CREAT
)) {
162 // If O_CREAT is present, ensure O_EXCL
163 if ((flags
& O_CREAT
) && !(flags
& O_EXCL
)) {
167 // If this file is to be unlinked, ensure it's created.
168 if (unlink_
&& !(flags
& O_CREAT
)) {
172 // Some flags affect the behavior of the current process. We don't support
173 // them and don't allow them for now.
174 if (flags
& kCurrentProcessOpenFlagsMask
) {
178 // Now check that all the flags are known to us.
179 const int creation_and_status_flags
= flags
& ~O_ACCMODE
;
181 const int known_flags
= O_APPEND
| O_ASYNC
| O_CLOEXEC
| O_CREAT
| O_DIRECT
|
182 O_DIRECTORY
| O_EXCL
| O_LARGEFILE
| O_NOATIME
|
183 O_NOCTTY
| O_NOFOLLOW
| O_NONBLOCK
| O_NDELAY
|
186 const int unknown_flags
= ~known_flags
;
187 const bool has_unknown_flags
= creation_and_status_flags
& unknown_flags
;
189 if (has_unknown_flags
)
194 *file_to_open
= path_
.c_str();
196 *file_to_open
= requested_filename
;
198 if (unlink_after_open
)
199 *unlink_after_open
= unlink_
;
204 const char* BrokerFilePermission::GetErrorMessageForTests() {
205 static char kInvalidBrokerFileString
[] = "Invalid BrokerFilePermission";
206 return kInvalidBrokerFileString
;
209 BrokerFilePermission::BrokerFilePermission(const std::string
& path
,
216 recursive_(recursive
),
218 allow_read_(allow_read
),
219 allow_write_(allow_write
),
220 allow_create_(allow_create
) {
221 // Validate this permission and die if invalid!
223 // Must have enough length for a '/'
224 CHECK(path_
.length() > 0) << GetErrorMessageForTests();
225 // Whitelisted paths must be absolute.
226 CHECK(path_
[0] == '/') << GetErrorMessageForTests();
228 // Don't allow unlinking on creation without create permission
230 CHECK(allow_create
) << GetErrorMessageForTests();
232 const char last_char
= *(path_
.rbegin());
233 // Recursive paths must have a trailing slash
235 CHECK(last_char
== '/') << GetErrorMessageForTests();
237 CHECK(last_char
!= '/') << GetErrorMessageForTests();
241 } // namespace syscall_broker
243 } // namespace sandbox