374 lines
13 KiB
Diff
374 lines
13 KiB
Diff
|
diff --git a/security/sandbox/linux/SandboxFilter.cpp b/security/sandbox/linux/SandboxFilter.cpp
|
||
|
--- a/security/sandbox/linux/SandboxFilter.cpp
|
||
|
+++ b/security/sandbox/linux/SandboxFilter.cpp
|
||
|
@@ -128,10 +128,11 @@
|
||
|
// Subclasses can assign these in their constructors to loosen the
|
||
|
// default settings.
|
||
|
SandboxBrokerClient* mBroker = nullptr;
|
||
|
bool mMayCreateShmem = false;
|
||
|
bool mAllowUnsafeSocketPair = false;
|
||
|
+ bool mBrokeredConnect = false; // Can connect() be brokered?
|
||
|
|
||
|
SandboxPolicyCommon() = default;
|
||
|
|
||
|
typedef const sandbox::arch_seccomp_data& ArgsRef;
|
||
|
|
||
|
@@ -533,10 +534,124 @@
|
||
|
MOZ_CRASH("unreachable?");
|
||
|
return -ENOSYS;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
+ // This just needs to return something to stand in for the
|
||
|
+ // unconnected socket until ConnectTrap, below, and keep track of
|
||
|
+ // the socket type somehow. Half a socketpair *is* a socket, so it
|
||
|
+ // should result in minimal confusion in the caller.
|
||
|
+ static intptr_t FakeSocketTrapCommon(int domain, int type, int protocol) {
|
||
|
+ int fds[2];
|
||
|
+ // X11 client libs will still try to getaddrinfo() even for a
|
||
|
+ // local connection. Also, WebRTC still has vestigial network
|
||
|
+ // code trying to do things in the content process. Politely tell
|
||
|
+ // them no.
|
||
|
+ if (domain != AF_UNIX) {
|
||
|
+ return -EAFNOSUPPORT;
|
||
|
+ }
|
||
|
+ if (socketpair(domain, type, protocol, fds) != 0) {
|
||
|
+ return -errno;
|
||
|
+ }
|
||
|
+ close(fds[1]);
|
||
|
+ return fds[0];
|
||
|
+ }
|
||
|
+
|
||
|
+ static intptr_t FakeSocketTrap(ArgsRef aArgs, void* aux) {
|
||
|
+ return FakeSocketTrapCommon(static_cast<int>(aArgs.args[0]),
|
||
|
+ static_cast<int>(aArgs.args[1]),
|
||
|
+ static_cast<int>(aArgs.args[2]));
|
||
|
+ }
|
||
|
+
|
||
|
+ static intptr_t FakeSocketTrapLegacy(ArgsRef aArgs, void* aux) {
|
||
|
+ const auto innerArgs = reinterpret_cast<unsigned long*>(aArgs.args[1]);
|
||
|
+
|
||
|
+ return FakeSocketTrapCommon(static_cast<int>(innerArgs[0]),
|
||
|
+ static_cast<int>(innerArgs[1]),
|
||
|
+ static_cast<int>(innerArgs[2]));
|
||
|
+ }
|
||
|
+
|
||
|
+ static Maybe<int> DoGetSockOpt(int fd, int optname) {
|
||
|
+ int optval;
|
||
|
+ socklen_t optlen = sizeof(optval);
|
||
|
+
|
||
|
+ if (getsockopt(fd, SOL_SOCKET, optname, &optval, &optlen) != 0) {
|
||
|
+ return Nothing();
|
||
|
+ }
|
||
|
+ MOZ_RELEASE_ASSERT(static_cast<size_t>(optlen) == sizeof(optval));
|
||
|
+ return Some(optval);
|
||
|
+ }
|
||
|
+
|
||
|
+ // Substitute the newly connected socket from the broker for the
|
||
|
+ // original socket. This is meant to be used on a fd from
|
||
|
+ // FakeSocketTrap, above, but it should also work to simulate
|
||
|
+ // re-connect()ing a real connected socket.
|
||
|
+ //
|
||
|
+ // Warning: This isn't quite right if the socket is dup()ed, because
|
||
|
+ // other duplicates will still be the original socket, but hopefully
|
||
|
+ // nothing we're dealing with does that.
|
||
|
+ static intptr_t ConnectTrapCommon(SandboxBrokerClient* aBroker, int aFd,
|
||
|
+ const struct sockaddr_un* aAddr,
|
||
|
+ socklen_t aLen) {
|
||
|
+ if (aFd < 0) {
|
||
|
+ return -EBADF;
|
||
|
+ }
|
||
|
+ const auto maybeDomain = DoGetSockOpt(aFd, SO_DOMAIN);
|
||
|
+ if (!maybeDomain) {
|
||
|
+ return -errno;
|
||
|
+ }
|
||
|
+ if (*maybeDomain != AF_UNIX) {
|
||
|
+ return -EAFNOSUPPORT;
|
||
|
+ }
|
||
|
+ const auto maybeType = DoGetSockOpt(aFd, SO_TYPE);
|
||
|
+ if (!maybeType) {
|
||
|
+ return -errno;
|
||
|
+ }
|
||
|
+ const int oldFlags = fcntl(aFd, F_GETFL);
|
||
|
+ if (oldFlags == -1) {
|
||
|
+ return -errno;
|
||
|
+ }
|
||
|
+ const int newFd = aBroker->Connect(aAddr, aLen, *maybeType);
|
||
|
+ if (newFd < 0) {
|
||
|
+ return newFd;
|
||
|
+ }
|
||
|
+ // Copy over the nonblocking flag. The connect() won't be
|
||
|
+ // nonblocking in that case, but that shouldn't matter for
|
||
|
+ // AF_UNIX. The other fcntl-settable flags are either irrelevant
|
||
|
+ // for sockets (e.g., O_APPEND) or would be blocked by this
|
||
|
+ // seccomp-bpf policy, so they're ignored.
|
||
|
+ if (fcntl(newFd, F_SETFL, oldFlags & O_NONBLOCK) != 0) {
|
||
|
+ close(newFd);
|
||
|
+ return -errno;
|
||
|
+ }
|
||
|
+ if (dup2(newFd, aFd) < 0) {
|
||
|
+ close(newFd);
|
||
|
+ return -errno;
|
||
|
+ }
|
||
|
+ close(newFd);
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ static intptr_t ConnectTrap(ArgsRef aArgs, void* aux) {
|
||
|
+ typedef const struct sockaddr_un* AddrPtr;
|
||
|
+
|
||
|
+ return ConnectTrapCommon(static_cast<SandboxBrokerClient*>(aux),
|
||
|
+ static_cast<int>(aArgs.args[0]),
|
||
|
+ reinterpret_cast<AddrPtr>(aArgs.args[1]),
|
||
|
+ static_cast<socklen_t>(aArgs.args[2]));
|
||
|
+ }
|
||
|
+
|
||
|
+ static intptr_t ConnectTrapLegacy(ArgsRef aArgs, void* aux) {
|
||
|
+ const auto innerArgs = reinterpret_cast<unsigned long*>(aArgs.args[1]);
|
||
|
+ typedef const struct sockaddr_un* AddrPtr;
|
||
|
+
|
||
|
+ return ConnectTrapCommon(static_cast<SandboxBrokerClient*>(aux),
|
||
|
+ static_cast<int>(innerArgs[0]),
|
||
|
+ reinterpret_cast<AddrPtr>(innerArgs[1]),
|
||
|
+ static_cast<socklen_t>(innerArgs[2]));
|
||
|
+ }
|
||
|
+
|
||
|
public:
|
||
|
ResultExpr InvalidSyscall() const override {
|
||
|
return Trap(BlockedSyscallTrap, nullptr);
|
||
|
}
|
||
|
|
||
|
@@ -630,15 +745,37 @@
|
||
|
return Some(Allow());
|
||
|
}
|
||
|
Arg<int> level(1), optname(2);
|
||
|
// SO_SNDBUF is used by IPC to avoid constructing
|
||
|
// unnecessarily large gather arrays for `sendmsg`.
|
||
|
- return Some(
|
||
|
- If(AllOf(level == SOL_SOCKET, optname == SO_SNDBUF), Allow())
|
||
|
- .Else(InvalidSyscall()));
|
||
|
+ //
|
||
|
+ // SO_DOMAIN and SO_TYPE are needed for connect() brokering,
|
||
|
+ // but they're harmless even when it's not enabled.
|
||
|
+ return Some(If(AllOf(level == SOL_SOCKET,
|
||
|
+ AnyOf(optname == SO_SNDBUF, optname == SO_DOMAIN,
|
||
|
+ optname == SO_TYPE)),
|
||
|
+ Allow())
|
||
|
+ .Else(InvalidSyscall()));
|
||
|
}
|
||
|
|
||
|
+ // These two cases are for connect() brokering, if enabled.
|
||
|
+ case SYS_SOCKET:
|
||
|
+ if (mBrokeredConnect) {
|
||
|
+ const auto trapFn = aHasArgs ? FakeSocketTrap : FakeSocketTrapLegacy;
|
||
|
+ MOZ_ASSERT(mBroker);
|
||
|
+ return Some(Trap(trapFn, mBroker));
|
||
|
+ }
|
||
|
+ return Nothing();
|
||
|
+
|
||
|
+ case SYS_CONNECT:
|
||
|
+ if (mBrokeredConnect) {
|
||
|
+ const auto trapFn = aHasArgs ? ConnectTrap : ConnectTrapLegacy;
|
||
|
+ MOZ_ASSERT(mBroker);
|
||
|
+ return Some(Trap(trapFn, mBroker));
|
||
|
+ }
|
||
|
+ return Nothing();
|
||
|
+
|
||
|
default:
|
||
|
return Nothing();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@@ -1006,10 +1143,16 @@
|
||
|
return If(AnyOf(request == TCGETS, request == TIOCGWINSZ),
|
||
|
Error(ENOTTY))
|
||
|
.Else(SandboxPolicyBase::EvaluateSyscall(sysno));
|
||
|
}
|
||
|
|
||
|
+ CASES_FOR_dup2: // See ConnectTrapCommon
|
||
|
+ if (mBrokeredConnect) {
|
||
|
+ return Allow();
|
||
|
+ }
|
||
|
+ return SandboxPolicyBase::EvaluateSyscall(sysno);
|
||
|
+
|
||
|
#ifdef MOZ_ASAN
|
||
|
// ASAN's error reporter wants to know if stderr is a tty.
|
||
|
case __NR_ioctl: {
|
||
|
Arg<int> fd(0);
|
||
|
return If(fd == STDERR_FILENO, Error(ENOTTY)).Else(InvalidSyscall());
|
||
|
@@ -1093,133 +1236,20 @@
|
||
|
|
||
|
close(fd);
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
- // This just needs to return something to stand in for the
|
||
|
- // unconnected socket until ConnectTrap, below, and keep track of
|
||
|
- // the socket type somehow. Half a socketpair *is* a socket, so it
|
||
|
- // should result in minimal confusion in the caller.
|
||
|
- static intptr_t FakeSocketTrapCommon(int domain, int type, int protocol) {
|
||
|
- int fds[2];
|
||
|
- // X11 client libs will still try to getaddrinfo() even for a
|
||
|
- // local connection. Also, WebRTC still has vestigial network
|
||
|
- // code trying to do things in the content process. Politely tell
|
||
|
- // them no.
|
||
|
- if (domain != AF_UNIX) {
|
||
|
- return -EAFNOSUPPORT;
|
||
|
- }
|
||
|
- if (socketpair(domain, type, protocol, fds) != 0) {
|
||
|
- return -errno;
|
||
|
- }
|
||
|
- close(fds[1]);
|
||
|
- return fds[0];
|
||
|
- }
|
||
|
-
|
||
|
- static intptr_t FakeSocketTrap(ArgsRef aArgs, void* aux) {
|
||
|
- return FakeSocketTrapCommon(static_cast<int>(aArgs.args[0]),
|
||
|
- static_cast<int>(aArgs.args[1]),
|
||
|
- static_cast<int>(aArgs.args[2]));
|
||
|
- }
|
||
|
-
|
||
|
- static intptr_t FakeSocketTrapLegacy(ArgsRef aArgs, void* aux) {
|
||
|
- const auto innerArgs = reinterpret_cast<unsigned long*>(aArgs.args[1]);
|
||
|
-
|
||
|
- return FakeSocketTrapCommon(static_cast<int>(innerArgs[0]),
|
||
|
- static_cast<int>(innerArgs[1]),
|
||
|
- static_cast<int>(innerArgs[2]));
|
||
|
- }
|
||
|
-
|
||
|
- static Maybe<int> DoGetSockOpt(int fd, int optname) {
|
||
|
- int optval;
|
||
|
- socklen_t optlen = sizeof(optval);
|
||
|
-
|
||
|
- if (getsockopt(fd, SOL_SOCKET, optname, &optval, &optlen) != 0) {
|
||
|
- return Nothing();
|
||
|
- }
|
||
|
- MOZ_RELEASE_ASSERT(static_cast<size_t>(optlen) == sizeof(optval));
|
||
|
- return Some(optval);
|
||
|
- }
|
||
|
-
|
||
|
- // Substitute the newly connected socket from the broker for the
|
||
|
- // original socket. This is meant to be used on a fd from
|
||
|
- // FakeSocketTrap, above, but it should also work to simulate
|
||
|
- // re-connect()ing a real connected socket.
|
||
|
- //
|
||
|
- // Warning: This isn't quite right if the socket is dup()ed, because
|
||
|
- // other duplicates will still be the original socket, but hopefully
|
||
|
- // nothing we're dealing with does that.
|
||
|
- static intptr_t ConnectTrapCommon(SandboxBrokerClient* aBroker, int aFd,
|
||
|
- const struct sockaddr_un* aAddr,
|
||
|
- socklen_t aLen) {
|
||
|
- if (aFd < 0) {
|
||
|
- return -EBADF;
|
||
|
- }
|
||
|
- const auto maybeDomain = DoGetSockOpt(aFd, SO_DOMAIN);
|
||
|
- if (!maybeDomain) {
|
||
|
- return -errno;
|
||
|
- }
|
||
|
- if (*maybeDomain != AF_UNIX) {
|
||
|
- return -EAFNOSUPPORT;
|
||
|
- }
|
||
|
- const auto maybeType = DoGetSockOpt(aFd, SO_TYPE);
|
||
|
- if (!maybeType) {
|
||
|
- return -errno;
|
||
|
- }
|
||
|
- const int oldFlags = fcntl(aFd, F_GETFL);
|
||
|
- if (oldFlags == -1) {
|
||
|
- return -errno;
|
||
|
- }
|
||
|
- const int newFd = aBroker->Connect(aAddr, aLen, *maybeType);
|
||
|
- if (newFd < 0) {
|
||
|
- return newFd;
|
||
|
- }
|
||
|
- // Copy over the nonblocking flag. The connect() won't be
|
||
|
- // nonblocking in that case, but that shouldn't matter for
|
||
|
- // AF_UNIX. The other fcntl-settable flags are either irrelevant
|
||
|
- // for sockets (e.g., O_APPEND) or would be blocked by this
|
||
|
- // seccomp-bpf policy, so they're ignored.
|
||
|
- if (fcntl(newFd, F_SETFL, oldFlags & O_NONBLOCK) != 0) {
|
||
|
- close(newFd);
|
||
|
- return -errno;
|
||
|
- }
|
||
|
- if (dup2(newFd, aFd) < 0) {
|
||
|
- close(newFd);
|
||
|
- return -errno;
|
||
|
- }
|
||
|
- close(newFd);
|
||
|
- return 0;
|
||
|
- }
|
||
|
-
|
||
|
- static intptr_t ConnectTrap(ArgsRef aArgs, void* aux) {
|
||
|
- typedef const struct sockaddr_un* AddrPtr;
|
||
|
-
|
||
|
- return ConnectTrapCommon(static_cast<SandboxBrokerClient*>(aux),
|
||
|
- static_cast<int>(aArgs.args[0]),
|
||
|
- reinterpret_cast<AddrPtr>(aArgs.args[1]),
|
||
|
- static_cast<socklen_t>(aArgs.args[2]));
|
||
|
- }
|
||
|
-
|
||
|
- static intptr_t ConnectTrapLegacy(ArgsRef aArgs, void* aux) {
|
||
|
- const auto innerArgs = reinterpret_cast<unsigned long*>(aArgs.args[1]);
|
||
|
- typedef const struct sockaddr_un* AddrPtr;
|
||
|
-
|
||
|
- return ConnectTrapCommon(static_cast<SandboxBrokerClient*>(aux),
|
||
|
- static_cast<int>(innerArgs[0]),
|
||
|
- reinterpret_cast<AddrPtr>(innerArgs[1]),
|
||
|
- static_cast<socklen_t>(innerArgs[2]));
|
||
|
- }
|
||
|
-
|
||
|
public:
|
||
|
ContentSandboxPolicy(SandboxBrokerClient* aBroker,
|
||
|
ContentProcessSandboxParams&& aParams)
|
||
|
: mParams(std::move(aParams)),
|
||
|
mAllowSysV(PR_GetEnv("MOZ_SANDBOX_ALLOW_SYSV") != nullptr),
|
||
|
mUsingRenderDoc(PR_GetEnv("RENDERDOC_CAPTUREOPTS") != nullptr) {
|
||
|
mBroker = aBroker;
|
||
|
mMayCreateShmem = true;
|
||
|
mAllowUnsafeSocketPair = true;
|
||
|
+ mBrokeredConnect = true;
|
||
|
}
|
||
|
|
||
|
~ContentSandboxPolicy() override = default;
|
||
|
|
||
|
Maybe<ResultExpr> EvaluateSocketCall(int aCall,
|
||
|
@@ -1232,18 +1262,16 @@
|
||
|
|
||
|
#ifdef ANDROID
|
||
|
case SYS_SOCKET:
|
||
|
return Some(Error(EACCES));
|
||
|
#else // #ifdef DESKTOP
|
||
|
- case SYS_SOCKET: {
|
||
|
- const auto trapFn = aHasArgs ? FakeSocketTrap : FakeSocketTrapLegacy;
|
||
|
- return Some(AllowBelowLevel(4, Trap(trapFn, nullptr)));
|
||
|
- }
|
||
|
- case SYS_CONNECT: {
|
||
|
- const auto trapFn = aHasArgs ? ConnectTrap : ConnectTrapLegacy;
|
||
|
- return Some(AllowBelowLevel(4, Trap(trapFn, mBroker)));
|
||
|
- }
|
||
|
+ case SYS_SOCKET:
|
||
|
+ case SYS_CONNECT:
|
||
|
+ if (BelowLevel(4)) {
|
||
|
+ return Some(Allow());
|
||
|
+ }
|
||
|
+ return SandboxPolicyCommon::EvaluateSocketCall(aCall, aHasArgs);
|
||
|
case SYS_RECV:
|
||
|
case SYS_SEND:
|
||
|
case SYS_GETSOCKOPT:
|
||
|
case SYS_SETSOCKOPT:
|
||
|
case SYS_GETSOCKNAME:
|
||
|
@@ -1458,13 +1486,10 @@
|
||
|
|
||
|
case __NR_getrusage:
|
||
|
case __NR_times:
|
||
|
return Allow();
|
||
|
|
||
|
- CASES_FOR_dup2: // See ConnectTrapCommon
|
||
|
- return Allow();
|
||
|
-
|
||
|
case __NR_fsync:
|
||
|
case __NR_msync:
|
||
|
return Allow();
|
||
|
|
||
|
case __NR_getpriority:
|
||
|
|