зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
2dfa36102d
Коммит
bb4d6b8630
|
@ -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
|
||||||
|
|
Загрузка…
Ссылка в новой задаче