Index: sandbox/linux/seccomp/socketcall.cc |
=================================================================== |
--- sandbox/linux/seccomp/socketcall.cc (revision 57969) |
+++ sandbox/linux/seccomp/socketcall.cc (working copy) |
@@ -1,1039 +0,0 @@ |
-// Copyright (c) 2010 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "debug.h" |
-#include "sandbox_impl.h" |
- |
-namespace playground { |
- |
-#if defined(__NR_socket) |
- |
-ssize_t Sandbox::sandbox_recvfrom(int sockfd, void* buf, size_t len, int flags, |
- void* from, socklen_t* fromlen) { |
- long long tm; |
- Debug::syscall(&tm, __NR_recvfrom, "Executing handler"); |
- |
- SysCalls sys; |
- if (!from && !flags) { |
- // recv() with a NULL sender and no flags is the same as read(), which |
- // is unrestricted in seccomp mode. |
- Debug::message("Replaced recv() with call to read()"); |
- ssize_t rc = sys.read(sockfd, buf, len); |
- if (rc < 0) { |
- Debug::elapsed(tm, __NR_recvfrom); |
- return -sys.my_errno; |
- } else { |
- Debug::elapsed(tm, __NR_recvfrom); |
- return rc; |
- } |
- } |
- |
- struct { |
- int sysnum; |
- long long cookie; |
- RecvFrom recvfrom_req; |
- } __attribute__((packed)) request; |
- request.sysnum = __NR_recvfrom; |
- request.cookie = cookie(); |
- request.recvfrom_req.sockfd = sockfd; |
- request.recvfrom_req.buf = buf; |
- request.recvfrom_req.len = len; |
- request.recvfrom_req.flags = flags; |
- request.recvfrom_req.from = from; |
- request.recvfrom_req.fromlen = fromlen; |
- |
- long rc; |
- if (write(sys, processFdPub(), &request, sizeof(request)) != |
- sizeof(request) || |
- read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { |
- die("Failed to forward recvfrom() request [sandbox]"); |
- } |
- Debug::elapsed(tm, __NR_recvfrom); |
- return static_cast<ssize_t>(rc); |
-} |
- |
-ssize_t Sandbox::sandbox_recvmsg(int sockfd, struct msghdr* msg, int flags) { |
- long long tm; |
- Debug::syscall(&tm, __NR_recvmsg, "Executing handler"); |
- |
- // We cannot simplify recvmsg() to recvfrom(), recv() or read(), as we do |
- // not know whether the caller needs us to set msg->msg_flags. |
- struct { |
- int sysnum; |
- long long cookie; |
- RecvMsg recvmsg_req; |
- } __attribute__((packed)) request; |
- request.sysnum = __NR_recvmsg; |
- request.cookie = cookie(); |
- request.recvmsg_req.sockfd = sockfd; |
- request.recvmsg_req.msg = msg; |
- request.recvmsg_req.flags = flags; |
- |
- long rc; |
- SysCalls sys; |
- if (write(sys, processFdPub(), &request, sizeof(request)) != |
- sizeof(request) || |
- read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { |
- die("Failed to forward recvmsg() request [sandbox]"); |
- } |
- Debug::elapsed(tm, __NR_recvmsg); |
- return static_cast<ssize_t>(rc); |
-} |
- |
-size_t Sandbox::sandbox_sendmsg(int sockfd, const struct msghdr* msg, |
- int flags) { |
- long long tm; |
- Debug::syscall(&tm, __NR_sendmsg, "Executing handler"); |
- |
- if (msg->msg_iovlen == 1 && msg->msg_controllen == 0) { |
- // sendmsg() can sometimes be simplified as sendto() |
- return sandbox_sendto(sockfd, msg->msg_iov, msg->msg_iovlen, |
- flags, msg->msg_name, msg->msg_namelen); |
- } |
- |
- struct Request { |
- int sysnum; |
- long long cookie; |
- SendMsg sendmsg_req; |
- struct msghdr msg; |
- } __attribute__((packed)); |
- char data[sizeof(struct Request) + msg->msg_namelen + msg->msg_controllen]; |
- struct Request *request = reinterpret_cast<struct Request *>(data); |
- request->sysnum = __NR_sendmsg; |
- request->cookie = cookie(); |
- request->sendmsg_req.sockfd = sockfd; |
- request->sendmsg_req.msg = msg; |
- request->sendmsg_req.flags = flags; |
- request->msg = *msg; |
- memcpy(reinterpret_cast<char *>( |
- memcpy(request + 1, msg->msg_name, msg->msg_namelen)) + |
- msg->msg_namelen, |
- msg->msg_control, msg->msg_controllen); |
- |
- long rc; |
- SysCalls sys; |
- if (write(sys, processFdPub(), &data, sizeof(data)) != |
- (ssize_t)sizeof(data) || |
- read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { |
- die("Failed to forward sendmsg() request [sandbox]"); |
- } |
- Debug::elapsed(tm, __NR_sendmsg); |
- return static_cast<ssize_t>(rc); |
-} |
- |
-ssize_t Sandbox::sandbox_sendto(int sockfd, const void* buf, size_t len, |
- int flags, const void* to, socklen_t tolen) { |
- long long tm; |
- Debug::syscall(&tm, __NR_sendto, "Executing handler"); |
- |
- SysCalls sys; |
- if (!to && !flags) { |
- // sendto() with a NULL recipient and no flags is the same as write(), |
- // which is unrestricted in seccomp mode. |
- Debug::message("Replaced sendto() with call to write()"); |
- ssize_t rc = sys.write(sockfd, buf, len); |
- if (rc < 0) { |
- Debug::elapsed(tm, __NR_sendto); |
- return -sys.my_errno; |
- } else { |
- Debug::elapsed(tm, __NR_sendto); |
- return rc; |
- } |
- } |
- |
- struct { |
- int sysnum; |
- long long cookie; |
- SendTo sendto_req; |
- } __attribute__((packed)) request; |
- request.sysnum = __NR_sendto; |
- request.cookie = cookie(); |
- request.sendto_req.sockfd = sockfd; |
- request.sendto_req.buf = buf; |
- request.sendto_req.len = len; |
- request.sendto_req.flags = flags; |
- request.sendto_req.to = to; |
- request.sendto_req.tolen = tolen; |
- |
- long rc; |
- if (write(sys, processFdPub(), &request, sizeof(request)) != |
- sizeof(request) || |
- read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { |
- die("Failed to forward sendto() request [sandbox]"); |
- } |
- Debug::elapsed(tm, __NR_sendto); |
- return static_cast<ssize_t>(rc); |
-} |
- |
-long Sandbox::sandbox_setsockopt(int sockfd, int level, int optname, |
- const void* optval, socklen_t optlen) { |
- long long tm; |
- Debug::syscall(&tm, __NR_setsockopt, "Executing handler"); |
- |
- struct { |
- int sysnum; |
- long long cookie; |
- SetSockOpt setsockopt_req; |
- } __attribute__((packed)) request; |
- request.sysnum = __NR_setsockopt; |
- request.cookie = cookie(); |
- request.setsockopt_req.sockfd = sockfd; |
- request.setsockopt_req.level = level; |
- request.setsockopt_req.optname = optname; |
- request.setsockopt_req.optval = optval; |
- request.setsockopt_req.optlen = optlen; |
- |
- long rc; |
- SysCalls sys; |
- if (write(sys, processFdPub(), &request, sizeof(request)) != |
- sizeof(request) || |
- read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { |
- die("Failed to forward setsockopt() request [sandbox]"); |
- } |
- Debug::elapsed(tm, __NR_setsockopt); |
- return rc; |
-} |
- |
-long Sandbox::sandbox_getsockopt(int sockfd, int level, int optname, |
- void* optval, socklen_t* optlen) { |
- long long tm; |
- Debug::syscall(&tm, __NR_getsockopt, "Executing handler"); |
- |
- struct { |
- int sysnum; |
- long long cookie; |
- GetSockOpt getsockopt_req; |
- } __attribute__((packed)) request; |
- request.sysnum = __NR_getsockopt; |
- request.cookie = cookie(); |
- request.getsockopt_req.sockfd = sockfd; |
- request.getsockopt_req.level = level; |
- request.getsockopt_req.optname = optname; |
- request.getsockopt_req.optval = optval; |
- request.getsockopt_req.optlen = optlen; |
- |
- long rc; |
- SysCalls sys; |
- if (write(sys, processFdPub(), &request, sizeof(request)) != |
- sizeof(request) || |
- read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { |
- die("Failed to forward getsockopt() request [sandbox]"); |
- } |
- Debug::elapsed(tm, __NR_getsockopt); |
- return rc; |
-} |
- |
-bool Sandbox::process_recvfrom(int parentMapsFd, int sandboxFd, |
- int threadFdPub, int threadFd, |
- SecureMem::Args* mem) { |
- // Read request |
- RecvFrom recvfrom_req; |
- SysCalls sys; |
- if (read(sys, sandboxFd, &recvfrom_req, sizeof(recvfrom_req)) != |
- sizeof(recvfrom_req)) { |
- die("Failed to read parameters for recvfrom() [process]"); |
- } |
- |
- // Unsupported flag encountered. Deny the call. |
- if (recvfrom_req.flags & |
- ~(MSG_DONTWAIT|MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_WAITALL)) { |
- SecureMem::abandonSystemCall(threadFd, -EINVAL); |
- return false; |
- } |
- |
- // While we do not anticipate any particular need to receive data on |
- // unconnected sockets, there is no particular risk in doing so. |
- SecureMem::sendSystemCall(threadFdPub, false, -1, mem, |
- __NR_recvfrom, recvfrom_req.sockfd, |
- recvfrom_req.buf, recvfrom_req.len, |
- recvfrom_req.flags, recvfrom_req.from, |
- recvfrom_req.fromlen); |
- return true; |
-} |
- |
-bool Sandbox::process_recvmsg(int parentMapsFd, int sandboxFd, int threadFdPub, |
- int threadFd, SecureMem::Args* mem) { |
- // Read request |
- RecvMsg recvmsg_req; |
- SysCalls sys; |
- if (read(sys, sandboxFd, &recvmsg_req, sizeof(recvmsg_req)) != |
- sizeof(recvmsg_req)) { |
- die("Failed to read parameters for recvmsg() [process]"); |
- } |
- |
- // Unsupported flag encountered. Deny the call. |
- if (recvmsg_req.flags & |
- ~(MSG_DONTWAIT|MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_WAITALL)) { |
- SecureMem::abandonSystemCall(threadFd, -EINVAL); |
- return false; |
- } |
- |
- // Receiving messages is general not security critical. |
- SecureMem::sendSystemCall(threadFdPub, false, -1, mem, |
- __NR_recvmsg, recvmsg_req.sockfd, |
- recvmsg_req.msg, recvmsg_req.flags); |
- return true; |
-} |
- |
-bool Sandbox::process_sendmsg(int parentMapsFd, int sandboxFd, int threadFdPub, |
- int threadFd, SecureMem::Args* mem) { |
- // Read request |
- struct { |
- SendMsg sendmsg_req; |
- struct msghdr msg; |
- } __attribute__((packed)) data; |
- SysCalls sys; |
- if (read(sys, sandboxFd, &data, sizeof(data)) != sizeof(data)) { |
- die("Failed to read parameters for sendmsg() [process]"); |
- } |
- |
- if (data.msg.msg_namelen > 4096 || data.msg.msg_controllen > 4096) { |
- die("Unexpected size for socketcall() payload [process]"); |
- } |
- char extra[data.msg.msg_namelen + data.msg.msg_controllen]; |
- if (read(sys, sandboxFd, &extra, sizeof(extra)) != (ssize_t)sizeof(extra)) { |
- die("Failed to read parameters for sendmsg() [process]"); |
- } |
- if (sizeof(struct msghdr) + sizeof(extra) > sizeof(mem->pathname)) { |
- goto deny; |
- } |
- |
- if (data.msg.msg_namelen || |
- (data.sendmsg_req.flags & |
- ~(MSG_CONFIRM|MSG_DONTWAIT|MSG_EOR|MSG_MORE|MSG_NOSIGNAL|MSG_OOB))) { |
- deny: |
- SecureMem::abandonSystemCall(threadFd, -EINVAL); |
- return false; |
- } |
- |
- // The trusted process receives file handles when a new untrusted thread |
- // gets created. We have security checks in place that prevent any |
- // critical information from being tampered with during thread creation. |
- // But if we disallowed passing of file handles, this would add an extra |
- // hurdle for an attacker. |
- // Unfortunately, for now, this is not possible as Chrome's |
- // base::SendRecvMsg() needs the ability to pass file handles. |
- if (data.msg.msg_controllen) { |
- data.msg.msg_control = extra + data.msg.msg_namelen; |
- struct cmsghdr *cmsg = CMSG_FIRSTHDR(&data.msg); |
- do { |
- if (cmsg->cmsg_level != SOL_SOCKET || |
- cmsg->cmsg_type != SCM_RIGHTS) { |
- goto deny; |
- } |
- } while ((cmsg = CMSG_NXTHDR(&data.msg, cmsg)) != NULL); |
- } |
- |
- // This must be a locked system call, because we have to ensure that the |
- // untrusted code does not tamper with the msghdr after we have examined it. |
- SecureMem::lockSystemCall(parentMapsFd, mem); |
- if (sizeof(extra) > 0) { |
- if (data.msg.msg_namelen > 0) { |
- data.msg.msg_name = mem->pathname + sizeof(struct msghdr); |
- } |
- if (data.msg.msg_controllen > 0) { |
- data.msg.msg_control = mem->pathname + sizeof(struct msghdr) + |
- data.msg.msg_namelen; |
- } |
- memcpy(mem->pathname + sizeof(struct msghdr), extra, sizeof(extra)); |
- } |
- memcpy(mem->pathname, &data.msg, sizeof(struct msghdr)); |
- SecureMem::sendSystemCall(threadFdPub, true, parentMapsFd, mem, |
- __NR_sendmsg, data.sendmsg_req.sockfd, |
- mem->pathname - (char*)mem + (char*)mem->self, |
- data.sendmsg_req.flags); |
- return true; |
-} |
- |
-bool Sandbox::process_sendto(int parentMapsFd, int sandboxFd, int threadFdPub, |
- int threadFd, SecureMem::Args* mem) { |
- // Read request |
- SendTo sendto_req; |
- SysCalls sys; |
- if (read(sys, sandboxFd, &sendto_req, sizeof(sendto_req)) != |
- sizeof(sendto_req)) { |
- die("Failed to read parameters for sendto() [process]"); |
- } |
- |
- // The sandbox does not allow sending to arbitrary addresses. |
- if (sendto_req.to) { |
- SecureMem::abandonSystemCall(threadFd, -EINVAL); |
- return false; |
- } |
- |
- // Unsupported flag encountered. Deny the call. |
- if (sendto_req.flags & |
- ~(MSG_CONFIRM|MSG_DONTWAIT|MSG_EOR|MSG_MORE|MSG_NOSIGNAL|MSG_OOB)) { |
- SecureMem::abandonSystemCall(threadFd, -EINVAL); |
- return false; |
- } |
- |
- // Sending data on a connected socket is similar to calling write(). |
- // Allow it. |
- SecureMem::sendSystemCall(threadFdPub, false, -1, mem, |
- __NR_sendto, sendto_req.sockfd, |
- sendto_req.buf, sendto_req.len, |
- sendto_req.flags, sendto_req.to, |
- sendto_req.tolen); |
- return true; |
-} |
- |
-bool Sandbox::process_setsockopt(int parentMapsFd, int sandboxFd, |
- int threadFdPub, int threadFd, |
- SecureMem::Args* mem) { |
- // Read request |
- SetSockOpt setsockopt_req; |
- SysCalls sys; |
- if (read(sys, sandboxFd, &setsockopt_req, sizeof(setsockopt_req)) != |
- sizeof(setsockopt_req)) { |
- die("Failed to read parameters for setsockopt() [process]"); |
- } |
- |
- switch (setsockopt_req.level) { |
- case SOL_SOCKET: |
- switch (setsockopt_req.optname) { |
- case SO_KEEPALIVE: |
- case SO_LINGER: |
- case SO_OOBINLINE: |
- case SO_RCVBUF: |
- case SO_RCVLOWAT: |
- case SO_SNDLOWAT: |
- case SO_RCVTIMEO: |
- case SO_SNDTIMEO: |
- case SO_REUSEADDR: |
- case SO_SNDBUF: |
- case SO_TIMESTAMP: |
- SecureMem::sendSystemCall(threadFdPub, false, -1, mem, |
- __NR_setsockopt, setsockopt_req.sockfd, |
- setsockopt_req.level, setsockopt_req.optname, |
- setsockopt_req.optval, setsockopt_req.optlen); |
- return true; |
- default: |
- break; |
- } |
- break; |
- case IPPROTO_TCP: |
- switch (setsockopt_req.optname) { |
- case TCP_CORK: |
- case TCP_DEFER_ACCEPT: |
- case TCP_INFO: |
- case TCP_KEEPCNT: |
- case TCP_KEEPIDLE: |
- case TCP_KEEPINTVL: |
- case TCP_LINGER2: |
- case TCP_MAXSEG: |
- case TCP_NODELAY: |
- case TCP_QUICKACK: |
- case TCP_SYNCNT: |
- case TCP_WINDOW_CLAMP: |
- SecureMem::sendSystemCall(threadFdPub, false, -1, mem, |
- __NR_setsockopt, setsockopt_req.sockfd, |
- setsockopt_req.level, setsockopt_req.optname, |
- setsockopt_req.optval, setsockopt_req.optlen); |
- return true; |
- default: |
- break; |
- } |
- break; |
- default: |
- break; |
- } |
- SecureMem::abandonSystemCall(threadFd, -EINVAL); |
- return false; |
-} |
- |
-bool Sandbox::process_getsockopt(int parentMapsFd, int sandboxFd, |
- int threadFdPub, int threadFd, |
- SecureMem::Args* mem) { |
- // Read request |
- GetSockOpt getsockopt_req; |
- SysCalls sys; |
- if (read(sys, sandboxFd, &getsockopt_req, sizeof(getsockopt_req)) != |
- sizeof(getsockopt_req)) { |
- die("Failed to read parameters for getsockopt() [process]"); |
- } |
- |
- switch (getsockopt_req.level) { |
- case SOL_SOCKET: |
- switch (getsockopt_req.optname) { |
- case SO_ACCEPTCONN: |
- case SO_ERROR: |
- case SO_KEEPALIVE: |
- case SO_LINGER: |
- case SO_OOBINLINE: |
- case SO_RCVBUF: |
- case SO_RCVLOWAT: |
- case SO_SNDLOWAT: |
- case SO_RCVTIMEO: |
- case SO_SNDTIMEO: |
- case SO_REUSEADDR: |
- case SO_SNDBUF: |
- case SO_TIMESTAMP: |
- case SO_TYPE: |
- SecureMem::sendSystemCall(threadFdPub, false, -1, mem, |
- __NR_getsockopt, getsockopt_req.sockfd, |
- getsockopt_req.level, getsockopt_req.optname, |
- getsockopt_req.optval, getsockopt_req.optlen); |
- return true; |
- default: |
- break; |
- } |
- break; |
- case IPPROTO_TCP: |
- switch (getsockopt_req.optname) { |
- case TCP_CORK: |
- case TCP_DEFER_ACCEPT: |
- case TCP_INFO: |
- case TCP_KEEPCNT: |
- case TCP_KEEPIDLE: |
- case TCP_KEEPINTVL: |
- case TCP_LINGER2: |
- case TCP_MAXSEG: |
- case TCP_NODELAY: |
- case TCP_QUICKACK: |
- case TCP_SYNCNT: |
- case TCP_WINDOW_CLAMP: |
- SecureMem::sendSystemCall(threadFdPub, false, -1, mem, |
- __NR_getsockopt, getsockopt_req.sockfd, |
- getsockopt_req.level, getsockopt_req.optname, |
- getsockopt_req.optval, getsockopt_req.optlen); |
- return true; |
- default: |
- break; |
- } |
- break; |
- default: |
- break; |
- } |
- SecureMem::abandonSystemCall(threadFd, -EINVAL); |
- return false; |
-} |
- |
-#endif |
-#if defined(__NR_socketcall) |
- |
-enum { |
- SYS_SOCKET = 1, |
- SYS_BIND = 2, |
- SYS_CONNECT = 3, |
- SYS_LISTEN = 4, |
- SYS_ACCEPT = 5, |
- SYS_GETSOCKNAME = 6, |
- SYS_GETPEERNAME = 7, |
- SYS_SOCKETPAIR = 8, |
- SYS_SEND = 9, |
- SYS_RECV = 10, |
- SYS_SENDTO = 11, |
- SYS_RECVFROM = 12, |
- SYS_SHUTDOWN = 13, |
- SYS_SETSOCKOPT = 14, |
- SYS_GETSOCKOPT = 15, |
- SYS_SENDMSG = 16, |
- SYS_RECVMSG = 17, |
- SYS_ACCEPT4 = 18 |
-}; |
- |
-struct Sandbox::SocketCallArgInfo { |
- size_t len; |
- off_t addrOff; |
- off_t lengthOff; |
-}; |
-const struct Sandbox::SocketCallArgInfo Sandbox::socketCallArgInfo[] = { |
- #define STRUCT(s) reinterpret_cast<SocketCall *>(0)->args.s |
- #define SIZE(s) sizeof(STRUCT(s)) |
- #define OFF(s, f) offsetof(typeof STRUCT(s), f) |
- { 0 }, |
- { SIZE(socket) }, |
- { SIZE(bind), OFF(bind, addr), OFF(bind, addrlen) }, |
- { SIZE(connect), OFF(connect, addr), OFF(connect, addrlen) }, |
- { SIZE(listen) }, |
- { SIZE(accept) }, |
- { SIZE(getsockname) }, |
- { SIZE(getpeername) }, |
- { SIZE(socketpair) }, |
- { SIZE(send) }, |
- { SIZE(recv) }, |
- { SIZE(sendto), OFF(sendto, to), OFF(sendto, tolen) }, |
- { SIZE(recvfrom) }, |
- { SIZE(shutdown) }, |
- { SIZE(setsockopt), OFF(setsockopt, optval), OFF(setsockopt, optlen) }, |
- { SIZE(getsockopt) }, |
- { SIZE(sendmsg) }, |
- { SIZE(recvmsg) }, |
- { SIZE(accept4) } |
- #undef STRUCT |
- #undef SIZE |
- #undef OFF |
-}; |
- |
-long Sandbox::sandbox_socketcall(int call, void* args) { |
- long long tm; |
- Debug::syscall(&tm, __NR_socketcall, "Executing handler", call); |
- |
- // When demultiplexing socketcall(), only accept calls that have a valid |
- // "call" opcode. |
- if (call < SYS_SOCKET || call > SYS_ACCEPT4) { |
- Debug::elapsed(tm, __NR_socketcall, call); |
- return -ENOSYS; |
- } |
- |
- // Some type of calls include a pointer to an address or name, which cannot |
- // be accessed by the trusted process, as it lives in a separate address |
- // space. For these calls, append the extra data to the serialized request. |
- // This requires some copying of data, as we have to make sure there is |
- // only a single atomic call to write(). |
- socklen_t numExtraData = 0; |
- const void* extraDataAddr = NULL; |
- if (socketCallArgInfo[call].lengthOff) { |
- memcpy(&numExtraData, |
- reinterpret_cast<char *>(args) + socketCallArgInfo[call].lengthOff, |
- sizeof(socklen_t)); |
- extraDataAddr = reinterpret_cast<char *>(args) + |
- socketCallArgInfo[call].addrOff; |
- } |
- |
- // sendmsg() and recvmsg() have more complicated requirements for computing |
- // the amount of extra data that needs to be sent to the trusted process. |
- if (call == SYS_SENDMSG) { |
- SendMsg *sendmsg_args = reinterpret_cast<SendMsg *>(args); |
- if (sendmsg_args->msg->msg_iovlen == 1 && |
- !sendmsg_args->msg->msg_control) { |
- // Further down in the code, this sendmsg() call will be simplified to |
- // a sendto() call. Make sure we already compute the correct value for |
- // numExtraData, as it is needed when we allocate "data[]" on the stack. |
- numExtraData = sendmsg_args->msg->msg_namelen; |
- extraDataAddr = sendmsg_args->msg->msg_name; |
- } else { |
- // sendmsg() needs to include some of the extra data so that we can |
- // inspect it in process_socketcall() |
- numExtraData = sizeof(*sendmsg_args->msg) + |
- sendmsg_args->msg->msg_namelen + |
- sendmsg_args->msg->msg_controllen; |
- extraDataAddr = NULL; |
- } |
- } |
- if (call == SYS_RECVMSG) { |
- RecvMsg *recvmsg_args = reinterpret_cast<RecvMsg *>(args); |
- numExtraData = sizeof(*recvmsg_args->msg); |
- extraDataAddr = recvmsg_args->msg; |
- } |
- |
- // Set up storage for the request header and copy the data from "args" |
- // into it. |
- struct Request { |
- int sysnum; |
- long long cookie; |
- SocketCall socketcall_req; |
- } __attribute__((packed)) *request; |
- char data[sizeof(struct Request) + numExtraData]; |
- request = reinterpret_cast<struct Request *>(data); |
- memcpy(&request->socketcall_req.args, args, socketCallArgInfo[call].len); |
- |
- // Simplify send(), sendto() and sendmsg(), if there are simpler equivalent |
- // calls. This allows us to occasionally replace them with calls to write(), |
- // which don't have to be forwarded to the trusted process. |
- SysCalls sys; |
- if (call == SYS_SENDMSG && |
- request->socketcall_req.args.sendmsg.msg->msg_iovlen == 1 && |
- !request->socketcall_req.args.sendmsg.msg->msg_control) { |
- // Ordering of these assignments is important, as we are reshuffling |
- // fields inside of a union. |
- call = SYS_SENDTO; |
- request->socketcall_req.args.sendto.flags = |
- request->socketcall_req.args.sendmsg.flags; |
- request->socketcall_req.args.sendto.to = |
- request->socketcall_req.args.sendmsg.msg->msg_name; |
- request->socketcall_req.args.sendto.tolen = |
- request->socketcall_req.args.sendmsg.msg->msg_namelen; |
- request->socketcall_req.args.sendto.len = |
- request->socketcall_req.args.sendmsg.msg->msg_iov->iov_len; |
- request->socketcall_req.args.sendto.buf = |
- request->socketcall_req.args.sendmsg.msg->msg_iov->iov_base; |
- } |
- if (call == SYS_SENDTO && !request->socketcall_req.args.sendto.to) { |
- // sendto() with a NULL address is the same as send() |
- call = SYS_SEND; |
- numExtraData = 0; |
- } |
- if (call == SYS_SEND && !request->socketcall_req.args.send.flags) { |
- // send() with no flags is the same as write(), which is unrestricted |
- // in seccomp mode. |
- Debug::message("Replaced socketcall() with call to write()"); |
- ssize_t rc = sys.write(request->socketcall_req.args.send.sockfd, |
- request->socketcall_req.args.send.buf, |
- request->socketcall_req.args.send.len); |
- if (rc < 0) { |
- Debug::elapsed(tm, __NR_socketcall, call); |
- return -sys.my_errno; |
- } else { |
- Debug::elapsed(tm, __NR_socketcall, call); |
- return rc; |
- } |
- } |
- |
- // Simplify recv(), and recvfrom(), if there are simpler equivalent calls. |
- // This allows us to occasionally replace them with calls to read(), which |
- // don't have to be forwarded to the trusted process. |
- // We cannot simplify recvmsg() to recvfrom(), recv() or read(), as we do |
- // not know whether the caller needs us to set msg->msg_flags. |
- if (call == SYS_RECVFROM && !request->socketcall_req.args.recvfrom.from) { |
- // recvfrom() with a NULL address buffer is the same as recv() |
- call = SYS_RECV; |
- } |
- if (call == SYS_RECV && !request->socketcall_req.args.recv.flags) { |
- // recv() with no flags is the same as read(), which is unrestricted |
- // in seccomp mode. |
- Debug::message("Replaced socketcall() with call to read()"); |
- ssize_t rc = sys.read(request->socketcall_req.args.recv.sockfd, |
- request->socketcall_req.args.recv.buf, |
- request->socketcall_req.args.recv.len); |
- if (rc < 0) { |
- Debug::elapsed(tm, __NR_socketcall, call); |
- return -sys.my_errno; |
- } else { |
- Debug::elapsed(tm, __NR_socketcall, call); |
- return rc; |
- } |
- } |
- |
- // Fill in the rest of the request header. |
- request->sysnum = __NR_socketcall; |
- request->cookie = cookie(); |
- request->socketcall_req.call = call; |
- request->socketcall_req.arg_ptr = args; |
- int padding = sizeof(request->socketcall_req.args) - |
- socketCallArgInfo[call].len; |
- if (padding > 0) { |
- memset((char *)(&request->socketcall_req.args + 1) - padding, 0, padding); |
- } |
- if (call == SYS_SENDMSG) { |
- // for sendmsg() we include the (optional) destination address, and the |
- // (optional) control data in the payload. |
- SendMsg *sendmsg_args = reinterpret_cast<SendMsg *>(args); |
- memcpy(reinterpret_cast<char *>( |
- memcpy(reinterpret_cast<char *>( |
- memcpy(request + 1, sendmsg_args->msg, sizeof(*sendmsg_args->msg))) + |
- sizeof(*sendmsg_args->msg), |
- sendmsg_args->msg->msg_name, sendmsg_args->msg->msg_namelen)) + |
- sendmsg_args->msg->msg_namelen, |
- sendmsg_args->msg->msg_control, sendmsg_args->msg->msg_controllen); |
- } else if (extraDataAddr) { |
- memcpy(request + 1, extraDataAddr, numExtraData); |
- } |
- |
- // Send request to trusted process and collect response from trusted thread. |
- long rc; |
- ssize_t len = sizeof(struct Request) + numExtraData; |
- if (write(sys, processFdPub(), data, len) != len || |
- read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { |
- die("Failed to forward socketcall() request [sandbox]"); |
- } |
- Debug::elapsed(tm, __NR_socketcall, call); |
- return rc; |
-} |
- |
-bool Sandbox::process_socketcall(int parentMapsFd, int sandboxFd, |
- int threadFdPub, int threadFd, |
- SecureMem::Args* mem) { |
- // Read request |
- SocketCall socketcall_req; |
- SysCalls sys; |
- if (read(sys, sandboxFd, &socketcall_req, sizeof(socketcall_req)) != |
- sizeof(socketcall_req)) { |
- die("Failed to read parameters for socketcall() [process]"); |
- } |
- |
- // sandbox_socketcall() should never send us an unexpected "call" opcode. |
- // If it did, something went very wrong and we better terminate the process. |
- if (socketcall_req.call < SYS_SOCKET || socketcall_req.call > SYS_ACCEPT4) { |
- die("Unexpected socketcall() [process]"); |
- } |
- |
- // Check if this particular operation carries an extra payload. |
- socklen_t numExtraData = 0; |
- if (socketCallArgInfo[socketcall_req.call].lengthOff) { |
- memcpy(&numExtraData, |
- reinterpret_cast<char *>(&socketcall_req) + |
- socketCallArgInfo[socketcall_req.call].lengthOff, |
- sizeof(socklen_t)); |
- } else if (socketcall_req.call == SYS_SENDMSG) { |
- numExtraData = sizeof(*socketcall_req.args.sendmsg.msg); |
- } else if (socketcall_req.call == SYS_RECVMSG) { |
- numExtraData = sizeof(*socketcall_req.args.recvmsg.msg); |
- } |
- |
- // Verify that the length for the payload is reasonable. We don't want to |
- // blow up our stack, and excessive (or negative) buffer sizes are almost |
- // certainly a bug. |
- if (numExtraData > 4096) { |
- die("Unexpected size for socketcall() payload [process]"); |
- } |
- |
- // Read the extra payload, if any. |
- char extra[numExtraData]; |
- if (numExtraData) { |
- if (read(sys, sandboxFd, extra, numExtraData) != (ssize_t)numExtraData) { |
- die("Failed to read socketcall() payload [process]"); |
- } |
- } |
- |
- // sendmsg() has another level of indirection and can carry even more payload |
- ssize_t numSendmsgExtra = 0; |
- if (socketcall_req.call == SYS_SENDMSG) { |
- struct msghdr* msg = reinterpret_cast<struct msghdr*>(extra); |
- if (msg->msg_namelen > 4096 || msg->msg_controllen > 4096) { |
- die("Unexpected size for socketcall() payload [process]"); |
- } |
- numSendmsgExtra = msg->msg_namelen + msg->msg_controllen; |
- } |
- char sendmsgExtra[numSendmsgExtra]; |
- if (numSendmsgExtra) { |
- if (read(sys, sandboxFd, sendmsgExtra, numSendmsgExtra) != |
- numSendmsgExtra) { |
- die("Failed to read socketcall() payload [process]"); |
- } |
- } |
- |
- int rc = -EINVAL; |
- switch (socketcall_req.call) { |
- case SYS_SOCKET: |
- // The sandbox does not allow creation of any new sockets. |
- goto deny; |
- case SYS_BIND: |
- // The sandbox does not allow binding an address to a socket. |
- goto deny; |
- case SYS_CONNECT: |
- // The sandbox does not allow connecting a socket. |
- goto deny; |
- case SYS_LISTEN: |
- // The sandbox does not allow a socket to enter listening state. |
- goto deny; |
- case SYS_ACCEPT4: |
- case SYS_ACCEPT: |
- // If the sandbox obtained a socket that is already in the listening |
- // state (e.g. because somebody sent it a suitable file descriptor), it |
- // is permissible to call accept(). |
- |
- accept_simple: |
- // None of the parameters need to be checked, so it is OK to refer |
- // to the parameter block created by the untrusted code. |
- SecureMem::sendSystemCall(threadFdPub, false, -1, mem, __NR_socketcall, |
- socketcall_req.call, socketcall_req.arg_ptr); |
- return true; |
- case SYS_GETSOCKNAME: |
- case SYS_GETPEERNAME: |
- // Querying the local and the remote name is not considered security |
- // sensitive for the purposes of the sandbox. |
- goto accept_simple; |
- case SYS_SOCKETPAIR: |
- // Socket pairs are connected to each other and not considered |
- // security sensitive. |
- goto accept_simple; |
- case SYS_SENDTO: |
- if (socketcall_req.args.sendto.to) { |
- // The sandbox does not allow sending to arbitrary addresses. |
- goto deny; |
- } |
- // Fall through |
- case SYS_SEND: |
- if (socketcall_req.args.send.flags & |
- ~(MSG_CONFIRM|MSG_DONTWAIT|MSG_EOR|MSG_MORE|MSG_NOSIGNAL|MSG_OOB)) { |
- // Unsupported flag encountered. Deny the call. |
- goto deny; |
- } |
- // Sending data on a connected socket is similar to calling write(). |
- // Allow it. |
- |
- accept_complex: |
- // The parameter block contains potentially security critical information |
- // that should not be tampered with after it has been inspected. Copy it |
- // into the write-protected securely shared memory before telling the |
- // trusted thread to execute the socket call. |
- SecureMem::lockSystemCall(parentMapsFd, mem); |
- memcpy(mem->pathname, &socketcall_req.args, sizeof(socketcall_req.args)); |
- SecureMem::sendSystemCall(threadFdPub, true, parentMapsFd, mem, |
- __NR_socketcall, socketcall_req.call, |
- mem->pathname - (char*)mem + (char*)mem->self); |
- return true; |
- case SYS_RECVFROM: |
- // While we do not anticipate any particular need to receive data on |
- // unconnected sockets, there is no particular risk in doing so. |
- // Fall through |
- case SYS_RECV: |
- if (socketcall_req.args.recv.flags & |
- ~(MSG_DONTWAIT|MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_WAITALL)) { |
- // Unsupported flag encountered. Deny the call. |
- goto deny; |
- } |
- // Receiving data on a connected socket is similar to calling read(). |
- // Allow it. |
- goto accept_complex; |
- case SYS_SHUTDOWN: |
- // Shutting down a socket is always OK. |
- goto accept_simple; |
- case SYS_SETSOCKOPT: |
- switch (socketcall_req.args.setsockopt.level) { |
- case SOL_SOCKET: |
- switch (socketcall_req.args.setsockopt.optname) { |
- case SO_KEEPALIVE: |
- case SO_LINGER: |
- case SO_OOBINLINE: |
- case SO_RCVBUF: |
- case SO_RCVLOWAT: |
- case SO_SNDLOWAT: |
- case SO_RCVTIMEO: |
- case SO_SNDTIMEO: |
- case SO_REUSEADDR: |
- case SO_SNDBUF: |
- case SO_TIMESTAMP: |
- goto accept_complex; |
- default: |
- break; |
- } |
- break; |
- case IPPROTO_TCP: |
- switch (socketcall_req.args.setsockopt.optname) { |
- case TCP_CORK: |
- case TCP_DEFER_ACCEPT: |
- case TCP_INFO: |
- case TCP_KEEPCNT: |
- case TCP_KEEPIDLE: |
- case TCP_KEEPINTVL: |
- case TCP_LINGER2: |
- case TCP_MAXSEG: |
- case TCP_NODELAY: |
- case TCP_QUICKACK: |
- case TCP_SYNCNT: |
- case TCP_WINDOW_CLAMP: |
- goto accept_complex; |
- default: |
- break; |
- } |
- break; |
- default: |
- break; |
- } |
- goto deny; |
- case SYS_GETSOCKOPT: |
- switch (socketcall_req.args.getsockopt.level) { |
- case SOL_SOCKET: |
- switch (socketcall_req.args.getsockopt.optname) { |
- case SO_ACCEPTCONN: |
- case SO_ERROR: |
- case SO_KEEPALIVE: |
- case SO_LINGER: |
- case SO_OOBINLINE: |
- case SO_RCVBUF: |
- case SO_RCVLOWAT: |
- case SO_SNDLOWAT: |
- case SO_RCVTIMEO: |
- case SO_SNDTIMEO: |
- case SO_REUSEADDR: |
- case SO_SNDBUF: |
- case SO_TIMESTAMP: |
- case SO_TYPE: |
- goto accept_complex; |
- default: |
- break; |
- } |
- break; |
- case IPPROTO_TCP: |
- switch (socketcall_req.args.getsockopt.optname) { |
- case TCP_CORK: |
- case TCP_DEFER_ACCEPT: |
- case TCP_INFO: |
- case TCP_KEEPCNT: |
- case TCP_KEEPIDLE: |
- case TCP_KEEPINTVL: |
- case TCP_LINGER2: |
- case TCP_MAXSEG: |
- case TCP_NODELAY: |
- case TCP_QUICKACK: |
- case TCP_SYNCNT: |
- case TCP_WINDOW_CLAMP: |
- goto accept_complex; |
- default: |
- break; |
- } |
- break; |
- default: |
- break; |
- } |
- goto deny; |
- case SYS_SENDMSG: { |
- struct msghdr* msg = reinterpret_cast<struct msghdr*>(extra); |
- |
- if (sizeof(socketcall_req.args) + sizeof(*msg) + numSendmsgExtra > |
- sizeof(mem->pathname)) { |
- goto deny; |
- } |
- |
- if (msg->msg_namelen || |
- (socketcall_req.args.sendmsg.flags & |
- ~(MSG_CONFIRM|MSG_DONTWAIT|MSG_EOR|MSG_MORE|MSG_NOSIGNAL|MSG_OOB))){ |
- goto deny; |
- } |
- |
- // The trusted process receives file handles when a new untrusted thread |
- // gets created. We have security checks in place that prevent any |
- // critical information from being tampered with during thread creation. |
- // But if we disallowed passing of file handles, this would add an extra |
- // hurdle for an attacker. |
- // Unfortunately, for now, this is not possible as Chrome's |
- // base::SendRecvMsg() needs the ability to pass file handles. |
- if (msg->msg_controllen) { |
- msg->msg_control = sendmsgExtra + msg->msg_namelen; |
- struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); |
- do { |
- if (cmsg->cmsg_level != SOL_SOCKET || |
- cmsg->cmsg_type != SCM_RIGHTS) { |
- goto deny; |
- } |
- } while ((cmsg = CMSG_NXTHDR(msg, cmsg)) != NULL); |
- } |
- |
- // This must be a locked system call, because we have to ensure that |
- // the untrusted code does not tamper with the msghdr after we have |
- // examined it. |
- SecureMem::lockSystemCall(parentMapsFd, mem); |
- socketcall_req.args.sendmsg.msg = |
- reinterpret_cast<struct msghdr*>(mem->pathname + |
- sizeof(socketcall_req.args) - |
- (char*)mem + (char*)mem->self); |
- memcpy(mem->pathname, &socketcall_req.args, sizeof(socketcall_req.args)); |
- if (numSendmsgExtra) { |
- if (msg->msg_namelen > 0) { |
- msg->msg_name = const_cast<struct msghdr*>( |
- socketcall_req.args.sendmsg.msg) + 1; |
- } |
- if (msg->msg_controllen > 0) { |
- msg->msg_control = (char *)( |
- socketcall_req.args.sendmsg.msg + 1) + msg->msg_namelen; |
- } |
- memcpy(mem->pathname + sizeof(socketcall_req.args) + sizeof(*msg), |
- sendmsgExtra, numSendmsgExtra); |
- } |
- memcpy(mem->pathname + sizeof(socketcall_req.args), msg, sizeof(*msg)); |
- SecureMem::sendSystemCall(threadFdPub, true, parentMapsFd, mem, |
- __NR_socketcall, socketcall_req.call, |
- mem->pathname - (char*)mem + (char*)mem->self); |
- return true; |
- } |
- case SYS_RECVMSG: |
- // Receiving messages is general not security critical. |
- if (socketcall_req.args.recvmsg.flags & |
- ~(MSG_DONTWAIT|MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_WAITALL)) { |
- goto deny; |
- } |
- goto accept_complex; |
- default: |
- deny: |
- SecureMem::abandonSystemCall(threadFd, rc); |
- return false; |
- } |
-} |
- |
-#endif |
- |
-} // namespace |