|
|
|
@ -340,495 +340,6 @@ public:
|
|
|
|
|
|
|
|
|
|
// The process-type-specific syscall rules start here:
|
|
|
|
|
|
|
|
|
|
#ifdef MOZ_CONTENT_SANDBOX |
|
|
|
|
// The seccomp-bpf filter for content processes is not a true sandbox
|
|
|
|
|
// on its own; its purpose is attack surface reduction and syscall
|
|
|
|
|
// interception in support of a semantic sandboxing layer. On B2G
|
|
|
|
|
// this is the Android process permission model; on desktop,
|
|
|
|
|
// namespaces and chroot() will be used.
|
|
|
|
|
class ContentSandboxPolicy : public SandboxPolicyCommon { |
|
|
|
|
SandboxBrokerClient* mBroker; |
|
|
|
|
|
|
|
|
|
// Trap handlers for filesystem brokering.
|
|
|
|
|
// (The amount of code duplication here could be improved....)
|
|
|
|
|
#ifdef __NR_open |
|
|
|
|
static intptr_t OpenTrap(ArgsRef aArgs, void* aux) { |
|
|
|
|
auto broker = static_cast<SandboxBrokerClient*>(aux); |
|
|
|
|
auto path = reinterpret_cast<const char*>(aArgs.args[0]); |
|
|
|
|
auto flags = static_cast<int>(aArgs.args[1]); |
|
|
|
|
return broker->Open(path, flags); |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
static intptr_t OpenAtTrap(ArgsRef aArgs, void* aux) { |
|
|
|
|
auto broker = static_cast<SandboxBrokerClient*>(aux); |
|
|
|
|
auto fd = static_cast<int>(aArgs.args[0]); |
|
|
|
|
auto path = reinterpret_cast<const char*>(aArgs.args[1]); |
|
|
|
|
auto flags = static_cast<int>(aArgs.args[2]); |
|
|
|
|
if (fd != AT_FDCWD && path[0] != '/') { |
|
|
|
|
SANDBOX_LOG_ERROR("unsupported fd-relative openat(%d, \"%s\", 0%o)", |
|
|
|
|
fd, path, flags); |
|
|
|
|
return BlockedSyscallTrap(aArgs, nullptr); |
|
|
|
|
} |
|
|
|
|
return broker->Open(path, flags); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#ifdef __NR_access |
|
|
|
|
static intptr_t AccessTrap(ArgsRef aArgs, void* aux) { |
|
|
|
|
auto broker = static_cast<SandboxBrokerClient*>(aux); |
|
|
|
|
auto path = reinterpret_cast<const char*>(aArgs.args[0]); |
|
|
|
|
auto mode = static_cast<int>(aArgs.args[1]); |
|
|
|
|
return broker->Access(path, mode); |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
static intptr_t AccessAtTrap(ArgsRef aArgs, void* aux) { |
|
|
|
|
auto broker = static_cast<SandboxBrokerClient*>(aux); |
|
|
|
|
auto fd = static_cast<int>(aArgs.args[0]); |
|
|
|
|
auto path = reinterpret_cast<const char*>(aArgs.args[1]); |
|
|
|
|
auto mode = static_cast<int>(aArgs.args[2]); |
|
|
|
|
// Linux's faccessat syscall has no "flags" argument. Attempting
|
|
|
|
|
// to handle the flags != 0 case is left to userspace; this is
|
|
|
|
|
// impossible to do correctly in all cases, but that's not our
|
|
|
|
|
// problem.
|
|
|
|
|
if (fd != AT_FDCWD && path[0] != '/') { |
|
|
|
|
SANDBOX_LOG_ERROR("unsupported fd-relative faccessat(%d, \"%s\", %d)", |
|
|
|
|
fd, path, mode); |
|
|
|
|
return BlockedSyscallTrap(aArgs, nullptr); |
|
|
|
|
} |
|
|
|
|
return broker->Access(path, mode); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static intptr_t StatTrap(ArgsRef aArgs, void* aux) { |
|
|
|
|
auto broker = static_cast<SandboxBrokerClient*>(aux); |
|
|
|
|
auto path = reinterpret_cast<const char*>(aArgs.args[0]); |
|
|
|
|
auto buf = reinterpret_cast<statstruct*>(aArgs.args[1]); |
|
|
|
|
return broker->Stat(path, buf); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static intptr_t LStatTrap(ArgsRef aArgs, void* aux) { |
|
|
|
|
auto broker = static_cast<SandboxBrokerClient*>(aux); |
|
|
|
|
auto path = reinterpret_cast<const char*>(aArgs.args[0]); |
|
|
|
|
auto buf = reinterpret_cast<statstruct*>(aArgs.args[1]); |
|
|
|
|
return broker->LStat(path, buf); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static intptr_t StatAtTrap(ArgsRef aArgs, void* aux) { |
|
|
|
|
auto broker = static_cast<SandboxBrokerClient*>(aux); |
|
|
|
|
auto fd = static_cast<int>(aArgs.args[0]); |
|
|
|
|
auto path = reinterpret_cast<const char*>(aArgs.args[1]); |
|
|
|
|
auto buf = reinterpret_cast<statstruct*>(aArgs.args[2]); |
|
|
|
|
auto flags = static_cast<int>(aArgs.args[3]); |
|
|
|
|
if (fd != AT_FDCWD && path[0] != '/') { |
|
|
|
|
SANDBOX_LOG_ERROR("unsupported fd-relative fstatat(%d, \"%s\", %p, %d)", |
|
|
|
|
fd, path, buf, flags); |
|
|
|
|
return BlockedSyscallTrap(aArgs, nullptr); |
|
|
|
|
} |
|
|
|
|
if ((flags & ~AT_SYMLINK_NOFOLLOW) != 0) { |
|
|
|
|
SANDBOX_LOG_ERROR("unsupported flags %d in fstatat(%d, \"%s\", %p, %d)", |
|
|
|
|
(flags & ~AT_SYMLINK_NOFOLLOW), fd, path, buf, flags); |
|
|
|
|
return BlockedSyscallTrap(aArgs, nullptr); |
|
|
|
|
} |
|
|
|
|
return (flags & AT_SYMLINK_NOFOLLOW) == 0 |
|
|
|
|
? broker->Stat(path, buf) |
|
|
|
|
: broker->LStat(path, buf); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static intptr_t ChmodTrap(ArgsRef aArgs, void* aux) { |
|
|
|
|
auto broker = static_cast<SandboxBrokerClient*>(aux); |
|
|
|
|
auto path = reinterpret_cast<const char*>(aArgs.args[0]); |
|
|
|
|
auto mode = static_cast<mode_t>(aArgs.args[1]); |
|
|
|
|
return broker->Chmod(path, mode); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static intptr_t LinkTrap(ArgsRef aArgs, void *aux) { |
|
|
|
|
auto broker = static_cast<SandboxBrokerClient*>(aux); |
|
|
|
|
auto path = reinterpret_cast<const char*>(aArgs.args[0]); |
|
|
|
|
auto path2 = reinterpret_cast<const char*>(aArgs.args[1]); |
|
|
|
|
return broker->Link(path, path2); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static intptr_t SymlinkTrap(ArgsRef aArgs, void *aux) { |
|
|
|
|
auto broker = static_cast<SandboxBrokerClient*>(aux); |
|
|
|
|
auto path = reinterpret_cast<const char*>(aArgs.args[0]); |
|
|
|
|
auto path2 = reinterpret_cast<const char*>(aArgs.args[1]); |
|
|
|
|
return broker->Symlink(path, path2); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static intptr_t RenameTrap(ArgsRef aArgs, void *aux) { |
|
|
|
|
auto broker = static_cast<SandboxBrokerClient*>(aux); |
|
|
|
|
auto path = reinterpret_cast<const char*>(aArgs.args[0]); |
|
|
|
|
auto path2 = reinterpret_cast<const char*>(aArgs.args[1]); |
|
|
|
|
return broker->Rename(path, path2); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static intptr_t MkdirTrap(ArgsRef aArgs, void* aux) { |
|
|
|
|
auto broker = static_cast<SandboxBrokerClient*>(aux); |
|
|
|
|
auto path = reinterpret_cast<const char*>(aArgs.args[0]); |
|
|
|
|
auto mode = static_cast<mode_t>(aArgs.args[1]); |
|
|
|
|
return broker->Mkdir(path, mode); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static intptr_t RmdirTrap(ArgsRef aArgs, void* aux) { |
|
|
|
|
auto broker = static_cast<SandboxBrokerClient*>(aux); |
|
|
|
|
auto path = reinterpret_cast<const char*>(aArgs.args[0]); |
|
|
|
|
return broker->Rmdir(path); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static intptr_t UnlinkTrap(ArgsRef aArgs, void* aux) { |
|
|
|
|
auto broker = static_cast<SandboxBrokerClient*>(aux); |
|
|
|
|
auto path = reinterpret_cast<const char*>(aArgs.args[0]); |
|
|
|
|
return broker->Unlink(path); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static intptr_t ReadlinkTrap(ArgsRef aArgs, void* aux) { |
|
|
|
|
auto broker = static_cast<SandboxBrokerClient*>(aux); |
|
|
|
|
auto path = reinterpret_cast<const char*>(aArgs.args[0]); |
|
|
|
|
auto buf = reinterpret_cast<char*>(aArgs.args[1]); |
|
|
|
|
auto size = static_cast<size_t>(aArgs.args[2]); |
|
|
|
|
return broker->Readlink(path, buf, size); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static intptr_t GetPPidTrap(ArgsRef aArgs, void* aux) { |
|
|
|
|
// 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
|
|
|
|
|
// pid namespace (Bug 1151624).
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
explicit ContentSandboxPolicy(SandboxBrokerClient* aBroker):mBroker(aBroker) { } |
|
|
|
|
virtual ~ContentSandboxPolicy() { } |
|
|
|
|
virtual ResultExpr PrctlPolicy() const override { |
|
|
|
|
// Ideally this should be restricted to a whitelist, but content
|
|
|
|
|
// uses enough things that it's not trivial to determine it.
|
|
|
|
|
return Allow(); |
|
|
|
|
} |
|
|
|
|
virtual Maybe<ResultExpr> EvaluateSocketCall(int aCall) const override { |
|
|
|
|
switch(aCall) { |
|
|
|
|
case SYS_RECVFROM: |
|
|
|
|
case SYS_SENDTO: |
|
|
|
|
return Some(Allow()); |
|
|
|
|
|
|
|
|
|
case SYS_SOCKETPAIR: { |
|
|
|
|
// See bug 1066750.
|
|
|
|
|
if (!kSocketCallHasArgs) { |
|
|
|
|
// 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(AllOf(domain == AF_UNIX, |
|
|
|
|
AnyOf(type == SOCK_STREAM, type == SOCK_SEQPACKET)), |
|
|
|
|
Allow()) |
|
|
|
|
.Else(InvalidSyscall())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#ifdef ANDROID |
|
|
|
|
case SYS_SOCKET: |
|
|
|
|
return Some(Error(EACCES)); |
|
|
|
|
#else // #ifdef DESKTOP
|
|
|
|
|
case SYS_RECV: |
|
|
|
|
case SYS_SEND: |
|
|
|
|
case SYS_SOCKET: // DANGEROUS
|
|
|
|
|
case SYS_CONNECT: // DANGEROUS
|
|
|
|
|
case SYS_ACCEPT: |
|
|
|
|
case SYS_ACCEPT4: |
|
|
|
|
case SYS_BIND: |
|
|
|
|
case SYS_LISTEN: |
|
|
|
|
case SYS_GETSOCKOPT: |
|
|
|
|
case SYS_SETSOCKOPT: |
|
|
|
|
case SYS_GETSOCKNAME: |
|
|
|
|
case SYS_GETPEERNAME: |
|
|
|
|
case SYS_SHUTDOWN: |
|
|
|
|
return Some(Allow()); |
|
|
|
|
#endif |
|
|
|
|
default: |
|
|
|
|
return SandboxPolicyCommon::EvaluateSocketCall(aCall); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#ifdef DESKTOP |
|
|
|
|
virtual Maybe<ResultExpr> EvaluateIpcCall(int aCall) const override { |
|
|
|
|
switch(aCall) { |
|
|
|
|
// These are a problem: SysV shared memory follows the Unix
|
|
|
|
|
// "same uid policy" and can't be restricted/brokered like file
|
|
|
|
|
// access. But the graphics layer might not be using them
|
|
|
|
|
// anymore; this needs to be studied.
|
|
|
|
|
case SHMGET: |
|
|
|
|
case SHMCTL: |
|
|
|
|
case SHMAT: |
|
|
|
|
case SHMDT: |
|
|
|
|
case SEMGET: |
|
|
|
|
case SEMCTL: |
|
|
|
|
case SEMOP: |
|
|
|
|
case MSGGET: |
|
|
|
|
return Some(Allow()); |
|
|
|
|
default: |
|
|
|
|
return SandboxPolicyCommon::EvaluateIpcCall(aCall); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
virtual ResultExpr EvaluateSyscall(int sysno) const override { |
|
|
|
|
if (mBroker) { |
|
|
|
|
// Have broker; route the appropriate syscalls to it.
|
|
|
|
|
switch (sysno) { |
|
|
|
|
case __NR_open: |
|
|
|
|
return Trap(OpenTrap, mBroker); |
|
|
|
|
case __NR_openat: |
|
|
|
|
return Trap(OpenAtTrap, mBroker); |
|
|
|
|
case __NR_access: |
|
|
|
|
return Trap(AccessTrap, mBroker); |
|
|
|
|
case __NR_faccessat: |
|
|
|
|
return Trap(AccessAtTrap, mBroker); |
|
|
|
|
CASES_FOR_stat: |
|
|
|
|
return Trap(StatTrap, mBroker); |
|
|
|
|
CASES_FOR_lstat: |
|
|
|
|
return Trap(LStatTrap, mBroker); |
|
|
|
|
CASES_FOR_fstatat: |
|
|
|
|
return Trap(StatAtTrap, mBroker); |
|
|
|
|
case __NR_chmod: |
|
|
|
|
return Trap(ChmodTrap, mBroker); |
|
|
|
|
case __NR_link: |
|
|
|
|
return Trap(LinkTrap, mBroker); |
|
|
|
|
case __NR_mkdir: |
|
|
|
|
return Trap(MkdirTrap, mBroker); |
|
|
|
|
case __NR_symlink: |
|
|
|
|
return Trap(SymlinkTrap, mBroker); |
|
|
|
|
case __NR_rename: |
|
|
|
|
return Trap(RenameTrap, mBroker); |
|
|
|
|
case __NR_rmdir: |
|
|
|
|
return Trap(RmdirTrap, mBroker); |
|
|
|
|
case __NR_unlink: |
|
|
|
|
return Trap(UnlinkTrap, mBroker); |
|
|
|
|
case __NR_readlink: |
|
|
|
|
return Trap(ReadlinkTrap, mBroker); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
// No broker; allow the syscalls directly. )-:
|
|
|
|
|
switch(sysno) { |
|
|
|
|
case __NR_open: |
|
|
|
|
case __NR_openat: |
|
|
|
|
case __NR_access: |
|
|
|
|
case __NR_faccessat: |
|
|
|
|
CASES_FOR_stat: |
|
|
|
|
CASES_FOR_lstat: |
|
|
|
|
CASES_FOR_fstatat: |
|
|
|
|
case __NR_chmod: |
|
|
|
|
case __NR_link: |
|
|
|
|
case __NR_mkdir: |
|
|
|
|
case __NR_symlink: |
|
|
|
|
case __NR_rename: |
|
|
|
|
case __NR_rmdir: |
|
|
|
|
case __NR_unlink: |
|
|
|
|
case __NR_readlink: |
|
|
|
|
return Allow(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
switch (sysno) { |
|
|
|
|
#ifdef DESKTOP |
|
|
|
|
case __NR_getppid: |
|
|
|
|
return Trap(GetPPidTrap, nullptr); |
|
|
|
|
|
|
|
|
|
// Filesystem syscalls that need more work to determine who's
|
|