diff --git a/pkgs/applications/networking/browsers/chromium/default.nix b/pkgs/applications/networking/browsers/chromium/default.nix index b0dc8721747..b5557cc1200 100644 --- a/pkgs/applications/networking/browsers/chromium/default.nix +++ b/pkgs/applications/networking/browsers/chromium/default.nix @@ -84,6 +84,11 @@ let buildPath = "out/${buildType}"; libExecPath = "$out/libexec/${packageName}"; + # user namespace sandbox patch + userns_patch = if versionOlder sourceInfo.version "29.0.0.0" + then ./sandbox_userns.patch + else ./sandbox_userns_29.patch; + in stdenv.mkDerivation rec { name = "${packageName}-${version}"; inherit packageName; @@ -116,18 +121,14 @@ in stdenv.mkDerivation rec { prePatch = "patchShebangs ."; - patches = [ ./sandbox_userns.patch ] + patches = [ userns_patch ] ++ optional cupsSupport ./cups_allow_deprecated.patch; postPatch = '' sed -i -r -e 's/-f(stack-protector)(-all)?/-fno-\1/' build/common.gypi + sed -i -e 's|/usr/bin/gcc|gcc|' third_party/WebKit/Source/core/core.gypi '' + optionalString useOpenSSL '' cat $opensslPatches | patch -p1 -d third_party/openssl/openssl - '' + '' - sed -i -e 's|/usr/bin/gcc|gcc|' \ - third_party/WebKit/Source/${if !versionOlder sourceInfo.version "28.0.0.0" - then "core/core.gypi" - else "WebCore/WebCore.gyp/WebCore.gyp"} ''; gypFlags = mkGypFlags (gypFlagsUseSystemLibs // { diff --git a/pkgs/applications/networking/browsers/chromium/sandbox_userns_29.patch b/pkgs/applications/networking/browsers/chromium/sandbox_userns_29.patch new file mode 100644 index 00000000000..c1987c43e15 --- /dev/null +++ b/pkgs/applications/networking/browsers/chromium/sandbox_userns_29.patch @@ -0,0 +1,287 @@ +commit 557daf9cc9c02b8f17e6ee84f9b1ae6e6132d478 +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_util.h b/base/process_util.h +index 0bec8e0..6b6f566 100644 +--- a/base/process_util.h ++++ b/base/process_util.h +@@ -175,6 +175,7 @@ struct 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) +@@ -249,6 +250,9 @@ struct 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_util_posix.cc b/base/process_util_posix.cc +index 83afe44..e529b2b 100644 +--- a/base/process_util_posix.cc ++++ b/base/process_util_posix.cc +@@ -34,6 +34,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 +@@ -621,8 +628,19 @@ bool LaunchProcess(const std::vector& argv, + + pid_t pid; + #if defined(OS_LINUX) +- if (options.clone_flags) { +- pid = syscall(__NR_clone, options.clone_flags, 0, 0, 0); ++ 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) { ++ pid = syscall(__NR_clone, flags, 0, 0, 0); + } else + #endif + { +@@ -635,6 +653,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 +@@ -749,6 +782,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 130f44a..c1232d4 100644 +--- a/content/browser/zygote_host/zygote_host_impl_linux.cc ++++ b/content/browser/zygote_host/zygote_host_impl_linux.cc +@@ -118,25 +118,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 " +@@ -160,7 +166,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 7d01722..2f445ef 100644 +--- a/content/zygote/zygote_main_linux.cc ++++ b/content/zygote/zygote_main_linux.cc +@@ -395,6 +395,13 @@ static bool EnterSandbox(sandbox::SetuidSandboxClient* setuid_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"; diff --git a/pkgs/applications/networking/browsers/chromium/sources.nix b/pkgs/applications/networking/browsers/chromium/sources.nix index 780c3b62a30..dce02893a56 100644 --- a/pkgs/applications/networking/browsers/chromium/sources.nix +++ b/pkgs/applications/networking/browsers/chromium/sources.nix @@ -1,18 +1,18 @@ # This file is autogenerated from update.sh in the same directory. { dev = { - version = "29.0.1521.3"; - url = "http://commondatastorage.googleapis.com/chromium-browser-official/chromium-29.0.1521.3.tar.xz"; - sha256 = "0szc3g24jlhcp8cgijdv0q9rfn3mhp2kjyc85ml4snskkpasfrv3"; + version = "29.0.1541.2"; + url = "http://commondatastorage.googleapis.com/chromium-browser-official/chromium-29.0.1541.2.tar.xz"; + sha256 = "0i3vp2zrk1sjdhkwdhig08jh0qmzahn96pm0i22r63cp8i9vny1p"; }; beta = { - version = "28.0.1500.45"; - url = "http://commondatastorage.googleapis.com/chromium-browser-official/chromium-28.0.1500.45.tar.xz"; - sha256 = "01sxqv6i7m5h0jsypg801w2ivbrir37wdi4ijd5yvprkyzbd90zi"; + version = "28.0.1500.52"; + url = "http://commondatastorage.googleapis.com/chromium-browser-official/chromium-28.0.1500.52.tar.xz"; + sha256 = "1d0q8lsvwqkaninmnyc8jjj0pnqxc5rr3lr3mgzj37avksxvyg3v"; }; stable = { - version = "27.0.1453.110"; - url = "http://commondatastorage.googleapis.com/chromium-browser-official/chromium-27.0.1453.110.tar.xz"; - sha256 = "1y61shbzrkcv70x9zyj559g2yyp40hi9y59f7wwx5g076lsaxsw5"; + version = "28.0.1500.52"; + url = "http://commondatastorage.googleapis.com/chromium-browser-official/chromium-28.0.1500.52.tar.xz"; + sha256 = "1d0q8lsvwqkaninmnyc8jjj0pnqxc5rr3lr3mgzj37avksxvyg3v"; }; }