broker_file_permission.cc 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  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. #include "sandbox/linux/syscall_broker/broker_file_permission.h"
  5. #include <fcntl.h>
  6. #include <stddef.h>
  7. #include <string.h>
  8. #include <unistd.h>
  9. #include <ostream>
  10. #include <string>
  11. #include "base/check.h"
  12. #include "base/check_op.h"
  13. #include "sandbox/linux/syscall_broker/broker_command.h"
  14. namespace sandbox {
  15. namespace syscall_broker {
  16. BrokerFilePermission::BrokerFilePermission(BrokerFilePermission&&) = default;
  17. BrokerFilePermission& BrokerFilePermission::operator=(BrokerFilePermission&&) =
  18. default;
  19. BrokerFilePermission::BrokerFilePermission(const BrokerFilePermission&) =
  20. default;
  21. BrokerFilePermission& BrokerFilePermission::operator=(
  22. const BrokerFilePermission&) = default;
  23. BrokerFilePermission::~BrokerFilePermission() = default;
  24. bool BrokerFilePermission::ValidatePath(const char* path) {
  25. if (!path)
  26. return false;
  27. const size_t len = strlen(path);
  28. // No empty paths
  29. if (len == 0)
  30. return false;
  31. // Paths must be absolute and not relative
  32. if (path[0] != '/')
  33. return false;
  34. // No trailing / (but "/" is valid)
  35. if (len > 1 && path[len - 1] == '/')
  36. return false;
  37. // No trailing /..
  38. if (len >= 3 && path[len - 3] == '/' && path[len - 2] == '.' &&
  39. path[len - 1] == '.')
  40. return false;
  41. // No /../ anywhere
  42. for (size_t i = 0; i < len; i++) {
  43. if (path[i] == '/' && (len - i) > 3) {
  44. if (path[i + 1] == '.' && path[i + 2] == '.' && path[i + 3] == '/') {
  45. return false;
  46. }
  47. }
  48. }
  49. return true;
  50. }
  51. // Async signal safe
  52. // Calls std::string::c_str(), strncmp and strlen. All these
  53. // methods are async signal safe in common standard libs.
  54. // TODO(leecam): remove dependency on std::string
  55. bool BrokerFilePermission::MatchPath(const char* requested_filename) const {
  56. // Note: This recursive match will allow any path under the allowlisted
  57. // path, for any number of directory levels. E.g. if the allowlisted
  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. // Allowlisting 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 recursive()
  69. ? strncmp(requested_filename, path_.c_str(), path_.length()) == 0
  70. : strcmp(requested_filename, path_.c_str()) == 0;
  71. }
  72. // Async signal safe.
  73. // External call to std::string::c_str() is
  74. // called in MatchPath.
  75. // TODO(leecam): remove dependency on std::string
  76. bool BrokerFilePermission::CheckAccess(const char* requested_filename,
  77. int mode,
  78. const char** file_to_access) const {
  79. // First, check if |mode| is existence, ability to read or ability
  80. // to write. We do not support X_OK.
  81. if (mode != F_OK && mode & ~(R_OK | W_OK))
  82. return false;
  83. if (!ValidatePath(requested_filename))
  84. return false;
  85. return CheckAccessInternal(requested_filename, mode, file_to_access);
  86. }
  87. bool BrokerFilePermission::CheckAccessInternal(
  88. const char* requested_filename,
  89. int mode,
  90. const char** file_to_access) const {
  91. if (!MatchPath(requested_filename))
  92. return false;
  93. bool allowed = false;
  94. switch (mode) {
  95. case F_OK:
  96. allowed = allow_read() || allow_write();
  97. break;
  98. case R_OK:
  99. allowed = allow_read();
  100. break;
  101. case W_OK:
  102. allowed = allow_write();
  103. break;
  104. case R_OK | W_OK:
  105. allowed = allow_read() && allow_write();
  106. break;
  107. default:
  108. break;
  109. }
  110. if (!allowed)
  111. return false;
  112. if (file_to_access)
  113. *file_to_access = recursive() ? requested_filename : path_.c_str();
  114. return true;
  115. }
  116. // Async signal safe.
  117. // External call to std::string::c_str() is
  118. // called in MatchPath.
  119. // TODO(leecam): remove dependency on std::string
  120. bool BrokerFilePermission::CheckOpen(const char* requested_filename,
  121. int flags,
  122. const char** file_to_open,
  123. bool* unlink_after_open) const {
  124. if (!ValidatePath(requested_filename))
  125. return false;
  126. if (!MatchPath(requested_filename))
  127. return false;
  128. // First, check the access mode is valid.
  129. const int access_mode = flags & O_ACCMODE;
  130. if (access_mode != O_RDONLY && access_mode != O_WRONLY &&
  131. access_mode != O_RDWR) {
  132. return false;
  133. }
  134. // Check if read is allowed.
  135. if (!allow_read() && (access_mode == O_RDONLY || access_mode == O_RDWR)) {
  136. return false;
  137. }
  138. // Check if write is allowed.
  139. if (!allow_write() && (access_mode == O_WRONLY || access_mode == O_RDWR)) {
  140. return false;
  141. }
  142. // Check if file creation is allowed.
  143. if (!allow_create() && (flags & O_CREAT)) {
  144. return false;
  145. }
  146. // If this file is to be temporary, ensure it is created, not pre-existing.
  147. // See https://crbug.com/415681#c17
  148. if (temporary_only() && (!(flags & O_CREAT) || !(flags & O_EXCL))) {
  149. return false;
  150. }
  151. // Some flags affect the behavior of the current process. We don't support
  152. // them and don't allow them for now.
  153. if (flags & kCurrentProcessOpenFlagsMask) {
  154. return false;
  155. }
  156. // The effect of (O_RDONLY | O_TRUNC) is undefined, and in some cases it
  157. // actually truncates, so deny.
  158. if (access_mode == O_RDONLY && (flags & O_TRUNC) != 0) {
  159. return false;
  160. }
  161. // Now check that all the flags are known to us.
  162. const int creation_and_status_flags = flags & ~O_ACCMODE;
  163. const int known_flags = O_APPEND | O_ASYNC | O_CLOEXEC | O_CREAT | O_DIRECT |
  164. O_DIRECTORY | O_EXCL | O_LARGEFILE | O_NOATIME |
  165. O_NOCTTY | O_NOFOLLOW | O_NONBLOCK | O_NDELAY |
  166. O_SYNC | O_TRUNC;
  167. const int unknown_flags = ~known_flags;
  168. const bool has_unknown_flags = creation_and_status_flags & unknown_flags;
  169. if (has_unknown_flags)
  170. return false;
  171. if (file_to_open)
  172. *file_to_open = recursive() ? requested_filename : path_.c_str();
  173. if (unlink_after_open)
  174. *unlink_after_open = temporary_only();
  175. return true;
  176. }
  177. bool BrokerFilePermission::CheckStat(const char* requested_filename,
  178. const char** file_to_access) const {
  179. if (!ValidatePath(requested_filename))
  180. return false;
  181. // Ability to access implies ability to stat().
  182. if (CheckAccessInternal(requested_filename, F_OK, file_to_access))
  183. return true;
  184. // Allow stat() on leading directories if have create or stat() permission.
  185. if (!(allow_create() || allow_stat_with_intermediates()))
  186. return false;
  187. // NOTE: ValidatePath proved requested_length != 0;
  188. size_t requested_length = strlen(requested_filename);
  189. CHECK(requested_length);
  190. // Special case for root: only one slash, otherwise must have a second
  191. // slash in the right spot to avoid substring matches.
  192. // |allow_stat_with_intermediates()| can match on the full path, and
  193. // |allow_create()| only matches a leading directory.
  194. if ((requested_length == 1 && requested_filename[0] == '/') ||
  195. (allow_stat_with_intermediates() && path_ == requested_filename) ||
  196. (requested_length < path_.length() &&
  197. memcmp(path_.c_str(), requested_filename, requested_length) == 0 &&
  198. path_.c_str()[requested_length] == '/')) {
  199. if (file_to_access)
  200. *file_to_access = requested_filename;
  201. return true;
  202. }
  203. return false;
  204. }
  205. const char* BrokerFilePermission::GetErrorMessageForTests() {
  206. return "Invalid BrokerFilePermission";
  207. }
  208. void BrokerFilePermission::DieOnInvalidPermission() {
  209. // Must have enough length for a '/'
  210. CHECK(path_.length() > 0) << GetErrorMessageForTests();
  211. // Allowlisted paths must be absolute.
  212. CHECK(path_[0] == '/') << GetErrorMessageForTests();
  213. // Don't allow temporary creation without create permission.
  214. if (temporary_only())
  215. CHECK(allow_create()) << GetErrorMessageForTests();
  216. // Recursive paths must have a trailing slash, absolutes must not.
  217. const char last_char = *(path_.rbegin());
  218. if (recursive())
  219. CHECK(last_char == '/') << GetErrorMessageForTests();
  220. else
  221. CHECK(last_char != '/') << GetErrorMessageForTests();
  222. }
  223. BrokerFilePermission::BrokerFilePermission(std::string path, uint64_t flags)
  224. : path_(std::move(path)), flags_(flags) {
  225. DieOnInvalidPermission();
  226. }
  227. BrokerFilePermission::BrokerFilePermission(
  228. std::string path,
  229. RecursionOption recurse_opt,
  230. PersistenceOption persist_opt,
  231. ReadPermission read_perm,
  232. WritePermission write_perm,
  233. CreatePermission create_perm,
  234. StatWithIntermediatesPermission stat_perm)
  235. : path_(std::move(path)) {
  236. flags_[kRecursiveBitPos] = recurse_opt == RecursionOption::kRecursive;
  237. flags_[kTemporaryOnlyBitPos] =
  238. persist_opt == PersistenceOption::kTemporaryOnly;
  239. flags_[kAllowReadBitPos] = read_perm == ReadPermission::kAllowRead;
  240. flags_[kAllowWriteBitPos] = write_perm == WritePermission::kAllowWrite;
  241. flags_[kAllowCreateBitPos] = create_perm == CreatePermission::kAllowCreate;
  242. flags_[kAllowStatWithIntermediatesBitPos] =
  243. stat_perm == StatWithIntermediatesPermission::kAllowStatWithIntermediates;
  244. DieOnInvalidPermission();
  245. }
  246. } // namespace syscall_broker
  247. } // namespace sandbox