OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 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 "debug.h" | |
6 #include "sandbox_impl.h" | |
7 | |
8 namespace playground { | |
9 | |
10 #if defined(__NR_socket) | |
11 | |
12 ssize_t Sandbox::sandbox_recvfrom(int sockfd, void* buf, size_t len, int flags, | |
13 void* from, socklen_t* fromlen) { | |
14 long long tm; | |
15 Debug::syscall(&tm, __NR_recvfrom, "Executing handler"); | |
16 | |
17 SysCalls sys; | |
18 if (!from && !flags) { | |
19 // recv() with a NULL sender and no flags is the same as read(), which | |
20 // is unrestricted in seccomp mode. | |
21 Debug::message("Replaced recv() with call to read()"); | |
22 ssize_t rc = sys.read(sockfd, buf, len); | |
23 if (rc < 0) { | |
24 Debug::elapsed(tm, __NR_recvfrom); | |
25 return -sys.my_errno; | |
26 } else { | |
27 Debug::elapsed(tm, __NR_recvfrom); | |
28 return rc; | |
29 } | |
30 } | |
31 | |
32 struct { | |
33 int sysnum; | |
34 long long cookie; | |
35 RecvFrom recvfrom_req; | |
36 } __attribute__((packed)) request; | |
37 request.sysnum = __NR_recvfrom; | |
38 request.cookie = cookie(); | |
39 request.recvfrom_req.sockfd = sockfd; | |
40 request.recvfrom_req.buf = buf; | |
41 request.recvfrom_req.len = len; | |
42 request.recvfrom_req.flags = flags; | |
43 request.recvfrom_req.from = from; | |
44 request.recvfrom_req.fromlen = fromlen; | |
45 | |
46 long rc; | |
47 if (write(sys, processFdPub(), &request, sizeof(request)) != | |
48 sizeof(request) || | |
49 read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { | |
50 die("Failed to forward recvfrom() request [sandbox]"); | |
51 } | |
52 Debug::elapsed(tm, __NR_recvfrom); | |
53 return static_cast<ssize_t>(rc); | |
54 } | |
55 | |
56 ssize_t Sandbox::sandbox_recvmsg(int sockfd, struct msghdr* msg, int flags) { | |
57 long long tm; | |
58 Debug::syscall(&tm, __NR_recvmsg, "Executing handler"); | |
59 | |
60 // We cannot simplify recvmsg() to recvfrom(), recv() or read(), as we do | |
61 // not know whether the caller needs us to set msg->msg_flags. | |
62 struct { | |
63 int sysnum; | |
64 long long cookie; | |
65 RecvMsg recvmsg_req; | |
66 } __attribute__((packed)) request; | |
67 request.sysnum = __NR_recvmsg; | |
68 request.cookie = cookie(); | |
69 request.recvmsg_req.sockfd = sockfd; | |
70 request.recvmsg_req.msg = msg; | |
71 request.recvmsg_req.flags = flags; | |
72 | |
73 long rc; | |
74 SysCalls sys; | |
75 if (write(sys, processFdPub(), &request, sizeof(request)) != | |
76 sizeof(request) || | |
77 read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { | |
78 die("Failed to forward recvmsg() request [sandbox]"); | |
79 } | |
80 Debug::elapsed(tm, __NR_recvmsg); | |
81 return static_cast<ssize_t>(rc); | |
82 } | |
83 | |
84 size_t Sandbox::sandbox_sendmsg(int sockfd, const struct msghdr* msg, | |
85 int flags) { | |
86 long long tm; | |
87 Debug::syscall(&tm, __NR_sendmsg, "Executing handler"); | |
88 | |
89 if (msg->msg_iovlen == 1 && msg->msg_controllen == 0) { | |
90 // sendmsg() can sometimes be simplified as sendto() | |
91 return sandbox_sendto(sockfd, msg->msg_iov, msg->msg_iovlen, | |
92 flags, msg->msg_name, msg->msg_namelen); | |
93 } | |
94 | |
95 struct Request { | |
96 int sysnum; | |
97 long long cookie; | |
98 SendMsg sendmsg_req; | |
99 struct msghdr msg; | |
100 } __attribute__((packed)); | |
101 char data[sizeof(struct Request) + msg->msg_namelen + msg->msg_controllen]; | |
102 struct Request *request = reinterpret_cast<struct Request *>(data); | |
103 request->sysnum = __NR_sendmsg; | |
104 request->cookie = cookie(); | |
105 request->sendmsg_req.sockfd = sockfd; | |
106 request->sendmsg_req.msg = msg; | |
107 request->sendmsg_req.flags = flags; | |
108 request->msg = *msg; | |
109 memcpy(reinterpret_cast<char *>( | |
110 memcpy(request + 1, msg->msg_name, msg->msg_namelen)) + | |
111 msg->msg_namelen, | |
112 msg->msg_control, msg->msg_controllen); | |
113 | |
114 long rc; | |
115 SysCalls sys; | |
116 if (write(sys, processFdPub(), &data, sizeof(data)) != | |
117 (ssize_t)sizeof(data) || | |
118 read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { | |
119 die("Failed to forward sendmsg() request [sandbox]"); | |
120 } | |
121 Debug::elapsed(tm, __NR_sendmsg); | |
122 return static_cast<ssize_t>(rc); | |
123 } | |
124 | |
125 ssize_t Sandbox::sandbox_sendto(int sockfd, const void* buf, size_t len, | |
126 int flags, const void* to, socklen_t tolen) { | |
127 long long tm; | |
128 Debug::syscall(&tm, __NR_sendto, "Executing handler"); | |
129 | |
130 SysCalls sys; | |
131 if (!to && !flags) { | |
132 // sendto() with a NULL recipient and no flags is the same as write(), | |
133 // which is unrestricted in seccomp mode. | |
134 Debug::message("Replaced sendto() with call to write()"); | |
135 ssize_t rc = sys.write(sockfd, buf, len); | |
136 if (rc < 0) { | |
137 Debug::elapsed(tm, __NR_sendto); | |
138 return -sys.my_errno; | |
139 } else { | |
140 Debug::elapsed(tm, __NR_sendto); | |
141 return rc; | |
142 } | |
143 } | |
144 | |
145 struct { | |
146 int sysnum; | |
147 long long cookie; | |
148 SendTo sendto_req; | |
149 } __attribute__((packed)) request; | |
150 request.sysnum = __NR_sendto; | |
151 request.cookie = cookie(); | |
152 request.sendto_req.sockfd = sockfd; | |
153 request.sendto_req.buf = buf; | |
154 request.sendto_req.len = len; | |
155 request.sendto_req.flags = flags; | |
156 request.sendto_req.to = to; | |
157 request.sendto_req.tolen = tolen; | |
158 | |
159 long rc; | |
160 if (write(sys, processFdPub(), &request, sizeof(request)) != | |
161 sizeof(request) || | |
162 read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { | |
163 die("Failed to forward sendto() request [sandbox]"); | |
164 } | |
165 Debug::elapsed(tm, __NR_sendto); | |
166 return static_cast<ssize_t>(rc); | |
167 } | |
168 | |
169 long Sandbox::sandbox_setsockopt(int sockfd, int level, int optname, | |
170 const void* optval, socklen_t optlen) { | |
171 long long tm; | |
172 Debug::syscall(&tm, __NR_setsockopt, "Executing handler"); | |
173 | |
174 struct { | |
175 int sysnum; | |
176 long long cookie; | |
177 SetSockOpt setsockopt_req; | |
178 } __attribute__((packed)) request; | |
179 request.sysnum = __NR_setsockopt; | |
180 request.cookie = cookie(); | |
181 request.setsockopt_req.sockfd = sockfd; | |
182 request.setsockopt_req.level = level; | |
183 request.setsockopt_req.optname = optname; | |
184 request.setsockopt_req.optval = optval; | |
185 request.setsockopt_req.optlen = optlen; | |
186 | |
187 long rc; | |
188 SysCalls sys; | |
189 if (write(sys, processFdPub(), &request, sizeof(request)) != | |
190 sizeof(request) || | |
191 read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { | |
192 die("Failed to forward setsockopt() request [sandbox]"); | |
193 } | |
194 Debug::elapsed(tm, __NR_setsockopt); | |
195 return rc; | |
196 } | |
197 | |
198 long Sandbox::sandbox_getsockopt(int sockfd, int level, int optname, | |
199 void* optval, socklen_t* optlen) { | |
200 long long tm; | |
201 Debug::syscall(&tm, __NR_getsockopt, "Executing handler"); | |
202 | |
203 struct { | |
204 int sysnum; | |
205 long long cookie; | |
206 GetSockOpt getsockopt_req; | |
207 } __attribute__((packed)) request; | |
208 request.sysnum = __NR_getsockopt; | |
209 request.cookie = cookie(); | |
210 request.getsockopt_req.sockfd = sockfd; | |
211 request.getsockopt_req.level = level; | |
212 request.getsockopt_req.optname = optname; | |
213 request.getsockopt_req.optval = optval; | |
214 request.getsockopt_req.optlen = optlen; | |
215 | |
216 long rc; | |
217 SysCalls sys; | |
218 if (write(sys, processFdPub(), &request, sizeof(request)) != | |
219 sizeof(request) || | |
220 read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { | |
221 die("Failed to forward getsockopt() request [sandbox]"); | |
222 } | |
223 Debug::elapsed(tm, __NR_getsockopt); | |
224 return rc; | |
225 } | |
226 | |
227 bool Sandbox::process_recvfrom(int parentMapsFd, int sandboxFd, | |
228 int threadFdPub, int threadFd, | |
229 SecureMem::Args* mem) { | |
230 // Read request | |
231 RecvFrom recvfrom_req; | |
232 SysCalls sys; | |
233 if (read(sys, sandboxFd, &recvfrom_req, sizeof(recvfrom_req)) != | |
234 sizeof(recvfrom_req)) { | |
235 die("Failed to read parameters for recvfrom() [process]"); | |
236 } | |
237 | |
238 // Unsupported flag encountered. Deny the call. | |
239 if (recvfrom_req.flags & | |
240 ~(MSG_DONTWAIT|MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_WAITALL)) { | |
241 SecureMem::abandonSystemCall(threadFd, -EINVAL); | |
242 return false; | |
243 } | |
244 | |
245 // While we do not anticipate any particular need to receive data on | |
246 // unconnected sockets, there is no particular risk in doing so. | |
247 SecureMem::sendSystemCall(threadFdPub, false, -1, mem, | |
248 __NR_recvfrom, recvfrom_req.sockfd, | |
249 recvfrom_req.buf, recvfrom_req.len, | |
250 recvfrom_req.flags, recvfrom_req.from, | |
251 recvfrom_req.fromlen); | |
252 return true; | |
253 } | |
254 | |
255 bool Sandbox::process_recvmsg(int parentMapsFd, int sandboxFd, int threadFdPub, | |
256 int threadFd, SecureMem::Args* mem) { | |
257 // Read request | |
258 RecvMsg recvmsg_req; | |
259 SysCalls sys; | |
260 if (read(sys, sandboxFd, &recvmsg_req, sizeof(recvmsg_req)) != | |
261 sizeof(recvmsg_req)) { | |
262 die("Failed to read parameters for recvmsg() [process]"); | |
263 } | |
264 | |
265 // Unsupported flag encountered. Deny the call. | |
266 if (recvmsg_req.flags & | |
267 ~(MSG_DONTWAIT|MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_WAITALL)) { | |
268 SecureMem::abandonSystemCall(threadFd, -EINVAL); | |
269 return false; | |
270 } | |
271 | |
272 // Receiving messages is general not security critical. | |
273 SecureMem::sendSystemCall(threadFdPub, false, -1, mem, | |
274 __NR_recvmsg, recvmsg_req.sockfd, | |
275 recvmsg_req.msg, recvmsg_req.flags); | |
276 return true; | |
277 } | |
278 | |
279 bool Sandbox::process_sendmsg(int parentMapsFd, int sandboxFd, int threadFdPub, | |
280 int threadFd, SecureMem::Args* mem) { | |
281 // Read request | |
282 struct { | |
283 SendMsg sendmsg_req; | |
284 struct msghdr msg; | |
285 } __attribute__((packed)) data; | |
286 SysCalls sys; | |
287 if (read(sys, sandboxFd, &data, sizeof(data)) != sizeof(data)) { | |
288 die("Failed to read parameters for sendmsg() [process]"); | |
289 } | |
290 | |
291 if (data.msg.msg_namelen > 4096 || data.msg.msg_controllen > 4096) { | |
292 die("Unexpected size for socketcall() payload [process]"); | |
293 } | |
294 char extra[data.msg.msg_namelen + data.msg.msg_controllen]; | |
295 if (read(sys, sandboxFd, &extra, sizeof(extra)) != (ssize_t)sizeof(extra)) { | |
296 die("Failed to read parameters for sendmsg() [process]"); | |
297 } | |
298 if (sizeof(struct msghdr) + sizeof(extra) > sizeof(mem->pathname)) { | |
299 goto deny; | |
300 } | |
301 | |
302 if (data.msg.msg_namelen || | |
303 (data.sendmsg_req.flags & | |
304 ~(MSG_CONFIRM|MSG_DONTWAIT|MSG_EOR|MSG_MORE|MSG_NOSIGNAL|MSG_OOB))) { | |
305 deny: | |
306 SecureMem::abandonSystemCall(threadFd, -EINVAL); | |
307 return false; | |
308 } | |
309 | |
310 // The trusted process receives file handles when a new untrusted thread | |
311 // gets created. We have security checks in place that prevent any | |
312 // critical information from being tampered with during thread creation. | |
313 // But if we disallowed passing of file handles, this would add an extra | |
314 // hurdle for an attacker. | |
315 // Unfortunately, for now, this is not possible as Chrome's | |
316 // base::SendRecvMsg() needs the ability to pass file handles. | |
317 if (data.msg.msg_controllen) { | |
318 data.msg.msg_control = extra + data.msg.msg_namelen; | |
319 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&data.msg); | |
320 do { | |
321 if (cmsg->cmsg_level != SOL_SOCKET || | |
322 cmsg->cmsg_type != SCM_RIGHTS) { | |
323 goto deny; | |
324 } | |
325 } while ((cmsg = CMSG_NXTHDR(&data.msg, cmsg)) != NULL); | |
326 } | |
327 | |
328 // This must be a locked system call, because we have to ensure that the | |
329 // untrusted code does not tamper with the msghdr after we have examined it. | |
330 SecureMem::lockSystemCall(parentMapsFd, mem); | |
331 if (sizeof(extra) > 0) { | |
332 if (data.msg.msg_namelen > 0) { | |
333 data.msg.msg_name = mem->pathname + sizeof(struct msghdr); | |
334 } | |
335 if (data.msg.msg_controllen > 0) { | |
336 data.msg.msg_control = mem->pathname + sizeof(struct msghdr) + | |
337 data.msg.msg_namelen; | |
338 } | |
339 memcpy(mem->pathname + sizeof(struct msghdr), extra, sizeof(extra)); | |
340 } | |
341 memcpy(mem->pathname, &data.msg, sizeof(struct msghdr)); | |
342 SecureMem::sendSystemCall(threadFdPub, true, parentMapsFd, mem, | |
343 __NR_sendmsg, data.sendmsg_req.sockfd, | |
344 mem->pathname - (char*)mem + (char*)mem->self, | |
345 data.sendmsg_req.flags); | |
346 return true; | |
347 } | |
348 | |
349 bool Sandbox::process_sendto(int parentMapsFd, int sandboxFd, int threadFdPub, | |
350 int threadFd, SecureMem::Args* mem) { | |
351 // Read request | |
352 SendTo sendto_req; | |
353 SysCalls sys; | |
354 if (read(sys, sandboxFd, &sendto_req, sizeof(sendto_req)) != | |
355 sizeof(sendto_req)) { | |
356 die("Failed to read parameters for sendto() [process]"); | |
357 } | |
358 | |
359 // The sandbox does not allow sending to arbitrary addresses. | |
360 if (sendto_req.to) { | |
361 SecureMem::abandonSystemCall(threadFd, -EINVAL); | |
362 return false; | |
363 } | |
364 | |
365 // Unsupported flag encountered. Deny the call. | |
366 if (sendto_req.flags & | |
367 ~(MSG_CONFIRM|MSG_DONTWAIT|MSG_EOR|MSG_MORE|MSG_NOSIGNAL|MSG_OOB)) { | |
368 SecureMem::abandonSystemCall(threadFd, -EINVAL); | |
369 return false; | |
370 } | |
371 | |
372 // Sending data on a connected socket is similar to calling write(). | |
373 // Allow it. | |
374 SecureMem::sendSystemCall(threadFdPub, false, -1, mem, | |
375 __NR_sendto, sendto_req.sockfd, | |
376 sendto_req.buf, sendto_req.len, | |
377 sendto_req.flags, sendto_req.to, | |
378 sendto_req.tolen); | |
379 return true; | |
380 } | |
381 | |
382 bool Sandbox::process_setsockopt(int parentMapsFd, int sandboxFd, | |
383 int threadFdPub, int threadFd, | |
384 SecureMem::Args* mem) { | |
385 // Read request | |
386 SetSockOpt setsockopt_req; | |
387 SysCalls sys; | |
388 if (read(sys, sandboxFd, &setsockopt_req, sizeof(setsockopt_req)) != | |
389 sizeof(setsockopt_req)) { | |
390 die("Failed to read parameters for setsockopt() [process]"); | |
391 } | |
392 | |
393 switch (setsockopt_req.level) { | |
394 case SOL_SOCKET: | |
395 switch (setsockopt_req.optname) { | |
396 case SO_KEEPALIVE: | |
397 case SO_LINGER: | |
398 case SO_OOBINLINE: | |
399 case SO_RCVBUF: | |
400 case SO_RCVLOWAT: | |
401 case SO_SNDLOWAT: | |
402 case SO_RCVTIMEO: | |
403 case SO_SNDTIMEO: | |
404 case SO_REUSEADDR: | |
405 case SO_SNDBUF: | |
406 case SO_TIMESTAMP: | |
407 SecureMem::sendSystemCall(threadFdPub, false, -1, mem, | |
408 __NR_setsockopt, setsockopt_req.sockfd, | |
409 setsockopt_req.level, setsockopt_req.optname, | |
410 setsockopt_req.optval, setsockopt_req.optlen); | |
411 return true; | |
412 default: | |
413 break; | |
414 } | |
415 break; | |
416 case IPPROTO_TCP: | |
417 switch (setsockopt_req.optname) { | |
418 case TCP_CORK: | |
419 case TCP_DEFER_ACCEPT: | |
420 case TCP_INFO: | |
421 case TCP_KEEPCNT: | |
422 case TCP_KEEPIDLE: | |
423 case TCP_KEEPINTVL: | |
424 case TCP_LINGER2: | |
425 case TCP_MAXSEG: | |
426 case TCP_NODELAY: | |
427 case TCP_QUICKACK: | |
428 case TCP_SYNCNT: | |
429 case TCP_WINDOW_CLAMP: | |
430 SecureMem::sendSystemCall(threadFdPub, false, -1, mem, | |
431 __NR_setsockopt, setsockopt_req.sockfd, | |
432 setsockopt_req.level, setsockopt_req.optname, | |
433 setsockopt_req.optval, setsockopt_req.optlen); | |
434 return true; | |
435 default: | |
436 break; | |
437 } | |
438 break; | |
439 default: | |
440 break; | |
441 } | |
442 SecureMem::abandonSystemCall(threadFd, -EINVAL); | |
443 return false; | |
444 } | |
445 | |
446 bool Sandbox::process_getsockopt(int parentMapsFd, int sandboxFd, | |
447 int threadFdPub, int threadFd, | |
448 SecureMem::Args* mem) { | |
449 // Read request | |
450 GetSockOpt getsockopt_req; | |
451 SysCalls sys; | |
452 if (read(sys, sandboxFd, &getsockopt_req, sizeof(getsockopt_req)) != | |
453 sizeof(getsockopt_req)) { | |
454 die("Failed to read parameters for getsockopt() [process]"); | |
455 } | |
456 | |
457 switch (getsockopt_req.level) { | |
458 case SOL_SOCKET: | |
459 switch (getsockopt_req.optname) { | |
460 case SO_ACCEPTCONN: | |
461 case SO_ERROR: | |
462 case SO_KEEPALIVE: | |
463 case SO_LINGER: | |
464 case SO_OOBINLINE: | |
465 case SO_RCVBUF: | |
466 case SO_RCVLOWAT: | |
467 case SO_SNDLOWAT: | |
468 case SO_RCVTIMEO: | |
469 case SO_SNDTIMEO: | |
470 case SO_REUSEADDR: | |
471 case SO_SNDBUF: | |
472 case SO_TIMESTAMP: | |
473 case SO_TYPE: | |
474 SecureMem::sendSystemCall(threadFdPub, false, -1, mem, | |
475 __NR_getsockopt, getsockopt_req.sockfd, | |
476 getsockopt_req.level, getsockopt_req.optname, | |
477 getsockopt_req.optval, getsockopt_req.optlen); | |
478 return true; | |
479 default: | |
480 break; | |
481 } | |
482 break; | |
483 case IPPROTO_TCP: | |
484 switch (getsockopt_req.optname) { | |
485 case TCP_CORK: | |
486 case TCP_DEFER_ACCEPT: | |
487 case TCP_INFO: | |
488 case TCP_KEEPCNT: | |
489 case TCP_KEEPIDLE: | |
490 case TCP_KEEPINTVL: | |
491 case TCP_LINGER2: | |
492 case TCP_MAXSEG: | |
493 case TCP_NODELAY: | |
494 case TCP_QUICKACK: | |
495 case TCP_SYNCNT: | |
496 case TCP_WINDOW_CLAMP: | |
497 SecureMem::sendSystemCall(threadFdPub, false, -1, mem, | |
498 __NR_getsockopt, getsockopt_req.sockfd, | |
499 getsockopt_req.level, getsockopt_req.optname, | |
500 getsockopt_req.optval, getsockopt_req.optlen); | |
501 return true; | |
502 default: | |
503 break; | |
504 } | |
505 break; | |
506 default: | |
507 break; | |
508 } | |
509 SecureMem::abandonSystemCall(threadFd, -EINVAL); | |
510 return false; | |
511 } | |
512 | |
513 #endif | |
514 #if defined(__NR_socketcall) | |
515 | |
516 enum { | |
517 SYS_SOCKET = 1, | |
518 SYS_BIND = 2, | |
519 SYS_CONNECT = 3, | |
520 SYS_LISTEN = 4, | |
521 SYS_ACCEPT = 5, | |
522 SYS_GETSOCKNAME = 6, | |
523 SYS_GETPEERNAME = 7, | |
524 SYS_SOCKETPAIR = 8, | |
525 SYS_SEND = 9, | |
526 SYS_RECV = 10, | |
527 SYS_SENDTO = 11, | |
528 SYS_RECVFROM = 12, | |
529 SYS_SHUTDOWN = 13, | |
530 SYS_SETSOCKOPT = 14, | |
531 SYS_GETSOCKOPT = 15, | |
532 SYS_SENDMSG = 16, | |
533 SYS_RECVMSG = 17, | |
534 SYS_ACCEPT4 = 18 | |
535 }; | |
536 | |
537 struct Sandbox::SocketCallArgInfo { | |
538 size_t len; | |
539 off_t addrOff; | |
540 off_t lengthOff; | |
541 }; | |
542 const struct Sandbox::SocketCallArgInfo Sandbox::socketCallArgInfo[] = { | |
543 #define STRUCT(s) reinterpret_cast<SocketCall *>(0)->args.s | |
544 #define SIZE(s) sizeof(STRUCT(s)) | |
545 #define OFF(s, f) offsetof(typeof STRUCT(s), f) | |
546 { 0 }, | |
547 { SIZE(socket) }, | |
548 { SIZE(bind), OFF(bind, addr), OFF(bind, addrlen) }, | |
549 { SIZE(connect), OFF(connect, addr), OFF(connect, addrlen) }, | |
550 { SIZE(listen) }, | |
551 { SIZE(accept) }, | |
552 { SIZE(getsockname) }, | |
553 { SIZE(getpeername) }, | |
554 { SIZE(socketpair) }, | |
555 { SIZE(send) }, | |
556 { SIZE(recv) }, | |
557 { SIZE(sendto), OFF(sendto, to), OFF(sendto, tolen) }, | |
558 { SIZE(recvfrom) }, | |
559 { SIZE(shutdown) }, | |
560 { SIZE(setsockopt), OFF(setsockopt, optval), OFF(setsockopt, optlen) }, | |
561 { SIZE(getsockopt) }, | |
562 { SIZE(sendmsg) }, | |
563 { SIZE(recvmsg) }, | |
564 { SIZE(accept4) } | |
565 #undef STRUCT | |
566 #undef SIZE | |
567 #undef OFF | |
568 }; | |
569 | |
570 long Sandbox::sandbox_socketcall(int call, void* args) { | |
571 long long tm; | |
572 Debug::syscall(&tm, __NR_socketcall, "Executing handler", call); | |
573 | |
574 // When demultiplexing socketcall(), only accept calls that have a valid | |
575 // "call" opcode. | |
576 if (call < SYS_SOCKET || call > SYS_ACCEPT4) { | |
577 Debug::elapsed(tm, __NR_socketcall, call); | |
578 return -ENOSYS; | |
579 } | |
580 | |
581 // Some type of calls include a pointer to an address or name, which cannot | |
582 // be accessed by the trusted process, as it lives in a separate address | |
583 // space. For these calls, append the extra data to the serialized request. | |
584 // This requires some copying of data, as we have to make sure there is | |
585 // only a single atomic call to write(). | |
586 socklen_t numExtraData = 0; | |
587 const void* extraDataAddr = NULL; | |
588 if (socketCallArgInfo[call].lengthOff) { | |
589 memcpy(&numExtraData, | |
590 reinterpret_cast<char *>(args) + socketCallArgInfo[call].lengthOff, | |
591 sizeof(socklen_t)); | |
592 extraDataAddr = reinterpret_cast<char *>(args) + | |
593 socketCallArgInfo[call].addrOff; | |
594 } | |
595 | |
596 // sendmsg() and recvmsg() have more complicated requirements for computing | |
597 // the amount of extra data that needs to be sent to the trusted process. | |
598 if (call == SYS_SENDMSG) { | |
599 SendMsg *sendmsg_args = reinterpret_cast<SendMsg *>(args); | |
600 if (sendmsg_args->msg->msg_iovlen == 1 && | |
601 !sendmsg_args->msg->msg_control) { | |
602 // Further down in the code, this sendmsg() call will be simplified to | |
603 // a sendto() call. Make sure we already compute the correct value for | |
604 // numExtraData, as it is needed when we allocate "data[]" on the stack. | |
605 numExtraData = sendmsg_args->msg->msg_namelen; | |
606 extraDataAddr = sendmsg_args->msg->msg_name; | |
607 } else { | |
608 // sendmsg() needs to include some of the extra data so that we can | |
609 // inspect it in process_socketcall() | |
610 numExtraData = sizeof(*sendmsg_args->msg) + | |
611 sendmsg_args->msg->msg_namelen + | |
612 sendmsg_args->msg->msg_controllen; | |
613 extraDataAddr = NULL; | |
614 } | |
615 } | |
616 if (call == SYS_RECVMSG) { | |
617 RecvMsg *recvmsg_args = reinterpret_cast<RecvMsg *>(args); | |
618 numExtraData = sizeof(*recvmsg_args->msg); | |
619 extraDataAddr = recvmsg_args->msg; | |
620 } | |
621 | |
622 // Set up storage for the request header and copy the data from "args" | |
623 // into it. | |
624 struct Request { | |
625 int sysnum; | |
626 long long cookie; | |
627 SocketCall socketcall_req; | |
628 } __attribute__((packed)) *request; | |
629 char data[sizeof(struct Request) + numExtraData]; | |
630 request = reinterpret_cast<struct Request *>(data); | |
631 memcpy(&request->socketcall_req.args, args, socketCallArgInfo[call].len); | |
632 | |
633 // Simplify send(), sendto() and sendmsg(), if there are simpler equivalent | |
634 // calls. This allows us to occasionally replace them with calls to write(), | |
635 // which don't have to be forwarded to the trusted process. | |
636 SysCalls sys; | |
637 if (call == SYS_SENDMSG && | |
638 request->socketcall_req.args.sendmsg.msg->msg_iovlen == 1 && | |
639 !request->socketcall_req.args.sendmsg.msg->msg_control) { | |
640 // Ordering of these assignments is important, as we are reshuffling | |
641 // fields inside of a union. | |
642 call = SYS_SENDTO; | |
643 request->socketcall_req.args.sendto.flags = | |
644 request->socketcall_req.args.sendmsg.flags; | |
645 request->socketcall_req.args.sendto.to = | |
646 request->socketcall_req.args.sendmsg.msg->msg_name; | |
647 request->socketcall_req.args.sendto.tolen = | |
648 request->socketcall_req.args.sendmsg.msg->msg_namelen; | |
649 request->socketcall_req.args.sendto.len = | |
650 request->socketcall_req.args.sendmsg.msg->msg_iov->iov_len; | |
651 request->socketcall_req.args.sendto.buf = | |
652 request->socketcall_req.args.sendmsg.msg->msg_iov->iov_base; | |
653 } | |
654 if (call == SYS_SENDTO && !request->socketcall_req.args.sendto.to) { | |
655 // sendto() with a NULL address is the same as send() | |
656 call = SYS_SEND; | |
657 numExtraData = 0; | |
658 } | |
659 if (call == SYS_SEND && !request->socketcall_req.args.send.flags) { | |
660 // send() with no flags is the same as write(), which is unrestricted | |
661 // in seccomp mode. | |
662 Debug::message("Replaced socketcall() with call to write()"); | |
663 ssize_t rc = sys.write(request->socketcall_req.args.send.sockfd, | |
664 request->socketcall_req.args.send.buf, | |
665 request->socketcall_req.args.send.len); | |
666 if (rc < 0) { | |
667 Debug::elapsed(tm, __NR_socketcall, call); | |
668 return -sys.my_errno; | |
669 } else { | |
670 Debug::elapsed(tm, __NR_socketcall, call); | |
671 return rc; | |
672 } | |
673 } | |
674 | |
675 // Simplify recv(), and recvfrom(), if there are simpler equivalent calls. | |
676 // This allows us to occasionally replace them with calls to read(), which | |
677 // don't have to be forwarded to the trusted process. | |
678 // We cannot simplify recvmsg() to recvfrom(), recv() or read(), as we do | |
679 // not know whether the caller needs us to set msg->msg_flags. | |
680 if (call == SYS_RECVFROM && !request->socketcall_req.args.recvfrom.from) { | |
681 // recvfrom() with a NULL address buffer is the same as recv() | |
682 call = SYS_RECV; | |
683 } | |
684 if (call == SYS_RECV && !request->socketcall_req.args.recv.flags) { | |
685 // recv() with no flags is the same as read(), which is unrestricted | |
686 // in seccomp mode. | |
687 Debug::message("Replaced socketcall() with call to read()"); | |
688 ssize_t rc = sys.read(request->socketcall_req.args.recv.sockfd, | |
689 request->socketcall_req.args.recv.buf, | |
690 request->socketcall_req.args.recv.len); | |
691 if (rc < 0) { | |
692 Debug::elapsed(tm, __NR_socketcall, call); | |
693 return -sys.my_errno; | |
694 } else { | |
695 Debug::elapsed(tm, __NR_socketcall, call); | |
696 return rc; | |
697 } | |
698 } | |
699 | |
700 // Fill in the rest of the request header. | |
701 request->sysnum = __NR_socketcall; | |
702 request->cookie = cookie(); | |
703 request->socketcall_req.call = call; | |
704 request->socketcall_req.arg_ptr = args; | |
705 int padding = sizeof(request->socketcall_req.args) - | |
706 socketCallArgInfo[call].len; | |
707 if (padding > 0) { | |
708 memset((char *)(&request->socketcall_req.args + 1) - padding, 0, padding); | |
709 } | |
710 if (call == SYS_SENDMSG) { | |
711 // for sendmsg() we include the (optional) destination address, and the | |
712 // (optional) control data in the payload. | |
713 SendMsg *sendmsg_args = reinterpret_cast<SendMsg *>(args); | |
714 memcpy(reinterpret_cast<char *>( | |
715 memcpy(reinterpret_cast<char *>( | |
716 memcpy(request + 1, sendmsg_args->msg, sizeof(*sendmsg_args->msg))) + | |
717 sizeof(*sendmsg_args->msg), | |
718 sendmsg_args->msg->msg_name, sendmsg_args->msg->msg_namelen)) + | |
719 sendmsg_args->msg->msg_namelen, | |
720 sendmsg_args->msg->msg_control, sendmsg_args->msg->msg_controllen); | |
721 } else if (extraDataAddr) { | |
722 memcpy(request + 1, extraDataAddr, numExtraData); | |
723 } | |
724 | |
725 // Send request to trusted process and collect response from trusted thread. | |
726 long rc; | |
727 ssize_t len = sizeof(struct Request) + numExtraData; | |
728 if (write(sys, processFdPub(), data, len) != len || | |
729 read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { | |
730 die("Failed to forward socketcall() request [sandbox]"); | |
731 } | |
732 Debug::elapsed(tm, __NR_socketcall, call); | |
733 return rc; | |
734 } | |
735 | |
736 bool Sandbox::process_socketcall(int parentMapsFd, int sandboxFd, | |
737 int threadFdPub, int threadFd, | |
738 SecureMem::Args* mem) { | |
739 // Read request | |
740 SocketCall socketcall_req; | |
741 SysCalls sys; | |
742 if (read(sys, sandboxFd, &socketcall_req, sizeof(socketcall_req)) != | |
743 sizeof(socketcall_req)) { | |
744 die("Failed to read parameters for socketcall() [process]"); | |
745 } | |
746 | |
747 // sandbox_socketcall() should never send us an unexpected "call" opcode. | |
748 // If it did, something went very wrong and we better terminate the process. | |
749 if (socketcall_req.call < SYS_SOCKET || socketcall_req.call > SYS_ACCEPT4) { | |
750 die("Unexpected socketcall() [process]"); | |
751 } | |
752 | |
753 // Check if this particular operation carries an extra payload. | |
754 socklen_t numExtraData = 0; | |
755 if (socketCallArgInfo[socketcall_req.call].lengthOff) { | |
756 memcpy(&numExtraData, | |
757 reinterpret_cast<char *>(&socketcall_req) + | |
758 socketCallArgInfo[socketcall_req.call].lengthOff, | |
759 sizeof(socklen_t)); | |
760 } else if (socketcall_req.call == SYS_SENDMSG) { | |
761 numExtraData = sizeof(*socketcall_req.args.sendmsg.msg); | |
762 } else if (socketcall_req.call == SYS_RECVMSG) { | |
763 numExtraData = sizeof(*socketcall_req.args.recvmsg.msg); | |
764 } | |
765 | |
766 // Verify that the length for the payload is reasonable. We don't want to | |
767 // blow up our stack, and excessive (or negative) buffer sizes are almost | |
768 // certainly a bug. | |
769 if (numExtraData > 4096) { | |
770 die("Unexpected size for socketcall() payload [process]"); | |
771 } | |
772 | |
773 // Read the extra payload, if any. | |
774 char extra[numExtraData]; | |
775 if (numExtraData) { | |
776 if (read(sys, sandboxFd, extra, numExtraData) != (ssize_t)numExtraData) { | |
777 die("Failed to read socketcall() payload [process]"); | |
778 } | |
779 } | |
780 | |
781 // sendmsg() has another level of indirection and can carry even more payload | |
782 ssize_t numSendmsgExtra = 0; | |
783 if (socketcall_req.call == SYS_SENDMSG) { | |
784 struct msghdr* msg = reinterpret_cast<struct msghdr*>(extra); | |
785 if (msg->msg_namelen > 4096 || msg->msg_controllen > 4096) { | |
786 die("Unexpected size for socketcall() payload [process]"); | |
787 } | |
788 numSendmsgExtra = msg->msg_namelen + msg->msg_controllen; | |
789 } | |
790 char sendmsgExtra[numSendmsgExtra]; | |
791 if (numSendmsgExtra) { | |
792 if (read(sys, sandboxFd, sendmsgExtra, numSendmsgExtra) != | |
793 numSendmsgExtra) { | |
794 die("Failed to read socketcall() payload [process]"); | |
795 } | |
796 } | |
797 | |
798 int rc = -EINVAL; | |
799 switch (socketcall_req.call) { | |
800 case SYS_SOCKET: | |
801 // The sandbox does not allow creation of any new sockets. | |
802 goto deny; | |
803 case SYS_BIND: | |
804 // The sandbox does not allow binding an address to a socket. | |
805 goto deny; | |
806 case SYS_CONNECT: | |
807 // The sandbox does not allow connecting a socket. | |
808 goto deny; | |
809 case SYS_LISTEN: | |
810 // The sandbox does not allow a socket to enter listening state. | |
811 goto deny; | |
812 case SYS_ACCEPT4: | |
813 case SYS_ACCEPT: | |
814 // If the sandbox obtained a socket that is already in the listening | |
815 // state (e.g. because somebody sent it a suitable file descriptor), it | |
816 // is permissible to call accept(). | |
817 | |
818 accept_simple: | |
819 // None of the parameters need to be checked, so it is OK to refer | |
820 // to the parameter block created by the untrusted code. | |
821 SecureMem::sendSystemCall(threadFdPub, false, -1, mem, __NR_socketcall, | |
822 socketcall_req.call, socketcall_req.arg_ptr); | |
823 return true; | |
824 case SYS_GETSOCKNAME: | |
825 case SYS_GETPEERNAME: | |
826 // Querying the local and the remote name is not considered security | |
827 // sensitive for the purposes of the sandbox. | |
828 goto accept_simple; | |
829 case SYS_SOCKETPAIR: | |
830 // Socket pairs are connected to each other and not considered | |
831 // security sensitive. | |
832 goto accept_simple; | |
833 case SYS_SENDTO: | |
834 if (socketcall_req.args.sendto.to) { | |
835 // The sandbox does not allow sending to arbitrary addresses. | |
836 goto deny; | |
837 } | |
838 // Fall through | |
839 case SYS_SEND: | |
840 if (socketcall_req.args.send.flags & | |
841 ~(MSG_CONFIRM|MSG_DONTWAIT|MSG_EOR|MSG_MORE|MSG_NOSIGNAL|MSG_OOB)) { | |
842 // Unsupported flag encountered. Deny the call. | |
843 goto deny; | |
844 } | |
845 // Sending data on a connected socket is similar to calling write(). | |
846 // Allow it. | |
847 | |
848 accept_complex: | |
849 // The parameter block contains potentially security critical information | |
850 // that should not be tampered with after it has been inspected. Copy it | |
851 // into the write-protected securely shared memory before telling the | |
852 // trusted thread to execute the socket call. | |
853 SecureMem::lockSystemCall(parentMapsFd, mem); | |
854 memcpy(mem->pathname, &socketcall_req.args, sizeof(socketcall_req.args)); | |
855 SecureMem::sendSystemCall(threadFdPub, true, parentMapsFd, mem, | |
856 __NR_socketcall, socketcall_req.call, | |
857 mem->pathname - (char*)mem + (char*)mem->self); | |
858 return true; | |
859 case SYS_RECVFROM: | |
860 // While we do not anticipate any particular need to receive data on | |
861 // unconnected sockets, there is no particular risk in doing so. | |
862 // Fall through | |
863 case SYS_RECV: | |
864 if (socketcall_req.args.recv.flags & | |
865 ~(MSG_DONTWAIT|MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_WAITALL)) { | |
866 // Unsupported flag encountered. Deny the call. | |
867 goto deny; | |
868 } | |
869 // Receiving data on a connected socket is similar to calling read(). | |
870 // Allow it. | |
871 goto accept_complex; | |
872 case SYS_SHUTDOWN: | |
873 // Shutting down a socket is always OK. | |
874 goto accept_simple; | |
875 case SYS_SETSOCKOPT: | |
876 switch (socketcall_req.args.setsockopt.level) { | |
877 case SOL_SOCKET: | |
878 switch (socketcall_req.args.setsockopt.optname) { | |
879 case SO_KEEPALIVE: | |
880 case SO_LINGER: | |
881 case SO_OOBINLINE: | |
882 case SO_RCVBUF: | |
883 case SO_RCVLOWAT: | |
884 case SO_SNDLOWAT: | |
885 case SO_RCVTIMEO: | |
886 case SO_SNDTIMEO: | |
887 case SO_REUSEADDR: | |
888 case SO_SNDBUF: | |
889 case SO_TIMESTAMP: | |
890 goto accept_complex; | |
891 default: | |
892 break; | |
893 } | |
894 break; | |
895 case IPPROTO_TCP: | |
896 switch (socketcall_req.args.setsockopt.optname) { | |
897 case TCP_CORK: | |
898 case TCP_DEFER_ACCEPT: | |
899 case TCP_INFO: | |
900 case TCP_KEEPCNT: | |
901 case TCP_KEEPIDLE: | |
902 case TCP_KEEPINTVL: | |
903 case TCP_LINGER2: | |
904 case TCP_MAXSEG: | |
905 case TCP_NODELAY: | |
906 case TCP_QUICKACK: | |
907 case TCP_SYNCNT: | |
908 case TCP_WINDOW_CLAMP: | |
909 goto accept_complex; | |
910 default: | |
911 break; | |
912 } | |
913 break; | |
914 default: | |
915 break; | |
916 } | |
917 goto deny; | |
918 case SYS_GETSOCKOPT: | |
919 switch (socketcall_req.args.getsockopt.level) { | |
920 case SOL_SOCKET: | |
921 switch (socketcall_req.args.getsockopt.optname) { | |
922 case SO_ACCEPTCONN: | |
923 case SO_ERROR: | |
924 case SO_KEEPALIVE: | |
925 case SO_LINGER: | |
926 case SO_OOBINLINE: | |
927 case SO_RCVBUF: | |
928 case SO_RCVLOWAT: | |
929 case SO_SNDLOWAT: | |
930 case SO_RCVTIMEO: | |
931 case SO_SNDTIMEO: | |
932 case SO_REUSEADDR: | |
933 case SO_SNDBUF: | |
934 case SO_TIMESTAMP: | |
935 case SO_TYPE: | |
936 goto accept_complex; | |
937 default: | |
938 break; | |
939 } | |
940 break; | |
941 case IPPROTO_TCP: | |
942 switch (socketcall_req.args.getsockopt.optname) { | |
943 case TCP_CORK: | |
944 case TCP_DEFER_ACCEPT: | |
945 case TCP_INFO: | |
946 case TCP_KEEPCNT: | |
947 case TCP_KEEPIDLE: | |
948 case TCP_KEEPINTVL: | |
949 case TCP_LINGER2: | |
950 case TCP_MAXSEG: | |
951 case TCP_NODELAY: | |
952 case TCP_QUICKACK: | |
953 case TCP_SYNCNT: | |
954 case TCP_WINDOW_CLAMP: | |
955 goto accept_complex; | |
956 default: | |
957 break; | |
958 } | |
959 break; | |
960 default: | |
961 break; | |
962 } | |
963 goto deny; | |
964 case SYS_SENDMSG: { | |
965 struct msghdr* msg = reinterpret_cast<struct msghdr*>(extra); | |
966 | |
967 if (sizeof(socketcall_req.args) + sizeof(*msg) + numSendmsgExtra > | |
968 sizeof(mem->pathname)) { | |
969 goto deny; | |
970 } | |
971 | |
972 if (msg->msg_namelen || | |
973 (socketcall_req.args.sendmsg.flags & | |
974 ~(MSG_CONFIRM|MSG_DONTWAIT|MSG_EOR|MSG_MORE|MSG_NOSIGNAL|MSG_OOB))){ | |
975 goto deny; | |
976 } | |
977 | |
978 // The trusted process receives file handles when a new untrusted thread | |
979 // gets created. We have security checks in place that prevent any | |
980 // critical information from being tampered with during thread creation. | |
981 // But if we disallowed passing of file handles, this would add an extra | |
982 // hurdle for an attacker. | |
983 // Unfortunately, for now, this is not possible as Chrome's | |
984 // base::SendRecvMsg() needs the ability to pass file handles. | |
985 if (msg->msg_controllen) { | |
986 msg->msg_control = sendmsgExtra + msg->msg_namelen; | |
987 struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); | |
988 do { | |
989 if (cmsg->cmsg_level != SOL_SOCKET || | |
990 cmsg->cmsg_type != SCM_RIGHTS) { | |
991 goto deny; | |
992 } | |
993 } while ((cmsg = CMSG_NXTHDR(msg, cmsg)) != NULL); | |
994 } | |
995 | |
996 // This must be a locked system call, because we have to ensure that | |
997 // the untrusted code does not tamper with the msghdr after we have | |
998 // examined it. | |
999 SecureMem::lockSystemCall(parentMapsFd, mem); | |
1000 socketcall_req.args.sendmsg.msg = | |
1001 reinterpret_cast<struct msghdr*>(mem->pathname + | |
1002 sizeof(socketcall_req.args) - | |
1003 (char*)mem + (char*)mem->self); | |
1004 memcpy(mem->pathname, &socketcall_req.args, sizeof(socketcall_req.args)); | |
1005 if (numSendmsgExtra) { | |
1006 if (msg->msg_namelen > 0) { | |
1007 msg->msg_name = const_cast<struct msghdr*>( | |
1008 socketcall_req.args.sendmsg.msg) + 1; | |
1009 } | |
1010 if (msg->msg_controllen > 0) { | |
1011 msg->msg_control = (char *)( | |
1012 socketcall_req.args.sendmsg.msg + 1) + msg->msg_namelen; | |
1013 } | |
1014 memcpy(mem->pathname + sizeof(socketcall_req.args) + sizeof(*msg), | |
1015 sendmsgExtra, numSendmsgExtra); | |
1016 } | |
1017 memcpy(mem->pathname + sizeof(socketcall_req.args), msg, sizeof(*msg)); | |
1018 SecureMem::sendSystemCall(threadFdPub, true, parentMapsFd, mem, | |
1019 __NR_socketcall, socketcall_req.call, | |
1020 mem->pathname - (char*)mem + (char*)mem->self); | |
1021 return true; | |
1022 } | |
1023 case SYS_RECVMSG: | |
1024 // Receiving messages is general not security critical. | |
1025 if (socketcall_req.args.recvmsg.flags & | |
1026 ~(MSG_DONTWAIT|MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_WAITALL)) { | |
1027 goto deny; | |
1028 } | |
1029 goto accept_complex; | |
1030 default: | |
1031 deny: | |
1032 SecureMem::abandonSystemCall(threadFd, rc); | |
1033 return false; | |
1034 } | |
1035 } | |
1036 | |
1037 #endif | |
1038 | |
1039 } // namespace | |
OLD | NEW |