Bug 1302711 - Add Linux socket process sandbox level 2, for ioctl lockdown. r=gcp

See the previous commit, about content sandbox level 6, for details;
this is basically the same (except with one more ioctl allowlisted, and
controlled by a different pref).

This patch also adds some plumbing to get the socket sandbox level into
the code that constructs the policy, modeled on how it works for content
processes.  (Previously the only levels of the socket process sandbox
were "on" and "off" so that wasn't necessary until now.)

Differential Revision: https://phabricator.services.mozilla.com/D249018
This commit is contained in:
Jed Davis
2025-05-22 05:02:27 +00:00
committed by jedavis@mozilla.com
parent 3c314e648e
commit adb9ba0744
9 changed files with 78 additions and 26 deletions

View File

@@ -1590,15 +1590,16 @@ pref("browser.bookmarks.editDialog.maxRecentFolders", 7);
// exotic configurations we can't reasonably support out of the box.
//
pref("security.sandbox.content.level", 6);
// Introduced as part of bug 1608558. Linux is currently the only platform
// that uses a sandbox level for the socket process. There are currently
// only 2 levels:
// 0 -> "no sandbox"
// 1 -> "sandboxed, allows socket operations and reading necessary certs"
pref("security.sandbox.socket.process.level", 1);
pref("security.sandbox.content.write_path_whitelist", "");
pref("security.sandbox.content.read_path_whitelist", "");
pref("security.sandbox.content.syscall_whitelist", "");
// Introduced as part of bug 1608558. Linux is currently the only platform
// that uses a sandbox level for the socket process. The following levels
// are currently defined:
// 0 -> "no sandbox"
// 1 -> "sandboxed, allows socket operations and reading necessary certs"
// 2 -> default-deny for ioctl
pref("security.sandbox.socket.process.level", 2);
#endif
#if defined(XP_OPENBSD) && defined(MOZ_SANDBOX)

View File

@@ -346,12 +346,9 @@ mozilla::ipc::IPCResult SocketProcessChild::RecvSetConnectivity(
mozilla::ipc::IPCResult SocketProcessChild::RecvInitLinuxSandbox(
const Maybe<ipc::FileDescriptor>& aBrokerFd) {
#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
int fd = -1;
if (aBrokerFd.isSome()) {
fd = aBrokerFd.value().ClonePlatformHandle().release();
}
RegisterProfilerObserversForSandboxProfiler();
SetSocketProcessSandbox(fd);
SetSocketProcessSandbox(
SocketProcessSandboxParams::ForThisProcess(aBrokerFd));
#endif // XP_LINUX && MOZ_SANDBOX
return IPC_OK();
}

View File

@@ -207,6 +207,13 @@ int GetEffectiveSocketProcessSandboxLevel() {
int level =
StaticPrefs::security_sandbox_socket_process_level_DoNotUseDirectly();
#ifdef XP_LINUX
// Turn off ioctl lockdown in safe mode, until it's gotten more testing.
if (level > 1 && gSafeMode) {
level = 1;
}
#endif
return level;
}

View File

@@ -721,6 +721,13 @@ void RunTestsSocket(SandboxTestingChild* child) {
"are available");
return 0;
});
child->ErrnoValueTest("ioctl_dma_buf"_ns, ENOSYS, [] {
// Attempt an arbitrary non-tty ioctl, on the wrong type of fd; if
// allowed it would fail with ENOTTY (see the RDD tests) but in
// this sandbox it should be blocked (ENOSYS).
return ioctl(0, _IOW('b', 0, uint64_t), nullptr);
});
# endif // XP_LINUX
#elif XP_MACOSX
RunMacTestLaunchProcess(child);

View File

