Add scoped_ptr-safe base::Value to Dictionary/List conversion functions.
[chromium-blink-merge.git] / sandbox / linux / syscall_broker / broker_file_permission.cc
blobbeceda93f50c8871b6d3c37b0ad8c66a6d97d1bc
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"
7 #include <fcntl.h>
8 #include <string.h>
10 #include <string>
12 #include "base/logging.h"
13 #include "sandbox/linux/syscall_broker/broker_common.h"
15 namespace sandbox {
17 namespace syscall_broker {
19 // Async signal safe
20 bool BrokerFilePermission::ValidatePath(const char* path) {
21 if (!path)
22 return false;
24 const size_t len = strlen(path);
25 // No empty paths
26 if (len == 0)
27 return false;
28 // Paths must be absolute and not relative
29 if (path[0] != '/')
30 return false;
31 // No trailing / (but "/" is valid)
32 if (len > 1 && path[len - 1] == '/')
33 return false;
34 // No trailing /..
35 if (len >= 3 && path[len - 3] == '/' && path[len - 2] == '.' &&
36 path[len - 1] == '.')
37 return false;
38 // No /../ anywhere
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] == '/') {
42 return false;
46 return true;
49 // Async signal safe
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.
59 // /good/file1
60 // /good/folder/file2
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.
68 return true;
69 } else if (strcmp(requested_filename, path) == 0) {
70 return true;
72 return false;
75 // Async signal safe.
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,
80 int mode,
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)) {
85 return false;
88 if (!ValidatePath(requested_filename))
89 return false;
91 if (!MatchPath(requested_filename)) {
92 return false;
94 bool allowed = false;
95 switch (mode) {
96 case F_OK:
97 if (allow_read_ || allow_write_)
98 allowed = true;
99 break;
100 case R_OK:
101 if (allow_read_)
102 allowed = true;
103 break;
104 case W_OK:
105 if (allow_write_)
106 allowed = true;
107 break;
108 case R_OK | W_OK:
109 if (allow_read_ && allow_write_)
110 allowed = true;
111 break;
112 default:
113 return false;
116 if (allowed && file_to_access) {
117 if (!recursive_)
118 *file_to_access = path_.c_str();
119 else
120 *file_to_access = requested_filename;
122 return allowed;
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,
130 int flags,
131 const char** file_to_open,
132 bool* unlink_after_open) const {
133 if (!ValidatePath(requested_filename))
134 return false;
136 if (!MatchPath(requested_filename)) {
137 return false;
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) {
144 return false;
147 // Check if read is allowed
148 if (!allow_read_ && (access_mode == O_RDONLY || access_mode == O_RDWR)) {
149 return false;
152 // Check if write is allowed
153 if (!allow_write_ && (access_mode == O_WRONLY || access_mode == O_RDWR)) {
154 return false;
157 // Check if file creation is allowed.
158 if (!allow_create_ && (flags & O_CREAT)) {
159 return false;
162 // If O_CREAT is present, ensure O_EXCL
163 if ((flags & O_CREAT) && !(flags & O_EXCL)) {
164 return false;
167 // If this file is to be unlinked, ensure it's created.
168 if (unlink_ && !(flags & O_CREAT)) {
169 return false;
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) {
175 return false;
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 |
184 O_SYNC | O_TRUNC;
186 const int unknown_flags = ~known_flags;
187 const bool has_unknown_flags = creation_and_status_flags & unknown_flags;
189 if (has_unknown_flags)
190 return false;
192 if (file_to_open) {
193 if (!recursive_)
194 *file_to_open = path_.c_str();
195 else
196 *file_to_open = requested_filename;
198 if (unlink_after_open)
199 *unlink_after_open = unlink_;
201 return true;
204 const char* BrokerFilePermission::GetErrorMessageForTests() {
205 static char kInvalidBrokerFileString[] = "Invalid BrokerFilePermission";
206 return kInvalidBrokerFileString;
209 BrokerFilePermission::BrokerFilePermission(const std::string& path,
210 bool recursive,
211 bool unlink,
212 bool allow_read,
213 bool allow_write,
214 bool allow_create)
215 : path_(path),
216 recursive_(recursive),
217 unlink_(unlink),
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
229 if (unlink_) {
230 CHECK(allow_create) << GetErrorMessageForTests();
232 const char last_char = *(path_.rbegin());
233 // Recursive paths must have a trailing slash
234 if (recursive_) {
235 CHECK(last_char == '/') << GetErrorMessageForTests();
236 } else {
237 CHECK(last_char != '/') << GetErrorMessageForTests();
241 } // namespace syscall_broker
243 } // namespace sandbox