Skip to content

Commit

Permalink
Deny setgroups if needed before writing to gid_map.
Browse files Browse the repository at this point in the history
On Linux 3.18.2 and later, in order to write to /proc/PID/gid_map,
either setgroups must be disabled in the target process's user
namespace, or the writer must have CAP_SETGID. This change disables
setgroups by writing to /proc/PID/setgroups.

Thanks neusepoff for catching this issue.

BUG=457362

Review URL: https://codereview.chromium.org/909333002

Cr-Commit-Position: refs/heads/master@{#315865}
  • Loading branch information
rickyz authored and Commit bot committed Feb 11, 2015
1 parent fec39c2 commit af7f464
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 6 deletions.
8 changes: 6 additions & 2 deletions sandbox/linux/services/credentials.cc
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,17 @@ 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";
CHECK(NamespaceUtils::WriteToIdMapFile(kGidMapFile, gid));
CHECK(NamespaceUtils::WriteToIdMapFile(kUidMapFile, uid));
PCHECK(NamespaceUtils::WriteToIdMapFile(kGidMapFile, gid));
PCHECK(NamespaceUtils::WriteToIdMapFile(kUidMapFile, uid));
DCHECK(GetRESIds(NULL, NULL));
return true;
}
Expand Down
14 changes: 11 additions & 3 deletions sandbox/linux/services/namespace_sandbox.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,26 @@ namespace {

class WriteUidGidMapDelegate : public base::LaunchOptions::PreExecDelegate {
public:
WriteUidGidMapDelegate() : uid_(getuid()), gid_(getgid()) {}
WriteUidGidMapDelegate()
: uid_(getuid()),
gid_(getgid()),
supports_deny_setgroups_(
NamespaceUtils::KernelSupportsDenySetgroups()) {}

~WriteUidGidMapDelegate() override {}

void RunAsyncSafe() override {
if (supports_deny_setgroups_) {
RAW_CHECK(NamespaceUtils::DenySetgroups());
}
RAW_CHECK(NamespaceUtils::WriteToIdMapFile("/proc/self/uid_map", uid_));
RAW_CHECK(NamespaceUtils::WriteToIdMapFile("/proc/self/gid_map", gid_));
}

private:
uid_t uid_;
gid_t gid_;
const uid_t uid_;
const gid_t gid_;
const bool supports_deny_setgroups_;
DISALLOW_COPY_AND_ASSIGN(WriteUidGidMapDelegate);
};

Expand Down
24 changes: 23 additions & 1 deletion sandbox/linux/services/namespace_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ namespace {
bool IsRunningOnValgrind() {
return RUNNING_ON_VALGRIND;
}

const char kProcSelfSetgroups[] = "/proc/self/setgroups";
} // namespace

// static
Expand All @@ -43,7 +45,7 @@ bool NamespaceUtils::WriteToIdMapFile(const char* map_file, generic_id_t id) {
const generic_id_t outside_id = id;

char mapping[64];
ssize_t len =
const ssize_t len =
base::strings::SafeSPrintf(mapping, "%d %d 1\n", inside_id, outside_id);
const ssize_t rc = HANDLE_EINTR(write(fd, mapping, len));
RAW_CHECK(IGNORE_EINTR(close(fd)) == 0);
Expand Down Expand Up @@ -92,4 +94,24 @@ bool NamespaceUtils::KernelSupportsUnprivilegedNamespace(int type) {
return base::PathExists(base::FilePath(path));
}

// static
bool NamespaceUtils::KernelSupportsDenySetgroups() {
return base::PathExists(base::FilePath(kProcSelfSetgroups));
}

// static
bool NamespaceUtils::DenySetgroups() {
// This function needs to be async-signal-safe.
int fd = HANDLE_EINTR(open(kProcSelfSetgroups, O_WRONLY));
if (fd == -1) {
return false;
}

static const char kDeny[] = "deny";
const ssize_t len = sizeof(kDeny) - 1;
const ssize_t rc = HANDLE_EINTR(write(fd, kDeny, len));
RAW_CHECK(IGNORE_EINTR(close(fd)) == 0);
return rc == len;
}

} // namespace sandbox
11 changes: 11 additions & 0 deletions sandbox/linux/services/namespace_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ class SANDBOX_EXPORT NamespaceUtils {
// not work from within a sandbox.
static bool KernelSupportsUnprivilegedNamespace(int type);

// Returns true if the kernel supports denying setgroups in a user namespace.
// On kernels where this is supported, DenySetgroups must be called before a
// gid mapping can be added.
static bool KernelSupportsDenySetgroups();

// Disables setgroups() within the current user namespace. On Linux 3.18.2 and
// later, this is required in order to write to /proc/self/gid_map without
// having CAP_SETGID. Callers can determine whether is this needed with
// KernelSupportsDenySetgroups. This function is async-signal-safe.
static bool DenySetgroups() WARN_UNUSED_RESULT;

private:
DISALLOW_IMPLICIT_CONSTRUCTORS(NamespaceUtils);
};
Expand Down
7 changes: 7 additions & 0 deletions sandbox/linux/services/namespace_utils_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,17 @@ SANDBOX_TEST(NamespaceUtils, WriteToIdMapFile) {
const uid_t uid = getuid();
const gid_t gid = getgid();

const bool supports_deny_setgroups =
NamespaceUtils::KernelSupportsDenySetgroups();

const pid_t pid =
base::ForkWithFlags(CLONE_NEWUSER | SIGCHLD, nullptr, nullptr);
ASSERT_NE(-1, pid);
if (pid == 0) {
if (supports_deny_setgroups) {
RAW_CHECK(NamespaceUtils::DenySetgroups());
}

RAW_CHECK(getuid() != uid);
RAW_CHECK(NamespaceUtils::WriteToIdMapFile("/proc/self/uid_map", uid));
RAW_CHECK(getuid() == uid);
Expand Down

0 comments on commit af7f464

Please sign in to comment.