| Index: sandbox/linux/services/credentials.cc
|
| diff --git a/sandbox/linux/services/credentials.cc b/sandbox/linux/services/credentials.cc
|
| index 291c2cad4450e9a473281255cde6b72009c3852f..ce5eeda78ed177002b4bb4083d452e4fa6edcf2d 100644
|
| --- a/sandbox/linux/services/credentials.cc
|
| +++ b/sandbox/linux/services/credentials.cc
|
| @@ -22,8 +22,11 @@
|
| #include "base/process/launch.h"
|
| #include "base/template_util.h"
|
| #include "base/third_party/valgrind/valgrind.h"
|
| +#include "sandbox/linux/services/namespace_utils.h"
|
| #include "sandbox/linux/services/syscall_wrappers.h"
|
|
|
| +namespace sandbox {
|
| +
|
| namespace {
|
|
|
| bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; }
|
| @@ -48,39 +51,6 @@ struct CapTextFreeDeleter {
|
| // Wrapper to manage the result from libcap2's cap_from_text().
|
| typedef scoped_ptr<char, CapTextFreeDeleter> ScopedCapText;
|
|
|
| -struct FILECloser {
|
| - inline void operator()(FILE* f) const {
|
| - DCHECK(f);
|
| - PCHECK(0 == fclose(f));
|
| - }
|
| -};
|
| -
|
| -// Don't use ScopedFILE in base since it doesn't check fclose().
|
| -// TODO(jln): fix base/.
|
| -typedef scoped_ptr<FILE, FILECloser> ScopedFILE;
|
| -
|
| -static_assert((base::is_same<uid_t, gid_t>::value),
|
| - "uid_t and gid_t should be the same type");
|
| -// generic_id_t can be used for either uid_t or gid_t.
|
| -typedef uid_t generic_id_t;
|
| -
|
| -// Write a uid or gid mapping from |id| to |id| in |map_file|.
|
| -bool WriteToIdMapFile(const char* map_file, generic_id_t id) {
|
| - ScopedFILE f(fopen(map_file, "w"));
|
| - PCHECK(f);
|
| - const uid_t inside_id = id;
|
| - const uid_t outside_id = id;
|
| - int num = fprintf(f.get(), "%d %d 1\n", inside_id, outside_id);
|
| - if (num < 0) return false;
|
| - // Manually call fflush() to catch permission failures.
|
| - int ret = fflush(f.get());
|
| - if (ret) {
|
| - VLOG(1) << "Could not write to id map file";
|
| - return false;
|
| - }
|
| - return true;
|
| -}
|
| -
|
| // Checks that the set of RES-uids and the set of RES-gids have
|
| // one element each and return that element in |resuid| and |resgid|
|
| // respectively. It's ok to pass NULL as one or both of the ids.
|
| @@ -97,6 +67,17 @@ bool GetRESIds(uid_t* resuid, gid_t* resgid) {
|
| return true;
|
| }
|
|
|
| +const int kExitSuccess = 0;
|
| +
|
| +int ChrootToSelfFdinfo(void*) {
|
| + RAW_CHECK(chroot("/proc/self/fdinfo/") == 0);
|
| +
|
| + // CWD is essentially an implicit file descriptor, so be careful to not
|
| + // leave it behind.
|
| + RAW_CHECK(chdir("/") == 0);
|
| + _exit(kExitSuccess);
|
| +}
|
| +
|
| // chroot() to an empty dir that is "safe". To be safe, it must not contain
|
| // any subdirectory (chroot-ing there would allow a chroot escape) and it must
|
| // be impossible to create an empty directory there.
|
| @@ -108,25 +89,32 @@ bool GetRESIds(uid_t* resuid, gid_t* resgid) {
|
| // 3. The process dies
|
| // After (3) happens, the directory is not available anymore in /proc.
|
| bool ChrootToSafeEmptyDir() {
|
| - // We do not use a thread because when we are in a PID namespace, we cannot
|
| - // easily get a handle to the /proc/tid directory for the thread (since /proc
|
| - // may not be aware of the PID namespace). With a process, we can just use
|
| - // /proc/self.
|
| - pid_t pid = base::ForkWithFlags(SIGCHLD | CLONE_FS, nullptr, nullptr);
|
| + // We need to chroot to a fdinfo that is unique to a process and have that
|
| + // process die.
|
| + // 1. We don't want to simply fork() because duplicating the page tables is
|
| + // slow with a big address space.
|
| + // 2. We do not use a regular thread (that would unshare CLONE_FILES) because
|
| + // when we are in a PID namespace, we cannot easily get a handle to the
|
| + // /proc/tid directory for the thread (since /proc may not be aware of the
|
| + // PID namespace). With a process, we can just use /proc/self.
|
| + pid_t pid = -1;
|
| + char stack_buf[PTHREAD_STACK_MIN];
|
| +#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \
|
| + defined(ARCH_CPU_MIPS64_FAMILY) || defined(ARCH_CPU_MIPS_FAMILY)
|
| + // The stack grows downward.
|
| + void* stack = stack_buf + sizeof(stack_buf);
|
| +#else
|
| +#error "Unsupported architecture"
|
| +#endif
|
| + pid = clone(ChrootToSelfFdinfo, stack,
|
| + CLONE_VM | CLONE_VFORK | CLONE_FS | SIGCHLD, nullptr, nullptr,
|
| + nullptr, nullptr);
|
| PCHECK(pid != -1);
|
| - if (pid == 0) {
|
| - RAW_CHECK(chroot("/proc/self/fdinfo/") == 0);
|
| -
|
| - // CWD is essentially an implicit file descriptor, so be careful to not
|
| - // leave it behind.
|
| - RAW_CHECK(chdir("/") == 0);
|
| - _exit(0);
|
| - }
|
|
|
| int status = -1;
|
| PCHECK(HANDLE_EINTR(waitpid(pid, &status, 0)) == pid);
|
|
|
| - return status == 0;
|
| + return kExitSuccess == status;
|
| }
|
|
|
| // CHECK() that an attempt to move to a new user namespace raised an expected
|
| @@ -141,8 +129,6 @@ void CheckCloneNewUserErrno(int error) {
|
|
|
| } // namespace.
|
|
|
| -namespace sandbox {
|
| -
|
| bool Credentials::DropAllCapabilities() {
|
| ScopedCap cap(cap_init());
|
| CHECK(cap);
|
| @@ -169,7 +155,7 @@ scoped_ptr<std::string> Credentials::GetCurrentCapString() {
|
| }
|
|
|
| // static
|
| -bool Credentials::SupportsNewUserNS() {
|
| +bool Credentials::CanCreateProcessInNewUserNS() {
|
| // Valgrind will let clone(2) pass-through, but doesn't support unshare(),
|
| // so always consider UserNS unsupported there.
|
| if (IsRunningOnValgrind()) {
|
| @@ -222,8 +208,8 @@ bool Credentials::MoveToNewUserNS() {
|
| DCHECK(GetRESIds(NULL, NULL));
|
| const char kGidMapFile[] = "/proc/self/gid_map";
|
| const char kUidMapFile[] = "/proc/self/uid_map";
|
| - CHECK(WriteToIdMapFile(kGidMapFile, gid));
|
| - CHECK(WriteToIdMapFile(kUidMapFile, uid));
|
| + CHECK(NamespaceUtils::WriteToIdMapFile(kGidMapFile, gid));
|
| + CHECK(NamespaceUtils::WriteToIdMapFile(kUidMapFile, uid));
|
| DCHECK(GetRESIds(NULL, NULL));
|
| return true;
|
| }
|
|
|