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