diff --git a/pkgs/applications/networking/browsers/chromium/sandbox_userns_31.patch b/pkgs/applications/networking/browsers/chromium/sandbox_userns_31.patch new file mode 100644 index 000000000000..490c1a9cebe9 --- /dev/null +++ b/pkgs/applications/networking/browsers/chromium/sandbox_userns_31.patch @@ -0,0 +1,297 @@ +commit ff4e8b4af04c58fc4c58ee7ed108aefcdc26a960 +Author: aszlig +Date: Thu May 16 14:17:56 2013 +0200 + + zygote: Add support for user namespaces on Linux. + + The implementation is done by patching the Zygote host to execute the sandbox + binary with CLONE_NEWUSER and setting the uid and gid mapping so that the child + process is using uid 0 and gid 0 which map to the current user of the parent. + Afterwards, the sandbox will continue as if it was called as a setuid binary. + + In addition, this adds new_user_namespace as an option in process_util in order + to set the UID and GID mapping correctly. The reason for this is that just + passing CLONE_NEWUSER to clone_flags doesn't help in LaunchProcess(), because + without setting the mappings exec*() will clear the process's capability sets. + + If the kernel doesn't support unprivileged user namespaces and the sandbox + binary doesn't have the setuid flag, the Zygote main process will run without a + sandbox. This is to mimic the behaviour if no SUID sandbox binary path is set. + + Signed-off-by: aszlig + +diff --git a/base/process/launch.cc b/base/process/launch.cc +index 1329a5a..ec28fdf 100644 +--- a/base/process/launch.cc ++++ b/base/process/launch.cc +@@ -24,6 +24,7 @@ LaunchOptions::LaunchOptions() + new_process_group(false) + #if defined(OS_LINUX) + , clone_flags(0) ++ , new_user_namespace(false) + #endif // OS_LINUX + #if defined(OS_CHROMEOS) + , ctrl_terminal_fd(-1) +diff --git a/base/process/launch.h b/base/process/launch.h +index ac2df5e..34a3851 100644 +--- a/base/process/launch.h ++++ b/base/process/launch.h +@@ -100,6 +100,9 @@ struct BASE_EXPORT LaunchOptions { + #if defined(OS_LINUX) + // If non-zero, start the process using clone(), using flags as provided. + int clone_flags; ++ ++ // If true, start the process in a new user namespace. ++ bool new_user_namespace; + #endif // defined(OS_LINUX) + + #if defined(OS_CHROMEOS) +diff --git a/base/process/launch_posix.cc b/base/process/launch_posix.cc +index de6286d..9333494 100644 +--- a/base/process/launch_posix.cc ++++ b/base/process/launch_posix.cc +@@ -37,6 +37,13 @@ + #include "base/threading/platform_thread.h" + #include "base/threading/thread_restrictions.h" + ++#if defined(OS_LINUX) ++#include ++#if !defined(CLONE_NEWUSER) ++#define CLONE_NEWUSER 0x10000000 ++#endif ++#endif ++ + #if defined(OS_CHROMEOS) + #include + #endif +@@ -294,13 +301,23 @@ bool LaunchProcess(const std::vector& argv, + + pid_t pid; + #if defined(OS_LINUX) +- if (options.clone_flags) { ++ int map_pipe_fd[2]; ++ int flags = options.clone_flags; ++ ++ if (options.new_user_namespace) { ++ flags |= CLONE_NEWUSER; ++ if (pipe(map_pipe_fd) < 0) { ++ DPLOG(ERROR) << "user namespace pipe"; ++ return false; ++ } ++ } ++ ++ if (options.clone_flags || options.new_user_namespace) { + // Signal handling in this function assumes the creation of a new + // process, so we check that a thread is not being created by mistake + // and that signal handling follows the process-creation rules. +- RAW_CHECK( +- !(options.clone_flags & (CLONE_SIGHAND | CLONE_THREAD | CLONE_VM))); +- pid = syscall(__NR_clone, options.clone_flags, 0, 0, 0); ++ RAW_CHECK(!(flags & (CLONE_SIGHAND | CLONE_THREAD | CLONE_VM))); ++ pid = syscall(__NR_clone, flags, 0, 0, 0); + } else + #endif + { +@@ -318,6 +335,21 @@ bool LaunchProcess(const std::vector& argv, + } else if (pid == 0) { + // Child process + ++#if defined(OS_LINUX) ++ if (options.new_user_namespace) { ++ // Close the write end of the pipe so we get an EOF when the parent closes ++ // the FD. This is to avoid race conditions when the UID/GID mappings are ++ // written _after_ execvp(). ++ close(map_pipe_fd[1]); ++ ++ char dummy; ++ if (HANDLE_EINTR(read(map_pipe_fd[0], &dummy, 1)) != 0) { ++ RAW_LOG(ERROR, "Unexpected input in uid/gid mapping pipe."); ++ _exit(127); ++ } ++ } ++#endif ++ + // DANGER: fork() rule: in the child, if you don't end up doing exec*(), + // you call _exit() instead of exit(). This is because _exit() does not + // call any previously-registered (in the parent) exit handlers, which +@@ -433,6 +465,40 @@ bool LaunchProcess(const std::vector& argv, + _exit(127); + } else { + // Parent process ++#if defined(OS_LINUX) ++ if (options.new_user_namespace) { ++ // We need to write UID/GID mapping here to map the current user outside ++ // the namespace to the root user inside the namespace in order to ++ // correctly "fool" the child process. ++ char buf[256]; ++ int map_fd, map_len; ++ ++ snprintf(buf, sizeof(buf), "/proc/%d/uid_map", pid); ++ map_fd = open(buf, O_RDWR); ++ DPCHECK(map_fd >= 0); ++ snprintf(buf, sizeof(buf), "0 %d 1", geteuid()); ++ map_len = strlen(buf); ++ if (write(map_fd, buf, map_len) != map_len) { ++ RAW_LOG(WARNING, "Can't write to uid_map."); ++ } ++ close(map_fd); ++ ++ snprintf(buf, sizeof(buf), "/proc/%d/gid_map", pid); ++ map_fd = open(buf, O_RDWR); ++ DPCHECK(map_fd >= 0); ++ snprintf(buf, sizeof(buf), "0 %d 1", getegid()); ++ map_len = strlen(buf); ++ if (write(map_fd, buf, map_len) != map_len) { ++ RAW_LOG(WARNING, "Can't write to gid_map."); ++ } ++ close(map_fd); ++ ++ // Close the pipe on the parent, so the child can continue doing the ++ // execvp() call. ++ close(map_pipe_fd[1]); ++ } ++#endif ++ + if (options.wait) { + // While this isn't strictly disk IO, waiting for another process to + // finish is the sort of thing ThreadRestrictions is trying to prevent. +diff --git a/content/browser/zygote_host/zygote_host_impl_linux.cc b/content/browser/zygote_host/zygote_host_impl_linux.cc +index fea43b5..95cbe07 100644 +--- a/content/browser/zygote_host/zygote_host_impl_linux.cc ++++ b/content/browser/zygote_host/zygote_host_impl_linux.cc +@@ -121,25 +121,31 @@ void ZygoteHostImpl::Init(const std::string& sandbox_cmd) { + + sandbox_binary_ = sandbox_cmd.c_str(); + +- // A non empty sandbox_cmd means we want a SUID sandbox. +- using_suid_sandbox_ = !sandbox_cmd.empty(); ++ bool userns_sandbox = false; ++ const std::vector cmd_line_unwrapped(cmd_line.argv()); + +- if (using_suid_sandbox_) { ++ if (!sandbox_cmd.empty()) { + struct stat st; + if (stat(sandbox_binary_.c_str(), &st) != 0) { + LOG(FATAL) << "The SUID sandbox helper binary is missing: " + << sandbox_binary_ << " Aborting now."; + } + +- if (access(sandbox_binary_.c_str(), X_OK) == 0 && +- (st.st_uid == 0) && +- (st.st_mode & S_ISUID) && +- (st.st_mode & S_IXOTH)) { ++ if (access(sandbox_binary_.c_str(), X_OK) == 0) { ++ using_suid_sandbox_ = true; ++ + cmd_line.PrependWrapper(sandbox_binary_); + + scoped_ptr + sandbox_client(sandbox::SetuidSandboxClient::Create()); + sandbox_client->SetupLaunchEnvironment(); ++ ++ if (!((st.st_uid == 0) && ++ (st.st_mode & S_ISUID) && ++ (st.st_mode & S_IXOTH))) { ++ userns_sandbox = true; ++ sandbox_client->SetNoSuid(); ++ } + } else { + LOG(FATAL) << "The SUID sandbox helper binary was found, but is not " + "configured correctly. Rather than run without sandboxing " +@@ -163,7 +169,19 @@ void ZygoteHostImpl::Init(const std::string& sandbox_cmd) { + base::ProcessHandle process = -1; + base::LaunchOptions options; + options.fds_to_remap = &fds_to_map; ++ if (userns_sandbox) ++ options.new_user_namespace = true; + base::LaunchProcess(cmd_line.argv(), options, &process); ++ ++ if (process == -1 && userns_sandbox) { ++ LOG(ERROR) << "User namespace sandbox failed to start, running without " ++ << "sandbox! You need at least kernel 3.8.0 with CONFIG_USER_NS " ++ << "enabled in order to use the sandbox without setuid bit."; ++ using_suid_sandbox_ = false; ++ options.new_user_namespace = false; ++ base::LaunchProcess(cmd_line_unwrapped, options, &process); ++ } ++ + CHECK(process != -1) << "Failed to launch zygote process"; + + if (using_suid_sandbox_) { +diff --git a/content/zygote/zygote_main_linux.cc b/content/zygote/zygote_main_linux.cc +index 567b305..1089233 100644 +--- a/content/zygote/zygote_main_linux.cc ++++ b/content/zygote/zygote_main_linux.cc +@@ -426,6 +426,13 @@ static bool EnterSuidSandbox(LinuxSandbox* linux_sandbox, + *has_started_new_init = true; + } + ++ // Don't set non-dumpable, as it causes trouble when the host tries to find ++ // the zygote process (XXX: Not quite sure why this happens with user ++ // namespaces). Fortunately, we also have the seccomp filter sandbox which ++ // should disallow the use of ptrace. ++ if (setuid_sandbox->IsNoSuid()) ++ return true; ++ + #if !defined(OS_OPENBSD) + // Previously, we required that the binary be non-readable. This causes the + // kernel to mark the process as non-dumpable at startup. The thinking was +diff --git a/sandbox/linux/suid/client/setuid_sandbox_client.cc b/sandbox/linux/suid/client/setuid_sandbox_client.cc +index 34231d4..36e3201 100644 +--- a/sandbox/linux/suid/client/setuid_sandbox_client.cc ++++ b/sandbox/linux/suid/client/setuid_sandbox_client.cc +@@ -166,6 +166,10 @@ bool SetuidSandboxClient::IsInNewNETNamespace() const { + return env_->HasVar(kSandboxNETNSEnvironmentVarName); + } + ++bool SetuidSandboxClient::IsNoSuid() const { ++ return env_->HasVar(kSandboxNoSuidVarName); ++} ++ + bool SetuidSandboxClient::IsSandboxed() const { + return sandboxed_; + } +@@ -175,5 +179,9 @@ void SetuidSandboxClient::SetupLaunchEnvironment() { + SetSandboxAPIEnvironmentVariable(env_); + } + ++void SetuidSandboxClient::SetNoSuid() { ++ env_->SetVar(kSandboxNoSuidVarName, "1"); ++} ++ + } // namespace sandbox + +diff --git a/sandbox/linux/suid/client/setuid_sandbox_client.h b/sandbox/linux/suid/client/setuid_sandbox_client.h +index a9f6536..2e8113a 100644 +--- a/sandbox/linux/suid/client/setuid_sandbox_client.h ++++ b/sandbox/linux/suid/client/setuid_sandbox_client.h +@@ -39,6 +39,8 @@ class SetuidSandboxClient { + bool IsInNewPIDNamespace() const; + // Did the setuid helper create a new network namespace ? + bool IsInNewNETNamespace() const; ++ // Is sandboxed without SUID binary ? ++ bool IsNoSuid() const; + // Are we done and fully sandboxed ? + bool IsSandboxed() const; + +@@ -46,6 +48,8 @@ class SetuidSandboxClient { + // helper. + void SetupLaunchEnvironment(); + ++ void SetNoSuid(); ++ + private: + // Holds the environment. Will never be NULL. + base::Environment* env_; +diff --git a/sandbox/linux/suid/common/sandbox.h b/sandbox/linux/suid/common/sandbox.h +index aad4ff8..bd710d5 100644 +--- a/sandbox/linux/suid/common/sandbox.h ++++ b/sandbox/linux/suid/common/sandbox.h +@@ -18,6 +18,7 @@ static const char kAdjustLowMemMarginSwitch[] = "--adjust-low-mem"; + + static const char kSandboxDescriptorEnvironmentVarName[] = "SBX_D"; + static const char kSandboxHelperPidEnvironmentVarName[] = "SBX_HELPER_PID"; ++static const char kSandboxNoSuidVarName[] = "SBX_NO_SUID"; + + static const long kSUIDSandboxApiNumber = 1; + static const char kSandboxEnvironmentApiRequest[] = "SBX_CHROME_API_RQ";