broker_host.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  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_host.h"
  5. #include <errno.h>
  6. #include <fcntl.h>
  7. #include <limits.h>
  8. #include <stddef.h>
  9. #include <sys/socket.h>
  10. #include <sys/stat.h>
  11. #include <sys/syscall.h>
  12. #include <sys/types.h>
  13. #include <unistd.h>
  14. #include <string>
  15. #include <utility>
  16. #include "base/files/scoped_file.h"
  17. #include "base/logging.h"
  18. #include "base/posix/eintr_wrapper.h"
  19. #include "sandbox/linux/services/syscall_wrappers.h"
  20. #include "sandbox/linux/syscall_broker/broker_command.h"
  21. #include "sandbox/linux/syscall_broker/broker_permission_list.h"
  22. #include "sandbox/linux/syscall_broker/broker_simple_message.h"
  23. #include "sandbox/linux/system_headers/linux_stat.h"
  24. #include "sandbox/linux/system_headers/linux_syscalls.h"
  25. namespace sandbox {
  26. namespace syscall_broker {
  27. namespace {
  28. // A little open(2) wrapper to handle some oddities for us. In the general case
  29. // make a direct system call since we want to keep in control of the broker
  30. // process' system calls profile to be able to loosely sandbox it.
  31. int sys_open(const char* pathname, int flags) {
  32. // Hardcode mode to rw------- when creating files.
  33. int mode = (flags & O_CREAT) ? 0600 : 0;
  34. return syscall(__NR_openat, AT_FDCWD, pathname, flags, mode);
  35. }
  36. bool GetPathAndFlags(BrokerSimpleMessage* message,
  37. const char** pathname,
  38. int* flags) {
  39. return message->ReadString(pathname) && message->ReadInt(flags);
  40. }
  41. // Perform access(2) on |requested_filename| with mode |mode| if allowed by our
  42. // permission_list. Write the syscall return value (-errno) to |reply|.
  43. void AccessFileForIPC(const BrokerCommandSet& allowed_command_set,
  44. const BrokerPermissionList& permission_list,
  45. const std::string& requested_filename,
  46. int mode,
  47. BrokerSimpleMessage* reply) {
  48. const char* file_to_access = NULL;
  49. if (!CommandAccessIsSafe(allowed_command_set, permission_list,
  50. requested_filename.c_str(), mode, &file_to_access)) {
  51. RAW_CHECK(reply->AddIntToMessage(-permission_list.denied_errno()));
  52. return;
  53. }
  54. RAW_CHECK(file_to_access);
  55. if (access(file_to_access, mode) < 0) {
  56. RAW_CHECK(reply->AddIntToMessage(-errno));
  57. return;
  58. }
  59. RAW_CHECK(reply->AddIntToMessage(0));
  60. }
  61. // Performs mkdir(2) on |filename| with mode |mode| if allowed by our
  62. // permission_list. Write the syscall return value (-errno) to |reply|.
  63. void MkdirFileForIPC(const BrokerCommandSet& allowed_command_set,
  64. const BrokerPermissionList& permission_list,
  65. const std::string& filename,
  66. int mode,
  67. BrokerSimpleMessage* reply) {
  68. const char* file_to_access = nullptr;
  69. if (!CommandMkdirIsSafe(allowed_command_set, permission_list,
  70. filename.c_str(), &file_to_access)) {
  71. RAW_CHECK(reply->AddIntToMessage(-permission_list.denied_errno()));
  72. return;
  73. }
  74. if (mkdir(file_to_access, mode) < 0) {
  75. RAW_CHECK(reply->AddIntToMessage(-errno));
  76. return;
  77. }
  78. RAW_CHECK(reply->AddIntToMessage(0));
  79. }
  80. // Open |requested_filename| with |flags| if allowed by our permission list.
  81. // Write the syscall return value (-errno) to |reply| and return the
  82. // file descriptor in the |opened_file| if relevant.
  83. void OpenFileForIPC(const BrokerCommandSet& allowed_command_set,
  84. const BrokerPermissionList& permission_list,
  85. const std::string& requested_filename,
  86. int flags,
  87. BrokerSimpleMessage* reply,
  88. base::ScopedFD* opened_file) {
  89. const char* file_to_open = NULL;
  90. bool unlink_after_open = false;
  91. if (!CommandOpenIsSafe(allowed_command_set, permission_list,
  92. requested_filename.c_str(), flags, &file_to_open,
  93. &unlink_after_open)) {
  94. RAW_CHECK(reply->AddIntToMessage(-permission_list.denied_errno()));
  95. return;
  96. }
  97. CHECK(file_to_open);
  98. opened_file->reset(sys_open(file_to_open, flags));
  99. if (!opened_file->is_valid()) {
  100. RAW_CHECK(reply->AddIntToMessage(-errno));
  101. return;
  102. }
  103. if (unlink_after_open)
  104. unlink(file_to_open);
  105. RAW_CHECK(reply->AddIntToMessage(0));
  106. }
  107. // Perform rename(2) on |old_filename| to |new_filename| and write the
  108. // result to |return_val|.
  109. void RenameFileForIPC(const BrokerCommandSet& allowed_command_set,
  110. const BrokerPermissionList& permission_list,
  111. const std::string& old_filename,
  112. const std::string& new_filename,
  113. BrokerSimpleMessage* reply) {
  114. const char* old_file_to_access = nullptr;
  115. const char* new_file_to_access = nullptr;
  116. if (!CommandRenameIsSafe(allowed_command_set, permission_list,
  117. old_filename.c_str(), new_filename.c_str(),
  118. &old_file_to_access, &new_file_to_access)) {
  119. RAW_CHECK(reply->AddIntToMessage(-permission_list.denied_errno()));
  120. return;
  121. }
  122. if (rename(old_file_to_access, new_file_to_access) < 0) {
  123. RAW_CHECK(reply->AddIntToMessage(-errno));
  124. return;
  125. }
  126. RAW_CHECK(reply->AddIntToMessage(0));
  127. }
  128. // Perform readlink(2) on |filename| using a buffer of MAX_PATH bytes.
  129. void ReadlinkFileForIPC(const BrokerCommandSet& allowed_command_set,
  130. const BrokerPermissionList& permission_list,
  131. const std::string& filename,
  132. BrokerSimpleMessage* reply) {
  133. const char* file_to_access = nullptr;
  134. if (!CommandReadlinkIsSafe(allowed_command_set, permission_list,
  135. filename.c_str(), &file_to_access)) {
  136. RAW_CHECK(reply->AddIntToMessage(-permission_list.denied_errno()));
  137. return;
  138. }
  139. char buf[PATH_MAX];
  140. ssize_t result = readlink(file_to_access, buf, sizeof(buf));
  141. if (result < 0) {
  142. RAW_CHECK(reply->AddIntToMessage(-errno));
  143. return;
  144. }
  145. RAW_CHECK(reply->AddIntToMessage(result));
  146. RAW_CHECK(reply->AddDataToMessage(buf, result));
  147. }
  148. void RmdirFileForIPC(const BrokerCommandSet& allowed_command_set,
  149. const BrokerPermissionList& permission_list,
  150. const std::string& requested_filename,
  151. BrokerSimpleMessage* reply) {
  152. const char* file_to_access = nullptr;
  153. if (!CommandRmdirIsSafe(allowed_command_set, permission_list,
  154. requested_filename.c_str(), &file_to_access)) {
  155. RAW_CHECK(reply->AddIntToMessage(-permission_list.denied_errno()));
  156. return;
  157. }
  158. if (rmdir(file_to_access) < 0) {
  159. RAW_CHECK(reply->AddIntToMessage(-errno));
  160. return;
  161. }
  162. RAW_CHECK(reply->AddIntToMessage(0));
  163. }
  164. // Perform stat(2) on |requested_filename| and write the result to
  165. // |return_val|.
  166. void StatFileForIPC(const BrokerCommandSet& allowed_command_set,
  167. const BrokerPermissionList& permission_list,
  168. BrokerCommand command_type,
  169. const std::string& requested_filename,
  170. bool follow_links,
  171. BrokerSimpleMessage* reply) {
  172. const char* file_to_access = nullptr;
  173. if (!CommandStatIsSafe(allowed_command_set, permission_list,
  174. requested_filename.c_str(), &file_to_access)) {
  175. RAW_CHECK(reply->AddIntToMessage(-permission_list.denied_errno()));
  176. return;
  177. }
  178. if (command_type == COMMAND_STAT) {
  179. struct kernel_stat sb;
  180. int sts = follow_links ? sandbox::sys_stat(file_to_access, &sb)
  181. : sandbox::sys_lstat(file_to_access, &sb);
  182. if (sts < 0) {
  183. RAW_CHECK(reply->AddIntToMessage(-errno));
  184. return;
  185. }
  186. RAW_CHECK(reply->AddIntToMessage(0));
  187. RAW_CHECK(
  188. reply->AddDataToMessage(reinterpret_cast<char*>(&sb), sizeof(sb)));
  189. } else {
  190. #if defined(__NR_fstatat64)
  191. DCHECK(command_type == COMMAND_STAT64);
  192. struct kernel_stat64 sb;
  193. int sts = sandbox::sys_fstatat64(AT_FDCWD, file_to_access, &sb,
  194. follow_links ? 0 : AT_SYMLINK_NOFOLLOW);
  195. if (sts < 0) {
  196. RAW_CHECK(reply->AddIntToMessage(-errno));
  197. return;
  198. }
  199. RAW_CHECK(reply->AddIntToMessage(0));
  200. RAW_CHECK(
  201. reply->AddDataToMessage(reinterpret_cast<char*>(&sb), sizeof(sb)));
  202. #else // defined(__NR_fstatat64)
  203. // We should not reach here on 64-bit systems, as the *stat*64() are only
  204. // necessary on 32-bit.
  205. RAW_CHECK(false);
  206. #endif
  207. }
  208. }
  209. void UnlinkFileForIPC(const BrokerCommandSet& allowed_command_set,
  210. const BrokerPermissionList& permission_list,
  211. const std::string& requested_filename,
  212. BrokerSimpleMessage* message) {
  213. const char* file_to_access = nullptr;
  214. if (!CommandUnlinkIsSafe(allowed_command_set, permission_list,
  215. requested_filename.c_str(), &file_to_access)) {
  216. RAW_CHECK(message->AddIntToMessage(-permission_list.denied_errno()));
  217. return;
  218. }
  219. if (unlink(file_to_access) < 0) {
  220. RAW_CHECK(message->AddIntToMessage(-errno));
  221. return;
  222. }
  223. RAW_CHECK(message->AddIntToMessage(0));
  224. }
  225. // Handle a |command_type| request contained in |iter| and write the reply
  226. // to |reply|.
  227. bool HandleRemoteCommand(const BrokerCommandSet& allowed_command_set,
  228. const BrokerPermissionList& permission_list,
  229. BrokerSimpleMessage* message,
  230. BrokerSimpleMessage* reply,
  231. base::ScopedFD* opened_file) {
  232. // Message structure:
  233. // int: command type
  234. // char[]: pathname
  235. // int: flags
  236. int command_type;
  237. if (!message->ReadInt(&command_type))
  238. return false;
  239. switch (command_type) {
  240. case COMMAND_ACCESS: {
  241. const char* requested_filename;
  242. int flags = 0;
  243. if (!GetPathAndFlags(message, &requested_filename, &flags))
  244. return false;
  245. AccessFileForIPC(allowed_command_set, permission_list, requested_filename,
  246. flags, reply);
  247. break;
  248. }
  249. case COMMAND_MKDIR: {
  250. const char* requested_filename;
  251. int mode = 0;
  252. if (!GetPathAndFlags(message, &requested_filename, &mode))
  253. return false;
  254. MkdirFileForIPC(allowed_command_set, permission_list, requested_filename,
  255. mode, reply);
  256. break;
  257. }
  258. case COMMAND_OPEN: {
  259. const char* requested_filename;
  260. int flags = 0;
  261. if (!GetPathAndFlags(message, &requested_filename, &flags))
  262. return false;
  263. OpenFileForIPC(allowed_command_set, permission_list, requested_filename,
  264. flags, reply, opened_file);
  265. break;
  266. }
  267. case COMMAND_READLINK: {
  268. const char* filename;
  269. if (!message->ReadString(&filename))
  270. return false;
  271. ReadlinkFileForIPC(allowed_command_set, permission_list, filename, reply);
  272. break;
  273. }
  274. case COMMAND_RENAME: {
  275. const char* old_filename;
  276. if (!message->ReadString(&old_filename))
  277. return false;
  278. const char* new_filename;
  279. if (!message->ReadString(&new_filename))
  280. return false;
  281. RenameFileForIPC(allowed_command_set, permission_list, old_filename,
  282. new_filename, reply);
  283. break;
  284. }
  285. case COMMAND_RMDIR: {
  286. const char* requested_filename;
  287. if (!message->ReadString(&requested_filename))
  288. return false;
  289. RmdirFileForIPC(allowed_command_set, permission_list, requested_filename,
  290. reply);
  291. break;
  292. }
  293. case COMMAND_STAT:
  294. case COMMAND_STAT64: {
  295. const char* requested_filename;
  296. if (!message->ReadString(&requested_filename))
  297. return false;
  298. int follow_links;
  299. if (!message->ReadInt(&follow_links))
  300. return false;
  301. StatFileForIPC(allowed_command_set, permission_list,
  302. static_cast<BrokerCommand>(command_type),
  303. requested_filename, !!follow_links, reply);
  304. break;
  305. }
  306. case COMMAND_UNLINK: {
  307. const char* requested_filename;
  308. if (!message->ReadString(&requested_filename))
  309. return false;
  310. UnlinkFileForIPC(allowed_command_set, permission_list, requested_filename,
  311. reply);
  312. break;
  313. }
  314. default:
  315. LOG(ERROR) << "Invalid IPC command";
  316. return false;
  317. }
  318. return true;
  319. }
  320. } // namespace
  321. BrokerHost::BrokerHost(const BrokerSandboxConfig& policy,
  322. BrokerChannel::EndPoint ipc_channel)
  323. : policy_(policy), ipc_channel_(std::move(ipc_channel)) {}
  324. BrokerHost::~BrokerHost() = default;
  325. // Handle a request on the IPC channel ipc_channel_.
  326. // A request should have a file descriptor attached on which we will reply and
  327. // that we will then close.
  328. // A request should start with an int that will be used as the command type.
  329. void BrokerHost::LoopAndHandleRequests() {
  330. for (;;) {
  331. BrokerSimpleMessage message;
  332. errno = 0;
  333. base::ScopedFD temporary_ipc;
  334. const ssize_t msg_len =
  335. message.RecvMsgWithFlags(ipc_channel_.get(), 0, &temporary_ipc);
  336. if (msg_len == 0 || (msg_len == -1 && errno == ECONNRESET)) {
  337. // EOF from the client, or the client died, we should finish looping.
  338. return;
  339. }
  340. // The client sends exactly one file descriptor, on which we
  341. // will write the reply.
  342. if (msg_len < 0) {
  343. PLOG(ERROR) << "Error reading message from the client";
  344. continue;
  345. }
  346. BrokerSimpleMessage reply;
  347. base::ScopedFD opened_file;
  348. if (!HandleRemoteCommand(policy_.allowed_command_set,
  349. *policy_.file_permissions, &message, &reply,
  350. &opened_file)) {
  351. // Does not exit if we received a malformed message.
  352. LOG(ERROR) << "Received malformed message from the client";
  353. continue;
  354. }
  355. ssize_t sent = reply.SendMsg(temporary_ipc.get(), opened_file.get());
  356. if (sent < 0)
  357. LOG(ERROR) << "sent failed";
  358. }
  359. }
  360. } // namespace syscall_broker
  361. } // namespace sandbox