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