1/* 2 * Copyright (C) 2010 Apple Inc. All rights reserved. 3 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) 4 * Copyright (C) 2011 Igalia S.L. 5 * Copyright (C) 2013 Intel Corporation. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 18 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 26 * THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include "config.h" 30#include "SeccompBroker.h" 31 32#if ENABLE(SECCOMP_FILTERS) 33 34#include "ArgumentCoders.h" 35#include "Syscall.h" 36#include <errno.h> 37#include <fcntl.h> 38#include <seccomp.h> 39#include <sys/socket.h> 40#include <sys/stat.h> 41#include <sys/types.h> 42#include <unistd.h> 43 44#ifndef SYS_SECCOMP 45#define SYS_SECCOMP 1 46#endif 47 48static const size_t messageMaxSize = 4096; 49static const char onlineCPUCountPath[] = "/sys/devices/system/cpu/online"; 50 51namespace WebKit { 52 53class SeccompBrokerClient { 54public: 55 static SeccompBrokerClient& shared(int socket = -1); 56 ~SeccompBrokerClient(); 57 58 void dispatch(Syscall*) const; 59 60 bool handleIfOpeningOnlineCPUCount(mcontext_t*) const; 61 62private: 63 SeccompBrokerClient(int socket); 64 65 int m_socket; 66 int m_onlineCPUCountFd; 67 68 mutable Mutex m_socketLock; 69}; 70 71static ssize_t sendMessage(int socket, void* data, size_t size, int fd = -1) 72{ 73 ASSERT(size <= messageMaxSize); 74 75 struct msghdr message; 76 memset(&message, 0, sizeof(message)); 77 78 struct iovec iov; 79 memset(&iov, 0, sizeof(iov)); 80 iov.iov_base = data; 81 iov.iov_len = size; 82 83 message.msg_iov = &iov; 84 message.msg_iovlen = 1; 85 86 char control[CMSG_SPACE(sizeof(fd))]; 87 if (fd >= 0) { 88 message.msg_control = control; 89 message.msg_controllen = sizeof(control); 90 memset(message.msg_control, 0, message.msg_controllen); 91 92 struct cmsghdr* cmsg = CMSG_FIRSTHDR(&message); 93 cmsg->cmsg_level = SOL_SOCKET; 94 cmsg->cmsg_type = SCM_RIGHTS; 95 cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); 96 97 memmove(CMSG_DATA(cmsg), &fd, sizeof(fd)); 98 } 99 100 return sendmsg(socket, &message, 0); 101} 102 103static ssize_t receiveMessage(int socket, void* data, size_t size, int* fd = 0) 104{ 105 struct msghdr message; 106 memset(&message, 0, sizeof(message)); 107 108 struct iovec iov; 109 memset(&iov, 0, sizeof(iov)); 110 iov.iov_base = data; 111 iov.iov_len = size; 112 113 message.msg_iov = &iov; 114 message.msg_iovlen = 1; 115 116 char control[CMSG_SPACE(sizeof(fd))]; 117 message.msg_control = control; 118 message.msg_controllen = sizeof(control); 119 memset(message.msg_control, 0, message.msg_controllen); 120 121 ssize_t receivedBytes = recvmsg(socket, &message, 0); 122 123 if (fd && receivedBytes > 0) { 124 struct cmsghdr* cmsg = CMSG_FIRSTHDR(&message); 125 if (cmsg && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) 126 memcpy(fd, CMSG_DATA(cmsg), sizeof(*fd)); 127 else 128 *fd = -1; 129 } 130 131 return receivedBytes >= 0 ? receivedBytes : -errno; 132} 133 134static void SIGSYSHandler(int signal, siginfo_t* info, void* data) 135{ 136 if (signal != SIGSYS || info->si_code != SYS_SECCOMP) 137 CRASH(); 138 139 ucontext_t* ucontext = static_cast<ucontext_t*>(data); 140 if (!ucontext) 141 CRASH(); 142 143 SeccompBrokerClient* client = &SeccompBrokerClient::shared(); 144 145 if (client->handleIfOpeningOnlineCPUCount(&ucontext->uc_mcontext)) 146 return; 147 148 // createFromContext might return a nullptr if it is able to resolve the 149 // syscall locally without sending it to the broker process. In this case, 150 // we just return. Examples of locally resolved syscalls are the ones 151 // with cached resources and invalid arguments. 152 std::unique_ptr<Syscall> syscall = Syscall::createFromContext(ucontext); 153 if (!syscall) 154 return; 155 156 client->dispatch(syscall.get()); 157} 158 159static void registerSIGSYSHandler() 160{ 161 struct sigaction action; 162 memset(&action, 0, sizeof(action)); 163 action.sa_sigaction = &SIGSYSHandler; 164 action.sa_flags = SA_SIGINFO | SA_NODEFER; 165 166 if (sigaction(SIGSYS, &action, 0) < 0) 167 CRASH(); 168 169 sigset_t mask; 170 sigemptyset(&mask); 171 sigaddset(&mask, SIGSYS); 172 173 if (sigprocmask(SIG_UNBLOCK, &mask, 0) < 0) 174 CRASH(); 175} 176 177SeccompBrokerClient& SeccompBrokerClient::shared(int socket) 178{ 179 DEPRECATED_DEFINE_STATIC_LOCAL(SeccompBrokerClient, brokerClient, (socket)); 180 181 return brokerClient; 182} 183 184SeccompBrokerClient::SeccompBrokerClient(int socket) 185 : m_socket(socket) 186 , m_onlineCPUCountFd(open(onlineCPUCountPath, O_RDONLY)) 187{ 188 ASSERT(m_socket >= 0 && m_onlineCPUCountFd >= 0); 189} 190 191SeccompBrokerClient::~SeccompBrokerClient() 192{ 193 close(m_socket); 194 close(m_onlineCPUCountFd); 195} 196 197void SeccompBrokerClient::dispatch(Syscall* syscall) const 198{ 199 auto encoder = std::make_unique<IPC::ArgumentEncoder>(); 200 encoder->encode(*syscall); 201 202 char buffer[messageMaxSize]; 203 ssize_t receivedBytes = 0; 204 int fd = -1; 205 206 m_socketLock.lock(); 207 208 if (sendMessage(m_socket, encoder->buffer(), encoder->bufferSize()) < 0) 209 CRASH(); 210 211 while (true) { 212 receivedBytes = receiveMessage(m_socket, &buffer, sizeof(buffer), &fd); 213 if (receivedBytes > 0) 214 break; 215 216 if (receivedBytes != -EINTR) 217 CRASH(); 218 } 219 220 m_socketLock.unlock(); 221 222 auto decoder = std::make_unique<IPC::ArgumentDecoder>((const uint8_t*) buffer, receivedBytes); 223 std::unique_ptr<SyscallResult> result = SyscallResult::createFromDecoder(decoder.get(), fd); 224 if (!result) 225 CRASH(); 226 227 syscall->setResult(result.get()); 228} 229 230bool SeccompBrokerClient::handleIfOpeningOnlineCPUCount(mcontext_t* context) const 231{ 232 if (context->gregs[REG_SYSCALL] != __NR_open) 233 return false; 234 235 const char *path = reinterpret_cast<char*>(context->gregs[REG_ARG0]); 236 if (strcmp(onlineCPUCountPath, path)) 237 return false; 238 239 // Malloc will eventually check the number of online CPUs (i.e being 240 // scheduled) present on the system by opening a special file. If it does 241 // that in the middle of the SIGSYS signal handler, it might trigger a 242 // recursive attempt of proxying the open() syscall to the broker. 243 // Because of that, we cache this resource. 244 context->gregs[REG_SYSCALL] = dup(m_onlineCPUCountFd); 245 246 return true; 247} 248 249void SeccompBroker::launchProcess(SeccompFilters* filters, const SyscallPolicy& policy) 250{ 251 static bool initialized = false; 252 if (initialized) 253 return; 254 255 // The sigprocmask filters bellow are needed to trap sigprocmask() 256 // so we can prevent the running processes from blocking SIGSYS. 257 filters->addRule("sigprocmask", SeccompFilters::Trap, 258 0, SeccompFilters::Equal, SIG_BLOCK, 259 1, SeccompFilters::NotEqual, 0); 260 filters->addRule("sigprocmask", SeccompFilters::Trap, 261 0, SeccompFilters::Equal, SIG_SETMASK, 262 1, SeccompFilters::NotEqual, 0); 263 filters->addRule("rt_sigprocmask", SeccompFilters::Trap, 264 0, SeccompFilters::Equal, SIG_BLOCK, 265 1, SeccompFilters::NotEqual, 0); 266 filters->addRule("rt_sigprocmask", SeccompFilters::Trap, 267 0, SeccompFilters::Equal, SIG_SETMASK, 268 1, SeccompFilters::NotEqual, 0); 269 270 // The sigaction filters bellow are needed to trap sigaction() 271 // so we can prevent the running processes from handling SIGSYS. 272 filters->addRule("sigaction", SeccompFilters::Trap, 273 0, SeccompFilters::Equal, SIGSYS); 274 filters->addRule("rt_sigaction", SeccompFilters::Trap, 275 0, SeccompFilters::Equal, SIGSYS); 276 277 SeccompBroker seccompBroker; 278 seccompBroker.setSyscallPolicy(policy); 279 seccompBroker.initialize(); 280 281 initialized = true; 282} 283 284void SeccompBroker::initialize() 285{ 286 int sockets[2]; 287 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0) 288 CRASH(); 289 290 pid_t pid = fork(); 291 if (pid) { // Sandboxed process. 292 close(sockets[1]); 293 SeccompBrokerClient::shared(sockets[0]); 294 registerSIGSYSHandler(); 295 } else { // Broker. 296 // TODO: The broker should setup seccomp filters 297 // for itself and block everything else other than 298 // the minimal set of syscalls needed to execute the 299 // syscalls it is suppose to proxy. 300 close(sockets[0]); 301 runLoop(sockets[1]); 302 } 303} 304 305NO_RETURN void SeccompBroker::runLoop(int socket) 306{ 307#ifndef NDEBUG 308 int i = STDERR_FILENO + 1; 309#else 310 int i = 0; 311#endif 312 // Close all inherited file descriptors other 313 // than the socket to the sandboxed process. 314 for (; i < FD_SETSIZE; ++i) 315 if (i != socket) 316 close(i); 317 318 while (true) { 319 char buffer[messageMaxSize]; 320 ssize_t receivedBytes = receiveMessage(socket, &buffer, sizeof(buffer)); 321 if (receivedBytes == -EINTR) 322 continue; 323 324 if (receivedBytes <= 0) 325 exit(receivedBytes ? EXIT_FAILURE : EXIT_SUCCESS); 326 327 auto decoder = std::make_unique<IPC::ArgumentDecoder>((const uint8_t*) buffer, receivedBytes); 328 std::unique_ptr<Syscall> syscall = Syscall::createFromDecoder(decoder.get()); 329 if (!syscall) 330 exit(EXIT_FAILURE); 331 332 std::unique_ptr<SyscallResult> result = syscall->execute(m_policy); 333 if (!result) 334 exit(EXIT_FAILURE); 335 336 auto encoder = std::make_unique<IPC::ArgumentEncoder>(); 337 encoder->encode(*result); 338 339 Vector<IPC::Attachment> attachments = encoder->releaseAttachments(); 340 int fd = attachments.size() == 1 ? attachments[0].fileDescriptor() : -1; 341 342 // The client is down, the broker should go away. 343 if (sendMessage(socket, encoder->buffer(), encoder->bufferSize(), fd) < 0) 344 exit(EXIT_SUCCESS); 345 } 346} 347 348} // namespace WebKit 349 350#endif // ENABLE(SECCOMP_FILTERS) 351