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 "sandbox/linux/syscall_broker/broker_common.h"
26 #include "sandbox/linux/syscall_broker/broker_policy.h"
27 #include "sandbox/linux/system_headers/linux_syscalls.h"
28 #include "third_party/valgrind/valgrind.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