void SandboxChroot::ThreadMain() { // First, drop everything that isn't CAP_SYS_CHROOT. (This code // assumes that this thread already has effective CAP_SYS_CHROOT, // because Prepare() checked for it before creating this thread.) LinuxCapabilities caps; caps.Effective(CAP_SYS_CHROOT) = true; if (!caps.SetCurrent()) { SANDBOX_LOG_ERROR("capset: %s", strerror(errno)); MOZ_CRASH("Can't limit chroot thread's capabilities"); } MOZ_ALWAYS_ZERO(pthread_mutex_lock(&mMutex)); MOZ_ASSERT(mCommand == NO_THREAD); mCommand = NO_COMMAND; MOZ_ALWAYS_ZERO(pthread_cond_signal(&mWakeup)); while (mCommand == NO_COMMAND) { MOZ_ALWAYS_ZERO(pthread_cond_wait(&mWakeup, &mMutex)); } if (mCommand == DO_CHROOT) { MOZ_ASSERT(mFd >= 0); if (!ChrootToFileDesc(mFd)) { MOZ_CRASH("Failed to chroot"); } } else { MOZ_ASSERT(mCommand == JUST_EXIT); } if (mFd >= 0) { AlwaysClose(mFd); mFd = -1; } mCommand = NO_THREAD; MOZ_ALWAYS_ZERO(pthread_mutex_unlock(&mMutex)); // Drop the remaining capabilities; see note in SandboxChroot.h // about the potential unreliability of pthread_join. if (!LinuxCapabilities().SetCurrent()) { MOZ_CRASH("can't drop capabilities"); } }
void SandboxEarlyInit(GeckoProcessType aType, bool aIsNuwa) { // Bug 1168555: Nuwa isn't reliably single-threaded at this point; // it starts an IPC I/O thread and then shuts it down before calling // the plugin-container entry point, but that thread may not have // finished exiting. If/when any type of sandboxing is used for the // Nuwa process (e.g., unsharing the network namespace there instead // of for each content process, to save memory), this will need to be // changed by moving the SandboxEarlyInit call to an earlier point. if (aIsNuwa) { return; } MOZ_RELEASE_ASSERT(IsSingleThreaded()); const SandboxInfo info = SandboxInfo::Get(); // Which kinds of resource isolation (of those that need to be set // up at this point) can be used by this process? bool canChroot = false; bool canUnshareNet = false; bool canUnshareIPC = false; switch (aType) { case GeckoProcessType_Default: MOZ_ASSERT(false, "SandboxEarlyInit in parent process"); return; #ifdef MOZ_GMP_SANDBOX case GeckoProcessType_GMPlugin: if (!info.Test(SandboxInfo::kEnabledForMedia)) { break; } canUnshareNet = true; canUnshareIPC = true; // Need seccomp-bpf to intercept open(). canChroot = info.Test(SandboxInfo::kHasSeccompBPF); break; #endif // In the future, content processes will be able to use some of // these. default: // Other cases intentionally left blank. break; } // If there's nothing to do, then we're done. if (!canChroot && !canUnshareNet && !canUnshareIPC) { return; } { LinuxCapabilities existingCaps; if (existingCaps.GetCurrent() && existingCaps.AnyEffective()) { SANDBOX_LOG_ERROR("PLEASE DO NOT RUN THIS AS ROOT. Strange things may" " happen when capabilities are dropped."); } } // If capabilities can't be gained, then nothing can be done. if (!info.Test(SandboxInfo::kHasUserNamespaces)) { // Drop any existing capabilities; unsharing the user namespace // would implicitly drop them, so if we're running in a broken // configuration where that would matter (e.g., running as root // from a non-root-owned mode-0700 directory) this means it will // break the same way on all kernels and be easier to troubleshoot. LinuxCapabilities().SetCurrent(); return; } // The failure cases for the various unshares, and setting up the // chroot helper, don't strictly need to be fatal -- but they also // shouldn't fail on any reasonable system, so let's take the small // risk of breakage over the small risk of quietly providing less // security than we expect. (Unlike in SandboxInfo, this is in the // child process, so crashing here isn't as severe a response to the // unexpected.) if (!UnshareUserNamespace()) { SANDBOX_LOG_ERROR("unshare(CLONE_NEWUSER): %s", strerror(errno)); // If CanCreateUserNamespace (SandboxInfo.cpp) returns true, then // the unshare shouldn't have failed. MOZ_CRASH("unshare(CLONE_NEWUSER)"); } // No early returns after this point! We need to drop the // capabilities that were gained by unsharing the user namesapce. if (canUnshareIPC && syscall(__NR_unshare, CLONE_NEWIPC) != 0) { SANDBOX_LOG_ERROR("unshare(CLONE_NEWIPC): %s", strerror(errno)); MOZ_CRASH("unshare(CLONE_NEWIPC)"); } if (canUnshareNet && syscall(__NR_unshare, CLONE_NEWNET) != 0) { SANDBOX_LOG_ERROR("unshare(CLONE_NEWNET): %s", strerror(errno)); MOZ_CRASH("unshare(CLONE_NEWNET)"); } if (canChroot) { gChrootHelper = MakeUnique<SandboxChroot>(); if (!gChrootHelper->Prepare()) { SANDBOX_LOG_ERROR("failed to set up chroot helper"); MOZ_CRASH("SandboxChroot::Prepare"); } } if (!LinuxCapabilities().SetCurrent()) { SANDBOX_LOG_ERROR("dropping capabilities: %s", strerror(errno)); MOZ_CRASH("can't drop capabilities"); } }
void SandboxEarlyInit(GeckoProcessType aType) { const SandboxInfo info = SandboxInfo::Get(); if (info.Test(SandboxInfo::kUnexpectedThreads)) { return; } MOZ_RELEASE_ASSERT(IsSingleThreaded()); // Which kinds of resource isolation (of those that need to be set // up at this point) can be used by this process? bool canChroot = false; bool canUnshareNet = false; bool canUnshareIPC = false; switch (aType) { case GeckoProcessType_Default: MOZ_ASSERT(false, "SandboxEarlyInit in parent process"); return; #ifdef MOZ_GMP_SANDBOX case GeckoProcessType_GMPlugin: if (!info.Test(SandboxInfo::kEnabledForMedia)) { break; } canUnshareNet = true; canUnshareIPC = true; // Need seccomp-bpf to intercept open(). canChroot = info.Test(SandboxInfo::kHasSeccompBPF); break; #endif // In the future, content processes will be able to use some of // these. default: // Other cases intentionally left blank. break; } // If TSYNC is not supported, set up signal handler // used to enable seccomp on each thread. if (!info.Test(SandboxInfo::kHasSeccompTSync)) { gSeccompTsyncBroadcastSignum = FindFreeSignalNumber(); if (gSeccompTsyncBroadcastSignum == 0) { SANDBOX_LOG_ERROR("No available signal numbers!"); MOZ_CRASH(); } void (*oldHandler)(int); oldHandler = signal(gSeccompTsyncBroadcastSignum, SetThreadSandboxHandler); if (oldHandler != SIG_DFL) { // See the comment on FindFreeSignalNumber about race conditions. SANDBOX_LOG_ERROR("signal %d in use by handler %p!\n", gSeccompTsyncBroadcastSignum, oldHandler); MOZ_CRASH(); } } // If there's nothing to do, then we're done. if (!canChroot && !canUnshareNet && !canUnshareIPC) { return; } { LinuxCapabilities existingCaps; if (existingCaps.GetCurrent() && existingCaps.AnyEffective()) { SANDBOX_LOG_ERROR("PLEASE DO NOT RUN THIS AS ROOT. Strange things may" " happen when capabilities are dropped."); } } // If capabilities can't be gained, then nothing can be done. if (!info.Test(SandboxInfo::kHasUserNamespaces)) { // Drop any existing capabilities; unsharing the user namespace // would implicitly drop them, so if we're running in a broken // configuration where that would matter (e.g., running as root // from a non-root-owned mode-0700 directory) this means it will // break the same way on all kernels and be easier to troubleshoot. LinuxCapabilities().SetCurrent(); return; } // The failure cases for the various unshares, and setting up the // chroot helper, don't strictly need to be fatal -- but they also // shouldn't fail on any reasonable system, so let's take the small // risk of breakage over the small risk of quietly providing less // security than we expect. (Unlike in SandboxInfo, this is in the // child process, so crashing here isn't as severe a response to the // unexpected.) if (!UnshareUserNamespace()) { SANDBOX_LOG_ERROR("unshare(CLONE_NEWUSER): %s", strerror(errno)); // If CanCreateUserNamespace (SandboxInfo.cpp) returns true, then // the unshare shouldn't have failed. MOZ_CRASH("unshare(CLONE_NEWUSER)"); } // No early returns after this point! We need to drop the // capabilities that were gained by unsharing the user namesapce. if (canUnshareIPC && syscall(__NR_unshare, CLONE_NEWIPC) != 0) { SANDBOX_LOG_ERROR("unshare(CLONE_NEWIPC): %s", strerror(errno)); MOZ_CRASH("unshare(CLONE_NEWIPC)"); } if (canUnshareNet && syscall(__NR_unshare, CLONE_NEWNET) != 0) { SANDBOX_LOG_ERROR("unshare(CLONE_NEWNET): %s", strerror(errno)); MOZ_CRASH("unshare(CLONE_NEWNET)"); } if (canChroot) { gChrootHelper = MakeUnique<SandboxChroot>(); if (!gChrootHelper->Prepare()) { SANDBOX_LOG_ERROR("failed to set up chroot helper"); MOZ_CRASH("SandboxChroot::Prepare"); } } if (!LinuxCapabilities().SetCurrent()) { SANDBOX_LOG_ERROR("dropping capabilities: %s", strerror(errno)); MOZ_CRASH("can't drop capabilities"); } }