fd342da7c5b20846b9077eb58620fe7489b2ecb4
[WebKit-https.git] / Source / WebKit2 / Shared / linux / SeccompFilters / SeccompBroker.cpp
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
48 static const size_t messageMaxSize = 4096;
49
50 namespace WebKit {
51
52 class SeccompBrokerClient {
53 public:
54     static SeccompBrokerClient& singleton(int socket = -1);
55     ~SeccompBrokerClient();
56
57     void dispatch(Syscall*) const;
58
59     bool handleIfOpeningCachedFile(mcontext_t*) const;
60
61 private:
62     SeccompBrokerClient(int socket);
63
64     void cacheFile(const char* path);
65
66     int m_socket;
67
68     mutable DeprecatedMutex m_socketLock;
69
70     // Maps files that may be read by malloc() to open file descriptors.
71     HashMap<String, int> m_fileDescriptorCache;
72 };
73
74 static ssize_t sendMessage(int socket, void* data, size_t size, int fd = -1)
75 {
76     ASSERT(size <= messageMaxSize);
77
78     struct msghdr message;
79     memset(&message, 0, sizeof(message));
80
81     struct iovec iov;
82     memset(&iov, 0, sizeof(iov));
83     iov.iov_base = data;
84     iov.iov_len = size;
85
86     message.msg_iov = &iov;
87     message.msg_iovlen = 1;
88
89     char control[CMSG_SPACE(sizeof(fd))];
90     if (fd >= 0) {
91         message.msg_control = control;
92         message.msg_controllen = sizeof(control);
93         memset(message.msg_control, 0, message.msg_controllen);
94
95         struct cmsghdr* cmsg = CMSG_FIRSTHDR(&message);
96         cmsg->cmsg_level = SOL_SOCKET;
97         cmsg->cmsg_type = SCM_RIGHTS;
98         cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
99
100         memmove(CMSG_DATA(cmsg), &fd, sizeof(fd));
101     }
102
103     return sendmsg(socket, &message, 0);
104 }
105
106 static ssize_t receiveMessage(int socket, void* data, size_t size, int* fd = 0)
107 {
108     struct msghdr message;
109     memset(&message, 0, sizeof(message));
110
111     struct iovec iov;
112     memset(&iov, 0, sizeof(iov));
113     iov.iov_base = data;
114     iov.iov_len = size;
115
116     message.msg_iov = &iov;
117     message.msg_iovlen = 1;
118
119     char control[CMSG_SPACE(sizeof(fd))];
120     message.msg_control = control;
121     message.msg_controllen = sizeof(control);
122     memset(message.msg_control, 0, message.msg_controllen);
123
124     ssize_t receivedBytes = recvmsg(socket, &message, 0);
125
126     if (fd && receivedBytes > 0) {
127         struct cmsghdr* cmsg = CMSG_FIRSTHDR(&message);
128         if (cmsg && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
129             memcpy(fd, CMSG_DATA(cmsg), sizeof(*fd));
130         else
131             *fd = -1;
132     }
133
134     return receivedBytes >= 0 ? receivedBytes : -errno;
135 }
136
137 static void SIGSYSHandler(int signal, siginfo_t* info, void* data)
138 {
139     if (signal != SIGSYS || info->si_code != SYS_SECCOMP)
140         CRASH();
141
142     ucontext_t* ucontext = static_cast<ucontext_t*>(data);
143     if (!ucontext)
144         CRASH();
145
146     SeccompBrokerClient* client = &SeccompBrokerClient::singleton();
147
148     if (client->handleIfOpeningCachedFile(&ucontext->uc_mcontext))
149         return;
150
151     // createFromContext might return a nullptr if it is able to resolve the
152     // syscall locally without sending it to the broker process. In this case,
153     // we just return. Examples of locally resolved syscalls are the ones
154     // with cached resources and invalid arguments.
155     std::unique_ptr<Syscall> syscall = Syscall::createFromContext(ucontext);
156     if (!syscall)
157         return;
158
159     client->dispatch(syscall.get());
160 }
161
162 static void registerSIGSYSHandler()
163 {
164     struct sigaction action;
165     memset(&action, 0, sizeof(action));
166     action.sa_sigaction = &SIGSYSHandler;
167     action.sa_flags = SA_SIGINFO | SA_NODEFER;
168
169     if (sigaction(SIGSYS, &action, 0) < 0)
170         CRASH();
171
172     sigset_t mask;
173     sigemptyset(&mask);
174     sigaddset(&mask, SIGSYS);
175
176     if (sigprocmask(SIG_UNBLOCK, &mask, 0) < 0)
177         CRASH();
178 }
179
180 SeccompBrokerClient& SeccompBrokerClient::singleton(int socket)
181 {
182     DEPRECATED_DEFINE_STATIC_LOCAL(SeccompBrokerClient, brokerClient, (socket));
183
184     return brokerClient;
185 }
186
187 SeccompBrokerClient::SeccompBrokerClient(int socket)
188     : m_socket(socket)
189 {
190     ASSERT(m_socket >= 0);
191
192     cacheFile("/proc/sys/vm/overcommit_memory");
193     cacheFile("/sys/devices/system/cpu/online");
194 }
195
196 SeccompBrokerClient::~SeccompBrokerClient()
197 {
198     for (int fd : m_fileDescriptorCache.values())
199         close(fd);
200     close(m_socket);
201 }
202
203 void SeccompBrokerClient::dispatch(Syscall* syscall) const
204 {
205     auto encoder = std::make_unique<IPC::ArgumentEncoder>();
206     encoder->encode(*syscall);
207
208     char buffer[messageMaxSize];
209     ssize_t receivedBytes = 0;
210     int fd = -1;
211
212     m_socketLock.lock();
213
214     if (sendMessage(m_socket, encoder->buffer(), encoder->bufferSize()) < 0)
215         CRASH();
216
217     while (true) {
218         receivedBytes = receiveMessage(m_socket, &buffer, sizeof(buffer), &fd);
219         if (receivedBytes > 0)
220             break;
221
222         if (receivedBytes != -EINTR)
223             CRASH();
224     }
225
226     m_socketLock.unlock();
227
228     auto decoder = std::make_unique<IPC::ArgumentDecoder>((const uint8_t*) buffer, receivedBytes);
229     std::unique_ptr<SyscallResult> result = SyscallResult::createFromDecoder(decoder.get(), fd);
230     if (!result)
231         CRASH();
232
233     syscall->setResult(result.get());
234 }
235
236 bool SeccompBrokerClient::handleIfOpeningCachedFile(mcontext_t* context) const
237 {
238     if (context->gregs[REG_SYSCALL] != __NR_open)
239         return false;
240
241     const char *path = reinterpret_cast<char*>(context->gregs[REG_ARG0]);
242
243     auto iter = m_fileDescriptorCache.find(path);
244     if (iter == m_fileDescriptorCache.end())
245         return false;
246
247     // Malloc will eventually check the number of online CPUs (i.e being
248     // scheduled) present on the system by opening a special file. If it does
249     // that in the middle of the SIGSYS signal handler, it might trigger a
250     // recursive attempt of proxying the open() syscall to the broker. The same
251     // problem occurs if malloc() checks the memory overcommit policy. Because
252     // of that, we cache these resources.
253     context->gregs[REG_SYSCALL] = dup(iter->value);
254
255     return true;
256 }
257
258 void SeccompBrokerClient::cacheFile(const char* path)
259 {
260     int fd = open(path, O_RDONLY);
261     ASSERT(fd >= 0);
262     m_fileDescriptorCache.set(path, fd);
263 }
264
265 void SeccompBroker::launchProcess(SeccompFilters* filters, const SyscallPolicy& policy)
266 {
267     static bool initialized = false;
268     if (initialized)
269         return;
270
271     if (filters->defaultAction() == SeccompFilters::Allow) {
272         // The sigprocmask filters bellow are needed to trap sigprocmask()
273         // so we can prevent the running processes from blocking SIGSYS.
274         filters->addRule("sigprocmask", SeccompFilters::Trap,
275             0, SeccompFilters::Equal, SIG_BLOCK,
276             1, SeccompFilters::NotEqual, 0);
277         filters->addRule("sigprocmask", SeccompFilters::Trap,
278             0, SeccompFilters::Equal, SIG_SETMASK,
279             1, SeccompFilters::NotEqual, 0);
280         filters->addRule("rt_sigprocmask", SeccompFilters::Trap,
281             0, SeccompFilters::Equal, SIG_BLOCK,
282             1, SeccompFilters::NotEqual, 0);
283         filters->addRule("rt_sigprocmask", SeccompFilters::Trap,
284             0, SeccompFilters::Equal, SIG_SETMASK,
285             1, SeccompFilters::NotEqual, 0);
286
287         // The sigaction filters bellow are needed to trap sigaction()
288         // so we can prevent the running processes from handling SIGSYS.
289         filters->addRule("sigaction", SeccompFilters::Trap,
290             0, SeccompFilters::Equal, SIGSYS);
291         filters->addRule("rt_sigaction", SeccompFilters::Trap,
292             0, SeccompFilters::Equal, SIGSYS);
293     }
294
295     if (filters->defaultAction() != SeccompFilters::Allow) {
296         // Needed for the SIGSYS handler to work.
297         filters->addRule("sigreturn", SeccompFilters::Allow);
298         filters->addRule("rt_sigreturn", SeccompFilters::Allow);
299
300         // Needed by malloc and free. We must never trap a syscall inside either
301         // because we need them in our SIGSYS handler and they are nonreentrant.
302         filters->addRule("brk", SeccompFilters::Allow);
303     }
304
305     SeccompBroker seccompBroker;
306     seccompBroker.setSyscallPolicy(policy);
307     seccompBroker.initialize();
308
309     initialized = true;
310 }
311
312 void SeccompBroker::initialize()
313 {
314     int sockets[2];
315     if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0)
316         CRASH();
317
318     pid_t pid = fork();
319     if (pid) { // Sandboxed process.
320         close(sockets[1]);
321         SeccompBrokerClient::singleton(sockets[0]);
322         registerSIGSYSHandler();
323     } else { // Broker.
324         // TODO: The broker should setup seccomp filters
325         // for itself and block everything else other than
326         // the minimal set of syscalls needed to execute the
327         // syscalls it is suppose to proxy.
328         close(sockets[0]);
329         runLoop(sockets[1]);
330     }
331 }
332
333 NO_RETURN void SeccompBroker::runLoop(int socket)
334 {
335     // Close unnecessary inherited file descriptors.
336     for (int i = STDERR_FILENO + 1; i < FD_SETSIZE; ++i) {
337         if (i != socket)
338             close(i);
339     }
340
341     while (true) {
342         char buffer[messageMaxSize];
343         ssize_t receivedBytes = receiveMessage(socket, &buffer, sizeof(buffer));
344         if (receivedBytes == -EINTR)
345             continue;
346
347         if (receivedBytes <= 0)
348             exit(receivedBytes ? EXIT_FAILURE : EXIT_SUCCESS);
349
350         auto decoder = std::make_unique<IPC::ArgumentDecoder>((const uint8_t*) buffer, receivedBytes);
351         std::unique_ptr<Syscall> syscall = Syscall::createFromDecoder(decoder.get());
352         if (!syscall)
353             exit(EXIT_FAILURE);
354
355         std::unique_ptr<SyscallResult> result = syscall->execute(m_policy);
356         if (!result)
357             exit(EXIT_FAILURE);
358
359         auto encoder = std::make_unique<IPC::ArgumentEncoder>();
360         encoder->encode(*result);
361
362         Vector<IPC::Attachment> attachments = encoder->releaseAttachments();
363         int fd = attachments.size() == 1 ? attachments[0].fileDescriptor() : -1;
364
365         // The client is down, the broker should go away.
366         if (sendMessage(socket, encoder->buffer(), encoder->bufferSize(), fd) < 0)
367             exit(EXIT_SUCCESS);
368     }
369 }
370
371 } // namespace WebKit
372
373 #endif // ENABLE(SECCOMP_FILTERS)