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_host.h"
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <stddef.h>
10 #include <sys/socket.h>
11 #include <sys/stat.h>
12 #include <sys/syscall.h>
13 #include <sys/types.h>
14 #include <unistd.h>
15
16 #include <string>
17 #include <utility>
18 #include <vector>
19
20 #include "base/files/scoped_file.h"
21 #include "base/logging.h"
22 #include "base/pickle.h"
23 #include "base/posix/eintr_wrapper.h"
24 #include "base/posix/unix_domain_socket_linux.h"
25 #include "base/third_party/valgrind/valgrind.h"
26 #include "sandbox/linux/syscall_broker/broker_common.h"
27 #include "sandbox/linux/syscall_broker/broker_policy.h"
28 #include "sandbox/linux/system_headers/linux_syscalls.h"
29
30 namespace sandbox {
31
32 namespace syscall_broker {
33
34 namespace {
35
IsRunningOnValgrind()36 bool IsRunningOnValgrind() {
37 return RUNNING_ON_VALGRIND;
38 }
39
40 // A little open(2) wrapper to handle some oddities for us. In the general case
41 // make a direct system call since we want to keep in control of the broker
42 // process' system calls profile to be able to loosely sandbox it.
sys_open(const char * pathname,int flags)43 int sys_open(const char* pathname, int flags) {
44 // Hardcode mode to rw------- when creating files.
45 int mode;
46 if (flags & O_CREAT) {
47 mode = 0600;
48 } else {
49 mode = 0;
50 }
51 if (IsRunningOnValgrind()) {
52 // Valgrind does not support AT_FDCWD, just use libc's open() in this case.
53 return open(pathname, flags, mode);
54 } else {
55 return syscall(__NR_openat, AT_FDCWD, pathname, flags, mode);
56 }
57 }
58
59 // Open |requested_filename| with |flags| if allowed by our policy.
60 // Write the syscall return value (-errno) to |write_pickle| and append
61 // a file descriptor to |opened_files| if relevant.
OpenFileForIPC(const BrokerPolicy & policy,const std::string & requested_filename,int flags,base::Pickle * write_pickle,std::vector<int> * opened_files)62 void OpenFileForIPC(const BrokerPolicy& policy,
63 const std::string& requested_filename,
64 int flags,
65 base::Pickle* write_pickle,
66 std::vector<int>* opened_files) {
67 DCHECK(write_pickle);
68 DCHECK(opened_files);
69 const char* file_to_open = NULL;
70 bool unlink_after_open = false;
71 const bool safe_to_open_file = policy.GetFileNameIfAllowedToOpen(
72 requested_filename.c_str(), flags, &file_to_open, &unlink_after_open);
73
74 if (safe_to_open_file) {
75 CHECK(file_to_open);
76 int opened_fd = sys_open(file_to_open, flags);
77 if (opened_fd < 0) {
78 write_pickle->WriteInt(-errno);
79 } else {
80 // Success.
81 if (unlink_after_open) {
82 unlink(file_to_open);
83 }
84 opened_files->push_back(opened_fd);
85 write_pickle->WriteInt(0);
86 }
87 } else {
88 write_pickle->WriteInt(-policy.denied_errno());
89 }
90 }
91
92 // Perform access(2) on |requested_filename| with mode |mode| if allowed by our
93 // policy. Write the syscall return value (-errno) to |write_pickle|.
AccessFileForIPC(const BrokerPolicy & policy,const std::string & requested_filename,int mode,base::Pickle * write_pickle)94 void AccessFileForIPC(const BrokerPolicy& policy,
95 const std::string& requested_filename,
96 int mode,
97 base::Pickle* write_pickle) {
98 DCHECK(write_pickle);
99 const char* file_to_access = NULL;
100 const bool safe_to_access_file = policy.GetFileNameIfAllowedToAccess(
101 requested_filename.c_str(), mode, &file_to_access);
102
103 if (safe_to_access_file) {
104 CHECK(file_to_access);
105 int access_ret = access(file_to_access, mode);
106 int access_errno = errno;
107 if (!access_ret)
108 write_pickle->WriteInt(0);
109 else
110 write_pickle->WriteInt(-access_errno);
111 } else {
112 write_pickle->WriteInt(-policy.denied_errno());
113 }
114 }
115
116 // Handle a |command_type| request contained in |iter| and send the reply
117 // on |reply_ipc|.
118 // Currently COMMAND_OPEN and COMMAND_ACCESS are supported.
HandleRemoteCommand(const BrokerPolicy & policy,IPCCommand command_type,int reply_ipc,base::PickleIterator iter)119 bool HandleRemoteCommand(const BrokerPolicy& policy,
120 IPCCommand command_type,
121 int reply_ipc,
122 base::PickleIterator iter) {
123 // Currently all commands have two arguments: filename and flags.
124 std::string requested_filename;
125 int flags = 0;
126 if (!iter.ReadString(&requested_filename) || !iter.ReadInt(&flags))
127 return false;
128
129 base::Pickle write_pickle;
130 std::vector<int> opened_files;
131
132 switch (command_type) {
133 case COMMAND_ACCESS:
134 AccessFileForIPC(policy, requested_filename, flags, &write_pickle);
135 break;
136 case COMMAND_OPEN:
137 OpenFileForIPC(
138 policy, requested_filename, flags, &write_pickle, &opened_files);
139 break;
140 default:
141 LOG(ERROR) << "Invalid IPC command";
142 break;
143 }
144
145 CHECK_LE(write_pickle.size(), kMaxMessageLength);
146 ssize_t sent = base::UnixDomainSocket::SendMsg(
147 reply_ipc, write_pickle.data(), write_pickle.size(), opened_files);
148
149 // Close anything we have opened in this process.
150 for (std::vector<int>::iterator it = opened_files.begin();
151 it != opened_files.end();
152 ++it) {
153 int ret = IGNORE_EINTR(close(*it));
154 DCHECK(!ret) << "Could not close file descriptor";
155 }
156
157 if (sent <= 0) {
158 LOG(ERROR) << "Could not send IPC reply";
159 return false;
160 }
161 return true;
162 }
163
164 } // namespace
165
BrokerHost(const BrokerPolicy & broker_policy,BrokerChannel::EndPoint ipc_channel)166 BrokerHost::BrokerHost(const BrokerPolicy& broker_policy,
167 BrokerChannel::EndPoint ipc_channel)
168 : broker_policy_(broker_policy), ipc_channel_(std::move(ipc_channel)) {}
169
~BrokerHost()170 BrokerHost::~BrokerHost() {
171 }
172
173 // Handle a request on the IPC channel ipc_channel_.
174 // A request should have a file descriptor attached on which we will reply and
175 // that we will then close.
176 // A request should start with an int that will be used as the command type.
HandleRequest() const177 BrokerHost::RequestStatus BrokerHost::HandleRequest() const {
178 std::vector<base::ScopedFD> fds;
179 char buf[kMaxMessageLength];
180 errno = 0;
181 const ssize_t msg_len = base::UnixDomainSocket::RecvMsg(
182 ipc_channel_.get(), buf, sizeof(buf), &fds);
183
184 if (msg_len == 0 || (msg_len == -1 && errno == ECONNRESET)) {
185 // EOF from the client, or the client died, we should die.
186 return RequestStatus::LOST_CLIENT;
187 }
188
189 // The client should send exactly one file descriptor, on which we
190 // will write the reply.
191 if (msg_len < 0 || fds.size() != 1 || fds[0].get() < 0) {
192 PLOG(ERROR) << "Error reading message from the client";
193 return RequestStatus::FAILURE;
194 }
195
196 base::ScopedFD temporary_ipc(std::move(fds[0]));
197
198 base::Pickle pickle(buf, msg_len);
199 base::PickleIterator iter(pickle);
200 int command_type;
201 if (iter.ReadInt(&command_type)) {
202 bool command_handled = false;
203 // Go through all the possible IPC messages.
204 switch (command_type) {
205 case COMMAND_ACCESS:
206 case COMMAND_OPEN:
207 // We reply on the file descriptor sent to us via the IPC channel.
208 command_handled = HandleRemoteCommand(
209 broker_policy_, static_cast<IPCCommand>(command_type),
210 temporary_ipc.get(), iter);
211 break;
212 default:
213 NOTREACHED();
214 break;
215 }
216
217 if (command_handled) {
218 return RequestStatus::SUCCESS;
219 } else {
220 return RequestStatus::FAILURE;
221 }
222
223 NOTREACHED();
224 }
225
226 LOG(ERROR) << "Error parsing IPC request";
227 return RequestStatus::FAILURE;
228 }
229
230 } // namespace syscall_broker
231
232 } // namespace sandbox
233