void SandboxFork::StartChrootServer() { // Run the rest of this function in a separate process that can // chroot() on behalf of this process after it's sandboxed. pid_t pid = ForkWithFlags(CLONE_FS); if (pid < 0) { MOZ_CRASH("failed to clone chroot helper process"); } if (pid > 0) { return; } LinuxCapabilities caps; caps.Effective(CAP_SYS_CHROOT) = true; if (!caps.SetCurrent()) { SANDBOX_LOG_ERROR("capset (chroot helper): %s", strerror(errno)); MOZ_DIAGNOSTIC_ASSERT(false); } CloseSuperfluousFds(mChrootMap); char msg; ssize_t msgLen = HANDLE_EINTR(read(mChrootServer, &msg, 1)); if (msgLen == 0) { // Process exited before chrooting (or chose not to chroot?). _exit(0); } MOZ_RELEASE_ASSERT(msgLen == 1); MOZ_RELEASE_ASSERT(msg == kSandboxChrootRequest); // This chroots both processes to this process's procfs fdinfo // directory, which becomes empty and unlinked when this process // exits at the end of this function, and which is always // unwriteable. int rv = chroot("/proc/self/fdinfo"); MOZ_RELEASE_ASSERT(rv == 0); // Drop CAP_SYS_CHROOT ASAP. This must happen before responding; // the main child won't be able to waitpid(), so it could start // handling hostile content before this process finishes exiting. DropAllCaps(); // The working directory still grant access to the real filesystem; // remove that. (Note: if the process can obtain directory fds, for // example via SandboxBroker, it must be blocked from using fchdir.) rv = chdir("/"); MOZ_RELEASE_ASSERT(rv == 0); msg = kSandboxChrootResponse; msgLen = HANDLE_EINTR(write(mChrootServer, &msg, 1)); MOZ_RELEASE_ASSERT(msgLen == 1); _exit(0); }
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"); } }