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; |
} |