Commit 357c1755 authored by thomasanderson's avatar thomasanderson Committed by Commit bot

Namespace sandbox: add check for unprivileged use of CLONE_NEWUSER

Debian 8 restricts use of CLONE_NEWUSER to only processes with
CAP_SYS_ADMIN. (https://github.com/semplice/linux/blob/master/debian/patches/debian/add-sysctl-to-disallow-unprivileged-CLONE_NEWUSER-by-default.patch)
Chrome was previously checking if the kernel supported CLONE_NEWUSER
by running clone(CLONE_NEWUSER, ...) with the same capabilities chrome
was launched with.  This leads to 2 scenarios:

1. If Chrome was run as root:
   The check for CLONE_NEWUSER will succeed.  Chrome will then set up
   the namespace sandbox by clone()'ing and dropping CAP_SYS_ADMIN.
   Subsequent clone()'s with CLONE_NEWUSER will then fail.

2. If Chrome was run as a normal user:
   The check for CLONE_NEWUSER will fail.  Chrome will fallback to
   using the setuid sandbox.

The solution is to simply drop CAP_SYS_ADMIN before the check.

In addition, this CL disallows running Chromium as root unless launched
with --no-sandbox.

BUG=638180

Review-Url: https://codereview.chromium.org/2578483002
Cr-Commit-Position: refs/heads/master@{#443062}
parent dc1892fc
......@@ -11455,7 +11455,7 @@ Tell us what happened exactly before you got the profile error message:
<ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> can not be run as root.
</message>
<message name="IDS_REFUSE_TO_RUN_AS_ROOT_2" desc="Detailed message in the error dialog when the user tries to start Chrome as root.">
Please start <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> as a normal user. To run as root, you must specify an alternate --user-data-dir for storage of profile information.
Please start <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> as a normal user. If you need to run as root for development, rerun with the --no-sandbox flag.
</message>
<if expr="is_win">
<message name="IDS_PROFILE_ON_NETWORK_WARNING" desc="Warning shown to the user when they are running Chrome on Windows with a profile located on a network share.">
......
......@@ -29,9 +29,9 @@
#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
#include "base/command_line.h"
#include "chrome/browser/ui/simple_message_box.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
#include "content/public/common/content_switches.h"
#include "ui/base/l10n/l10n_util.h"
#endif // defined(OS_LINUX) && !defined(OS_CHROMEOS)
......@@ -74,12 +74,12 @@ void ChromeBrowserMainExtraPartsViews::PreProfileInit() {
// On the Linux desktop, we want to prevent the user from logging in as root,
// so that we don't destroy the profile. Now that we have some minimal ui
// initialized, check to see if we're running as root and bail if we are.
if (getuid() != 0)
if (geteuid() != 0)
return;
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (command_line.HasSwitch(switches::kUserDataDir))
if (command_line.HasSwitch(switches::kNoSandbox))
return;
base::string16 title = l10n_util::GetStringFUTF16(
......
......@@ -150,6 +150,18 @@ int CapabilityToKernelValue(Credentials::Capability cap) {
return 0;
}
void SetGidAndUidMaps(gid_t gid, uid_t uid) {
if (NamespaceUtils::KernelSupportsDenySetgroups()) {
PCHECK(NamespaceUtils::DenySetgroups());
}
DCHECK(GetRESIds(NULL, NULL));
const char kGidMapFile[] = "/proc/self/gid_map";
const char kUidMapFile[] = "/proc/self/uid_map";
PCHECK(NamespaceUtils::WriteToIdMapFile(kGidMapFile, gid));
PCHECK(NamespaceUtils::WriteToIdMapFile(kUidMapFile, uid));
DCHECK(GetRESIds(NULL, NULL));
}
} // namespace.
// static
......@@ -253,8 +265,14 @@ bool Credentials::CanCreateProcessInNewUserNS() {
return false;
#endif
// This is roughly a fork().
const pid_t pid = sys_clone(CLONE_NEWUSER | SIGCHLD, 0, 0, 0, 0);
uid_t uid;
gid_t gid;
if (!GetRESIds(&uid, &gid)) {
return false;
}
const pid_t pid =
base::ForkWithFlags(CLONE_NEWUSER | SIGCHLD, nullptr, nullptr);
if (pid == -1) {
CheckCloneNewUserErrno(errno);
......@@ -262,20 +280,29 @@ bool Credentials::CanCreateProcessInNewUserNS() {
}
// The parent process could have had threads. In the child, these threads
// have disappeared. Make sure to not do anything in the child, as this is a
// fragile execution environment.
// have disappeared.
if (pid == 0) {
// unshare() requires the effective uid and gid to have a mapping in the
// parent namespace.
SetGidAndUidMaps(gid, uid);
// Make sure we drop CAP_SYS_ADMIN.
CHECK(sandbox::Credentials::DropAllCapabilities());
// Ensure we have unprivileged use of CLONE_NEWUSER. Debian
// Jessie explicitly forbids this case. See:
// add-sysctl-to-disallow-unprivileged-CLONE_NEWUSER-by-default.patch
PCHECK(0 == sys_unshare(CLONE_NEWUSER));
_exit(kExitSuccess);
}
// Always reap the child.
int status = -1;
PCHECK(HANDLE_EINTR(waitpid(pid, &status, 0)) == pid);
CHECK(WIFEXITED(status));
CHECK_EQ(kExitSuccess, WEXITSTATUS(status));
// clone(2) succeeded, we can use CLONE_NEWUSER.
return true;
// clone(2) succeeded. Now return true only if the system grants
// unprivileged use of CLONE_NEWUSER as well.
return WIFEXITED(status) && WEXITSTATUS(status) == kExitSuccess;
}
bool Credentials::MoveToNewUserNS() {
......@@ -296,18 +323,9 @@ bool Credentials::MoveToNewUserNS() {
return false;
}
if (NamespaceUtils::KernelSupportsDenySetgroups()) {
PCHECK(NamespaceUtils::DenySetgroups());
}
// The current {r,e,s}{u,g}id is now an overflow id (c.f.
// /proc/sys/kernel/overflowuid). Setup the uid and gid maps.
DCHECK(GetRESIds(NULL, NULL));
const char kGidMapFile[] = "/proc/self/gid_map";
const char kUidMapFile[] = "/proc/self/uid_map";
PCHECK(NamespaceUtils::WriteToIdMapFile(kGidMapFile, gid));
PCHECK(NamespaceUtils::WriteToIdMapFile(kUidMapFile, uid));
DCHECK(GetRESIds(NULL, NULL));
SetGidAndUidMaps(gid, uid);
return true;
}
......
......@@ -22,8 +22,11 @@
namespace {
bool IsFileSystemAccessDenied() {
base::ScopedFD root_dir(HANDLE_EINTR(open("/", O_RDONLY)));
return !root_dir.is_valid();
// We would rather check "/" instead of "/proc/self/exe" here, but
// that gives false positives when running as root. See
// https://codereview.chromium.org/2578483002/#msg3
base::ScopedFD proc_self_exe(HANDLE_EINTR(open("/proc/self/exe", O_RDONLY)));
return !proc_self_exe.is_valid();
}
int GetHelperApi(base::Environment* env) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment