Bug 1511560 - Move the socketpair handling into SandboxPolicyCommon. r=gcp

The sandbox broker uses socketpair to construct the per-request channels
over which responses are sent; thus, if and only if the policy will be
using brokering, it will allow socketpair as safely as possible (i.e.,
denying datagram sockets if possible).

Depends on D14522

Differential Revision: https://phabricator.services.mozilla.com/D14523

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Jed Davis 2019-02-23 00:44:10 +00:00
Родитель 2dfa36102d
Коммит bb4d6b8630
3 изменённых файлов: 77 добавлений и 61 удалений

Просмотреть файл

@ -292,6 +292,24 @@ class SandboxPolicyCommon : public SandboxPolicyBase {
return broker->Readlink(path, buf, size); return broker->Readlink(path, buf, size);
} }
static intptr_t SocketpairDatagramTrap(ArgsRef aArgs, void* aux) {
auto fds = reinterpret_cast<int*>(aArgs.args[3]);
// Return sequential packet sockets instead of the expected
// datagram sockets; see bug 1355274 for details.
return ConvertError(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
}
static intptr_t SocketpairUnpackTrap(ArgsRef aArgs, void* aux) {
#ifdef __NR_socketpair
auto argsPtr = reinterpret_cast<unsigned long*>(aArgs.args[1]);
return DoSyscall(__NR_socketpair, argsPtr[0], argsPtr[1], argsPtr[2],
argsPtr[3]);
#else
MOZ_CRASH("unreachable?");
return -ENOSYS;
#endif
}
public: public:
ResultExpr InvalidSyscall() const override { ResultExpr InvalidSyscall() const override {
return Trap(BlockedSyscallTrap, nullptr); return Trap(BlockedSyscallTrap, nullptr);
@ -341,6 +359,39 @@ class SandboxPolicyCommon : public SandboxPolicyBase {
case SYS_RECVMSG: case SYS_RECVMSG:
case SYS_SENDMSG: case SYS_SENDMSG:
return Some(Allow()); return Some(Allow());
case SYS_SOCKETPAIR: {
// Allow "safe" (always connected) socketpairs when using the
// file broker.
if (mBroker == nullptr) {
return Nothing();
}
// See bug 1066750.
if (!aHasArgs) {
// If this is a socketcall(2) platform, but the kernel also
// supports separate syscalls (>= 4.2.0), we can unpack the
// arguments and filter them.
if (HasSeparateSocketCalls()) {
return Some(Trap(SocketpairUnpackTrap, nullptr));
}
// Otherwise, we can't filter the args if the platform passes
// them by pointer.
return Some(Allow());
}
Arg<int> domain(0), type(1);
return Some(
If(domain == AF_UNIX,
Switch(type & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
.Case(SOCK_STREAM, Allow())
.Case(SOCK_SEQPACKET, Allow())
// This is used only by content (and only for
// direct PulseAudio, which is deprecated) but it
// doesn't increase attack surface:
.Case(SOCK_DGRAM, Trap(SocketpairDatagramTrap, nullptr))
.Default(InvalidSyscall()))
.Else(InvalidSyscall()));
}
default: default:
return Nothing(); return Nothing();
} }
@ -601,25 +652,6 @@ class ContentSandboxPolicy : public SandboxPolicyCommon {
return AllowBelowLevel(aLevel, InvalidSyscall()); return AllowBelowLevel(aLevel, InvalidSyscall());
} }
// Returns true if the running kernel supports separate syscalls for
// socket operations, or false if it supports only socketcall(2).
static bool HasSeparateSocketCalls() {
# ifdef __NR_socket
// If there's no socketcall, then obviously there are separate syscalls.
# ifdef __NR_socketcall
int fd = syscall(__NR_socket, AF_LOCAL, SOCK_STREAM, 0);
if (fd < 0) {
MOZ_DIAGNOSTIC_ASSERT(errno == ENOSYS);
return false;
}
close(fd);
# endif // __NR_socketcall
return true;
# else // ifndef __NR_socket
return false;
# endif // __NR_socket
}
static intptr_t GetPPidTrap(ArgsRef aArgs, void* aux) { static intptr_t GetPPidTrap(ArgsRef aArgs, void* aux) {
// In a pid namespace, getppid() will return 0. We will return 0 instead // In a pid namespace, getppid() will return 0. We will return 0 instead
// of the real parent pid to see what breaks when we introduce the // of the real parent pid to see what breaks when we introduce the
@ -627,24 +659,6 @@ class ContentSandboxPolicy : public SandboxPolicyCommon {
return 0; return 0;
} }
static intptr_t SocketpairDatagramTrap(ArgsRef aArgs, void* aux) {
auto fds = reinterpret_cast<int*>(aArgs.args[3]);
// Return sequential packet sockets instead of the expected
// datagram sockets; see bug 1355274 for details.
return ConvertError(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
}
static intptr_t SocketpairUnpackTrap(ArgsRef aArgs, void* aux) {
# ifdef __NR_socketpair
auto argsPtr = reinterpret_cast<unsigned long*>(aArgs.args[1]);
return DoSyscall(__NR_socketpair, argsPtr[0], argsPtr[1], argsPtr[2],
argsPtr[3]);
# else
MOZ_CRASH("unreachable?");
return -ENOSYS;
# endif
}
static intptr_t StatFsTrap(ArgsRef aArgs, void* aux) { static intptr_t StatFsTrap(ArgsRef aArgs, void* aux) {
// Warning: the kernel interface is not the C interface. The // Warning: the kernel interface is not the C interface. The
// structs are different (<asm/statfs.h> vs. <sys/statfs.h>), and // structs are different (<asm/statfs.h> vs. <sys/statfs.h>), and
@ -811,30 +825,6 @@ class ContentSandboxPolicy : public SandboxPolicyCommon {
case SYS_SENDMMSG: // libresolv via libasyncns; see bug 1355274 case SYS_SENDMMSG: // libresolv via libasyncns; see bug 1355274
return Some(Allow()); return Some(Allow());
case SYS_SOCKETPAIR: {
// See bug 1066750.
if (!aHasArgs) {
// If this is a socketcall(2) platform, but the kernel also
// supports separate syscalls (>= 4.2.0), we can unpack the
// arguments and filter them.
if (HasSeparateSocketCalls()) {
return Some(Trap(SocketpairUnpackTrap, nullptr));
}
// Otherwise, we can't filter the args if the platform passes
// them by pointer.
return Some(Allow());
}
Arg<int> domain(0), type(1);
return Some(
If(domain == AF_UNIX,
Switch(type & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
.Case(SOCK_STREAM, Allow())
.Case(SOCK_SEQPACKET, Allow())
.Case(SOCK_DGRAM, Trap(SocketpairDatagramTrap, nullptr))
.Default(InvalidSyscall()))
.Else(InvalidSyscall()));
}
# ifdef ANDROID # ifdef ANDROID
case SYS_SOCKET: case SYS_SOCKET:
return Some(Error(EACCES)); return Some(Error(EACCES));

Просмотреть файл

@ -10,6 +10,9 @@
# include <linux/ipc.h> # include <linux/ipc.h>
#endif #endif
#include <linux/net.h> #include <linux/net.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <unistd.h>
#include "mozilla/UniquePtr.h" #include "mozilla/UniquePtr.h"
#include "sandbox/linux/bpf_dsl/bpf_dsl.h" #include "sandbox/linux/bpf_dsl/bpf_dsl.h"
@ -120,4 +123,23 @@ sandbox::bpf_dsl::ResultExpr SandboxPolicyBase::EvaluateSyscall(
} }
} }
/* static */ bool SandboxPolicyBase::HasSeparateSocketCalls() {
#ifdef __NR_socket
// If there's no socketcall, then obviously there are separate syscalls.
# ifdef __NR_socketcall
// This could be memoized, but currently it's called at most once
// per process.
int fd = syscall(__NR_socket, AF_LOCAL, SOCK_STREAM, 0);
if (fd < 0) {
MOZ_DIAGNOSTIC_ASSERT(errno == ENOSYS);
return false;
}
close(fd);
# endif // __NR_socketcall
return true;
#else // ifndef __NR_socket
return false;
#endif // __NR_socket
}
} // namespace mozilla } // namespace mozilla

Просмотреть файл

@ -49,6 +49,10 @@ class SandboxPolicyBase : public sandbox::bpf_dsl::Policy {
return Nothing(); return Nothing();
} }
#endif #endif
// Returns true if the running kernel supports separate syscalls for
// socket operations, or false if it supports only socketcall(2).
static bool HasSeparateSocketCalls();
}; };
} // namespace mozilla } // namespace mozilla