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 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. 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. 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|. 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. 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 166 BrokerHost::BrokerHost(const BrokerPolicy& broker_policy, 167 BrokerChannel::EndPoint ipc_channel) 168 : broker_policy_(broker_policy), ipc_channel_(std::move(ipc_channel)) {} 169 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. 177 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