diff options
Diffstat (limited to 'sandbox/linux/seccomp/socketcall.cc')
-rw-r--r-- | sandbox/linux/seccomp/socketcall.cc | 1039 |
1 files changed, 0 insertions, 1039 deletions
diff --git a/sandbox/linux/seccomp/socketcall.cc b/sandbox/linux/seccomp/socketcall.cc deleted file mode 100644 index c7b2015..0000000 --- a/sandbox/linux/seccomp/socketcall.cc +++ /dev/null @@ -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 |