@@ -804,24 +804,24 @@ void SetRemoteDataDecoderSandbox(int aBroker) {
SetCurrentProcessSandbox(GetDecoderSandboxPolicy(sBroker));
}
void SetSocketProcessSandbox(int aBroker) {
void SetSocketProcessSandbox(SocketProcessSandboxParams&& aParams) {
if (!SandboxInfo::Get().Test(SandboxInfo::kHasSeccompBPF) ||
PR_GetEnv("MOZ_DISABLE_SOCKET_PROCESS_SANDBOX")) {
if (aBroker >= 0) {
close(aBroker);
}
return;
}
gSandboxReporterClient = new SandboxReporterClient(
SandboxReport::ProcType::SOCKET_PROCESS, TakeSandboxReporterFd());
// FIXME(bug 1513773): merge this with the ones for content and RDD?
static SandboxBrokerClient* sBroker;
if (aBroker >= 0) {
sBroker = new SandboxBrokerClient(aBroker);
MOZ_ASSERT(!sBroker); // This should only ever be called once.
if (aParams.mBroker) {
sBroker = new SandboxBrokerClient(aParams.mBroker.release());
}
SetCurrentProcessSandbox(GetSocketProcessSandboxPolicy(sBroker));
SetCurrentProcessSandbox(
GetSocketProcessSandboxPolicy(sBroker, std::move(aParams)));
}
void SetUtilitySandbox(int aBroker, ipc::SandboxingKind aKind) {

View File

@@ -54,6 +54,23 @@ struct ContentProcessSandboxParams {
const Maybe<ipc::FileDescriptor>& aBroker);
};
// Similarly to ContentProcessSandboxParams, a collection of
// parameters for the socket process. Currently this is just the
// level (and the broker), but in the future there could be more.
struct SocketProcessSandboxParams {
// Socket process sandbox level; see also GetEffectiveSandboxLevel
// and the comments for "security.sandbox.socket.process.level" in
// browser/app/profile/firefox.js
int mLevel = 0;
// The filesystem broker client fd; this *is* a RAII class so it
// needs to be `release()`d or moved to consume it.
mozilla::UniqueFileHandle mBroker;
static SocketProcessSandboxParams ForThisProcess(
const Maybe<ipc::FileDescriptor>& aBroker);
};
// Call only if SandboxInfo::CanSandboxContent() returns true.
// (No-op if the sandbox is disabled.)
// isFileProcess determines whether we allow system wide file reads.
@@ -66,7 +83,7 @@ MOZ_EXPORT void SetMediaPluginSandbox(const char* aFilePath);
MOZ_EXPORT void SetRemoteDataDecoderSandbox(int aBroker);
MOZ_EXPORT void SetSocketProcessSandbox(int aBroker);
MOZ_EXPORT void SetSocketProcessSandbox(SocketProcessSandboxParams&& aParams);
MOZ_EXPORT void SetUtilitySandbox(int aBroker, ipc::SandboxingKind aKind);

View File

@@ -2056,8 +2056,15 @@ UniquePtr<sandbox::bpf_dsl::Policy> GetDecoderSandboxPolicy(
// Basically a clone of RDDSandboxPolicy until we know exactly what
// the SocketProcess sandbox looks like.
class SocketProcessSandboxPolicy final : public SandboxPolicyCommon {
private:
SocketProcessSandboxParams mParams;
bool BelowLevel(int aLevel) const { return mParams.mLevel < aLevel; }
public:
explicit SocketProcessSandboxPolicy(SandboxBrokerClient* aBroker) {
explicit SocketProcessSandboxPolicy(SandboxBrokerClient* aBroker,
SocketProcessSandboxParams&& aParams)
: mParams(std::move(aParams)) {
mBroker = aBroker;
mMayCreateShmem = true;
}
@@ -2139,9 +2146,10 @@ class SocketProcessSandboxPolicy final : public SandboxPolicyCommon {
.ElseIf(request == FIONBIO, Allow())
// This is used by PR_Available in nsSocketInputStream::Available.
.ElseIf(request == FIONREAD, Allow())
// Allow anything that isn't a tty ioctl, for now; bug 1302711
// will cover changing this to a default-deny policy.
.ElseIf(shifted_type != kTtyIoctls, Allow())
// Allow anything that isn't a tty ioctl (if level < 2)
.ElseIf(
BelowLevel(2) ? shifted_type != kTtyIoctls : BoolConst(false),
Allow())
.Else(SandboxPolicyCommon::EvaluateSyscall(sysno));
}
@@ -2193,9 +2201,9 @@ class SocketProcessSandboxPolicy final : public SandboxPolicyCommon {
};
UniquePtr<sandbox::bpf_dsl::Policy> GetSocketProcessSandboxPolicy(
SandboxBrokerClient* aMaybeBroker) {
SandboxBrokerClient* aMaybeBroker, SocketProcessSandboxParams&& aParams) {
return UniquePtr<sandbox::bpf_dsl::Policy>(
new SocketProcessSandboxPolicy(aMaybeBroker));
new SocketProcessSandboxPolicy(aMaybeBroker, std::move(aParams)));
}
class UtilitySandboxPolicy : public SandboxPolicyCommon {

View File

@@ -22,6 +22,7 @@ namespace mozilla {
class SandboxBrokerClient;
struct ContentProcessSandboxParams;
struct SocketProcessSandboxParams;
UniquePtr<sandbox::bpf_dsl::Policy> GetContentSandboxPolicy(
SandboxBrokerClient* aMaybeBroker, ContentProcessSandboxParams&& aParams);
@@ -36,7 +37,7 @@ UniquePtr<sandbox::bpf_dsl::Policy> GetDecoderSandboxPolicy(
SandboxBrokerClient* aMaybeBroker);
UniquePtr<sandbox::bpf_dsl::Policy> GetSocketProcessSandboxPolicy(
SandboxBrokerClient* aMaybeBroker);
SandboxBrokerClient* aMaybeBroker, SocketProcessSandboxParams&& aParams);
UniquePtr<sandbox::bpf_dsl::Policy> GetUtilitySandboxPolicy(
SandboxBrokerClient* aMaybeBroker);

View File

@@ -47,4 +47,18 @@ ContentProcessSandboxParams::ForThisProcess(
return params;
}
/* static */ SocketProcessSandboxParams
SocketProcessSandboxParams::ForThisProcess(
const Maybe<ipc::FileDescriptor>& aBroker) {
SocketProcessSandboxParams self;
if (aBroker.isSome()) {
self.mBroker = aBroker->ClonePlatformHandle();
MOZ_RELEASE_ASSERT(self.mBroker);
}
self.mLevel = GetEffectiveSocketProcessSandboxLevel();
return self;
}
} // namespace mozilla