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 "content/browser/sandbox_ipc_linux.h"
6 
7 #include <fcntl.h>
8 #include <stddef.h>
9 #include <stdint.h>
10 #include <string.h>
11 #include <sys/poll.h>
12 #include <sys/socket.h>
13 #include <sys/stat.h>
14 
15 #include "base/command_line.h"
16 #include "base/files/scoped_file.h"
17 #include "base/linux_util.h"
18 #include "base/logging.h"
19 #include "base/memory/platform_shared_memory_region.h"
20 #include "base/posix/eintr_wrapper.h"
21 #include "base/posix/unix_domain_socket.h"
22 #include "base/process/launch.h"
23 #include "base/stl_util.h"
24 #include "base/strings/string_number_conversions.h"
25 #include "content/public/common/content_switches.h"
26 #include "sandbox/linux/services/libc_interceptor.h"
27 #include "sandbox/policy/linux/sandbox_linux.h"
28 
29 namespace content {
30 
31 const size_t kMaxSandboxIPCMessagePayloadSize = 64;
32 
33 // static
SandboxIPCHandler(int lifeline_fd,int browser_socket)34 SandboxIPCHandler::SandboxIPCHandler(int lifeline_fd, int browser_socket)
35     : lifeline_fd_(lifeline_fd), browser_socket_(browser_socket) {}
36 
Run()37 void SandboxIPCHandler::Run() {
38   struct pollfd pfds[2];
39   pfds[0].fd = lifeline_fd_;
40   pfds[0].events = POLLIN;
41   pfds[1].fd = browser_socket_;
42   pfds[1].events = POLLIN;
43 
44   int failed_polls = 0;
45   for (;;) {
46     const int r =
47         HANDLE_EINTR(poll(pfds, base::size(pfds), -1 /* no timeout */));
48     // '0' is not a possible return value with no timeout.
49     DCHECK_NE(0, r);
50     if (r < 0) {
51       PLOG(WARNING) << "poll";
52       if (failed_polls++ == 3) {
53         LOG(FATAL) << "poll(2) failing. SandboxIPCHandler aborting.";
54         return;
55       }
56       continue;
57     }
58 
59     failed_polls = 0;
60 
61     // The browser process will close the other end of this pipe on shutdown,
62     // so we should exit.
63     if (pfds[0].revents) {
64       break;
65     }
66 
67     // If poll(2) reports an error condition in this fd,
68     // we assume the zygote is gone and we exit the loop.
69     if (pfds[1].revents & (POLLERR | POLLHUP)) {
70       break;
71     }
72 
73     if (pfds[1].revents & POLLIN) {
74       HandleRequestFromChild(browser_socket_);
75     }
76   }
77 
78   VLOG(1) << "SandboxIPCHandler stopping.";
79 }
80 
HandleRequestFromChild(int fd)81 void SandboxIPCHandler::HandleRequestFromChild(int fd) {
82   std::vector<base::ScopedFD> fds;
83 
84   // A FontConfigIPC::METHOD_MATCH message could be kMaxFontFamilyLength
85   // bytes long (this is the largest message type).
86   // The size limit  used to be FontConfigIPC::kMaxFontFamilyLength which was
87   // 2048, but we do not receive FontConfig IPC here anymore. The only payloads
88   // here are sandbox::policy::SandboxLinux::METHOD_MAKE_SHARED_MEMORY_SEGMENT
89   // and HandleLocalTime from libc_interceptor for which
90   // kMaxSandboxIPCMessagePayloadSize set to 64 should be plenty.
91   // 128 bytes padding are necessary so recvmsg() does not return MSG_TRUNC
92   // error for a maximum length message.
93   char buf[kMaxSandboxIPCMessagePayloadSize + 128];
94 
95   const ssize_t len =
96       base::UnixDomainSocket::RecvMsg(fd, buf, sizeof(buf), &fds);
97   if (len == -1) {
98     // TODO: should send an error reply, or the sender might block forever.
99     if (errno == EMSGSIZE) {
100       NOTREACHED() << "Sandbox host message is larger than "
101                       "kMaxSandboxIPCMessagePayloadSize";
102     } else {
103       PLOG(ERROR) << "Recvmsg failed";
104       NOTREACHED();
105     }
106     return;
107   }
108   if (fds.empty())
109     return;
110 
111   base::Pickle pickle(buf, len);
112   base::PickleIterator iter(pickle);
113 
114   int kind;
115   if (!iter.ReadInt(&kind))
116     return;
117 
118   // Give sandbox first shot at request, if it is not handled, then
119   // false is returned and we continue on.
120   if (sandbox::HandleInterceptedCall(kind, fd, iter, fds))
121     return;
122 
123   if (kind ==
124       sandbox::policy::SandboxLinux::METHOD_MAKE_SHARED_MEMORY_SEGMENT) {
125     HandleMakeSharedMemorySegment(fd, iter, fds);
126     return;
127   }
128   NOTREACHED();
129 }
130 
HandleMakeSharedMemorySegment(int fd,base::PickleIterator iter,const std::vector<base::ScopedFD> & fds)131 void SandboxIPCHandler::HandleMakeSharedMemorySegment(
132     int fd,
133     base::PickleIterator iter,
134     const std::vector<base::ScopedFD>& fds) {
135   uint32_t size;
136   if (!iter.ReadUInt32(&size))
137     return;
138   // TODO(crbug.com/982879): executable shared memory should be removed when
139   // NaCl is unshipped.
140   bool executable;
141   if (!iter.ReadBool(&executable))
142     return;
143   base::ScopedFD shm_fd;
144   if (executable) {
145     shm_fd =
146         base::subtle::PlatformSharedMemoryRegion::ExecutableRegion::CreateFD(
147             size);
148   } else {
149     base::subtle::PlatformSharedMemoryRegion region =
150         base::subtle::PlatformSharedMemoryRegion::CreateUnsafe(size);
151     shm_fd = std::move(region.PassPlatformHandle().fd);
152   }
153   base::Pickle reply;
154   SendRendererReply(fds, reply, shm_fd.get());
155   // shm_fd will close the handle which is no longer needed by this process.
156 }
157 
SendRendererReply(const std::vector<base::ScopedFD> & fds,const base::Pickle & reply,int reply_fd)158 void SandboxIPCHandler::SendRendererReply(
159     const std::vector<base::ScopedFD>& fds,
160     const base::Pickle& reply,
161     int reply_fd) {
162   struct msghdr msg;
163   memset(&msg, 0, sizeof(msg));
164   struct iovec iov = {const_cast<void*>(reply.data()), reply.size()};
165   msg.msg_iov = &iov;
166   msg.msg_iovlen = 1;
167 
168   char control_buffer[CMSG_SPACE(sizeof(reply_fd))];
169 
170   if (reply_fd != -1) {
171     struct stat st;
172     if (fstat(reply_fd, &st) == 0 && S_ISDIR(st.st_mode)) {
173       LOG(FATAL) << "Tried to send a directory descriptor over sandbox IPC";
174       // We must never send directory descriptors to a sandboxed process
175       // because they can use openat with ".." elements in the path in order
176       // to escape the sandbox and reach the real filesystem.
177     }
178 
179     struct cmsghdr* cmsg;
180     msg.msg_control = control_buffer;
181     msg.msg_controllen = sizeof(control_buffer);
182     cmsg = CMSG_FIRSTHDR(&msg);
183     cmsg->cmsg_level = SOL_SOCKET;
184     cmsg->cmsg_type = SCM_RIGHTS;
185     cmsg->cmsg_len = CMSG_LEN(sizeof(reply_fd));
186     memcpy(CMSG_DATA(cmsg), &reply_fd, sizeof(reply_fd));
187     msg.msg_controllen = cmsg->cmsg_len;
188   }
189 
190   if (HANDLE_EINTR(sendmsg(fds[0].get(), &msg, MSG_DONTWAIT)) < 0)
191     PLOG(ERROR) << "sendmsg";
192 }
193 
~SandboxIPCHandler()194 SandboxIPCHandler::~SandboxIPCHandler() {
195   if (IGNORE_EINTR(close(lifeline_fd_)) < 0)
196     PLOG(ERROR) << "close";
197   if (IGNORE_EINTR(close(browser_socket_)) < 0)
198     PLOG(ERROR) << "close";
199 }
200 
201 }  // namespace content
202