| OLD | NEW |
| (Empty) | |
| 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 |
| 10 #include <string> |
| 11 |
| 12 #include "base/logging.h" |
| 13 #include "base/pickle.h" |
| 14 #include "sandbox/linux/syscall_broker/broker_common.h" |
| 15 |
| 16 namespace sandbox { |
| 17 |
| 18 namespace syscall_broker { |
| 19 |
| 20 // Async signal safe |
| 21 bool ValidatePath(const char* path) { |
| 22 if (!path) |
| 23 return false; |
| 24 |
| 25 size_t len = strlen(path); |
| 26 // No empty paths |
| 27 if (len == 0) |
| 28 return false; |
| 29 // Paths must be absolute and not relative |
| 30 if (path[0] != '/') |
| 31 return false; |
| 32 // No trailing / (but "/" is valid) |
| 33 if (len > 1 && path[len - 1] == '/') |
| 34 return false; |
| 35 // No trailing /.. |
| 36 if (len >= 3 && path[len - 3] == '/' && path[len - 2] == '.' && |
| 37 path[len - 1] == '.') |
| 38 return false; |
| 39 // No /../ anywhere |
| 40 for (size_t i = 0; i < len; i++) { |
| 41 if (path[i] == '/' && (len - i) > 3) { |
| 42 if (path[i + 1] == '.' && path[i + 2] == '.' && path[i + 3] == '/') { |
| 43 return false; |
| 44 } |
| 45 } |
| 46 } |
| 47 return true; |
| 48 } |
| 49 |
| 50 Pickle BrokerFilePermission::Serialize() { |
| 51 Pickle pickle; |
| 52 pickle.WriteString(path_); |
| 53 pickle.WriteBool(recursive_); |
| 54 pickle.WriteBool(unlink_); |
| 55 pickle.WriteBool(allow_read_); |
| 56 pickle.WriteBool(allow_write_); |
| 57 pickle.WriteBool(allow_create_); |
| 58 return pickle; |
| 59 } |
| 60 |
| 61 bool BrokerFilePermission::CreateFromPickleBuf(const char* buf, |
| 62 size_t len, |
| 63 BrokerFilePermission** perm) { |
| 64 std::string path; |
| 65 bool recursive; |
| 66 bool unlink; |
| 67 bool allow_read; |
| 68 bool allow_write; |
| 69 bool allow_create; |
| 70 Pickle pickle(buf, len); |
| 71 PickleIterator iter(pickle); |
| 72 |
| 73 if (perm && pickle.ReadString(&iter, &path) && |
| 74 pickle.ReadBool(&iter, &recursive) && pickle.ReadBool(&iter, &unlink) && |
| 75 pickle.ReadBool(&iter, &allow_read) && |
| 76 pickle.ReadBool(&iter, &allow_write) && |
| 77 pickle.ReadBool(&iter, &allow_create)) { |
| 78 *perm = new BrokerFilePermission(path, recursive, unlink, allow_read, |
| 79 allow_write, allow_create); |
| 80 return (perm != NULL); |
| 81 } |
| 82 return false; |
| 83 } |
| 84 |
| 85 // Async signal safe |
| 86 // Calls std::string::c_str(), strncmp and strlen. All these |
| 87 // methods are async signal safe in common standard libs. |
| 88 // TODO(leecam): remove dependency on std::string |
| 89 bool BrokerFilePermission::IsPathCoveredByThisPermission( |
| 90 const char* requested_filename) const { |
| 91 const char* path = path_.c_str(); |
| 92 if ((recursive_ && strncmp(requested_filename, path, strlen(path)) == 0)) { |
| 93 // Note: This prefix match will allow any path under the whitelisted |
| 94 // path, for any number of directory levels. E.g. if the whitelisted |
| 95 // path is /good/ then the following will be permitted by the policy. |
| 96 // /good/file1 |
| 97 // /good/folder/file2 |
| 98 // /good/folder/folder2/file3 |
| 99 // If an attacker could make 'folder' a symlink to ../../ they would have |
| 100 // access to the entire filesystem. |
| 101 // Whitelisting with multiple depths is useful, e.g /proc/ but |
| 102 // the system needs to ensure symlinks can not be created! |
| 103 // That said if an attacker can convert any of the absolute paths |
| 104 // to a symlink they can control any file on the system also. |
| 105 return true; |
| 106 } else if (strcmp(requested_filename, path) == 0) { |
| 107 return true; |
| 108 } |
| 109 return false; |
| 110 } |
| 111 |
| 112 // Async signal safe. |
| 113 // External call to std::string::c_str() is |
| 114 // called in IsPathCoveredByThisPermission. |
| 115 // TODO(leecam): remove dependency on std::string |
| 116 bool BrokerFilePermission::CheckAccess(const char* requested_filename, |
| 117 int mode, |
| 118 const char** file_to_access) const { |
| 119 // First, check if |mode| is existence, ability to read or ability |
| 120 // to write. We do not support X_OK. |
| 121 if (mode != F_OK && mode & ~(R_OK | W_OK)) { |
| 122 return false; |
| 123 } |
| 124 |
| 125 if (!ValidatePath(requested_filename)) |
| 126 return false; |
| 127 |
| 128 if (!IsPathCoveredByThisPermission(requested_filename)) { |
| 129 return false; |
| 130 } |
| 131 bool allowed = false; |
| 132 switch (mode) { |
| 133 case F_OK: |
| 134 if (allow_read_ || allow_write_) |
| 135 allowed = true; |
| 136 break; |
| 137 case R_OK: |
| 138 if (allow_read_) |
| 139 allowed = true; |
| 140 break; |
| 141 case W_OK: |
| 142 if (allow_write_) |
| 143 allowed = true; |
| 144 break; |
| 145 case R_OK | W_OK: |
| 146 if (allow_read_ && allow_write_) |
| 147 allowed = true; |
| 148 break; |
| 149 default: |
| 150 return false; |
| 151 } |
| 152 |
| 153 if (allowed && file_to_access) { |
| 154 if (!recursive_) |
| 155 *file_to_access = path_.c_str(); |
| 156 else |
| 157 *file_to_access = requested_filename; |
| 158 } |
| 159 return allowed; |
| 160 } |
| 161 |
| 162 // Async signal safe. |
| 163 // External call to std::string::c_str() is |
| 164 // called in IsPathCoveredByThisPermission. |
| 165 // TODO(leecam): remove dependency on std::string |
| 166 bool BrokerFilePermission::CheckOpen(const char* requested_filename, |
| 167 int flags, |
| 168 const char** file_to_open, |
| 169 bool* unlink_after_open) const { |
| 170 if (!ValidatePath(requested_filename)) |
| 171 return false; |
| 172 |
| 173 if (!IsPathCoveredByThisPermission(requested_filename)) { |
| 174 return false; |
| 175 } |
| 176 |
| 177 // First, check the access mode is valid. |
| 178 const int access_mode = flags & O_ACCMODE; |
| 179 if (access_mode != O_RDONLY && access_mode != O_WRONLY && |
| 180 access_mode != O_RDWR) { |
| 181 return false; |
| 182 } |
| 183 |
| 184 // Check if read is allowed |
| 185 if (!allow_read_ && (access_mode == O_RDONLY || access_mode == O_RDWR)) { |
| 186 return false; |
| 187 } |
| 188 |
| 189 // Check if write is allowed |
| 190 if (!allow_write_ && (access_mode == O_WRONLY || access_mode == O_RDWR)) { |
| 191 return false; |
| 192 } |
| 193 |
| 194 // Check if file creation is allowed. |
| 195 if (!allow_create_ && (flags & O_CREAT)) { |
| 196 return false; |
| 197 } |
| 198 |
| 199 // If O_CREAT is present, ensure O_EXCL |
| 200 if ((flags & O_CREAT) && !(flags & O_EXCL)) { |
| 201 return false; |
| 202 } |
| 203 |
| 204 // If this file is to be unlinked, ensure its created. |
| 205 if (unlink_ && !(flags & O_CREAT)) { |
| 206 return false; |
| 207 } |
| 208 |
| 209 // Some flags affect the behavior of the current process. We don't support |
| 210 // them and don't allow them for now. |
| 211 if (flags & kCurrentProcessOpenFlagsMask) { |
| 212 return false; |
| 213 } |
| 214 |
| 215 // Now check that all the flags are known to us. |
| 216 const int creation_and_status_flags = flags & ~O_ACCMODE; |
| 217 |
| 218 const int known_flags = O_APPEND | O_ASYNC | O_CLOEXEC | O_CREAT | O_DIRECT | |
| 219 O_DIRECTORY | O_EXCL | O_LARGEFILE | O_NOATIME | |
| 220 O_NOCTTY | O_NOFOLLOW | O_NONBLOCK | O_NDELAY | |
| 221 O_SYNC | O_TRUNC; |
| 222 |
| 223 const int unknown_flags = ~known_flags; |
| 224 const bool has_unknown_flags = creation_and_status_flags & unknown_flags; |
| 225 |
| 226 if (has_unknown_flags) |
| 227 return false; |
| 228 |
| 229 if (file_to_open) { |
| 230 if (!recursive_) |
| 231 *file_to_open = path_.c_str(); |
| 232 else |
| 233 *file_to_open = requested_filename; |
| 234 } |
| 235 if (unlink_after_open) |
| 236 *unlink_after_open = unlink_; |
| 237 |
| 238 return true; |
| 239 } |
| 240 |
| 241 BrokerFilePermission::BrokerFilePermission(std::string path, |
| 242 bool recursive, |
| 243 bool unlink, |
| 244 bool allow_read, |
| 245 bool allow_write, |
| 246 bool allow_create) |
| 247 : path_(path), |
| 248 recursive_(recursive), |
| 249 unlink_(unlink), |
| 250 allow_read_(allow_read), |
| 251 allow_write_(allow_write), |
| 252 allow_create_(allow_create) { |
| 253 // Validate this permission and die if invalid! |
| 254 |
| 255 // Don't allow unlinking on creation without create permission |
| 256 if (unlink_) |
| 257 CHECK(allow_create); |
| 258 // Recursive paths must have a trailing slash |
| 259 if (recursive_) |
| 260 CHECK(path_.back() == '/'); |
| 261 // Whitelisted paths must be absolute. |
| 262 CHECK(path_.front() == '/'); |
| 263 } |
| 264 |
| 265 } // namespace syscall_broker |
| 266 |
| 267 } // namespace sandbox |
| OLD | NEW |