Index: sandbox/linux/services/credentials.cc |
diff --git a/sandbox/linux/services/credentials.cc b/sandbox/linux/services/credentials.cc |
index 4f041dc2887b681fb20538b07beeb52627af48a2..21214c3a2fe14c811ae9c6be410bcd3866480198 100644 |
--- a/sandbox/linux/services/credentials.cc |
+++ b/sandbox/linux/services/credentials.cc |
@@ -7,21 +7,28 @@ |
#include <dirent.h> |
#include <errno.h> |
#include <fcntl.h> |
+#include <signal.h> |
#include <stdio.h> |
#include <sys/capability.h> |
#include <sys/stat.h> |
+#include <sys/syscall.h> |
#include <sys/types.h> |
+#include <sys/wait.h> |
#include <unistd.h> |
#include "base/basictypes.h" |
#include "base/bind.h" |
#include "base/logging.h" |
+#include "base/posix/eintr_wrapper.h" |
#include "base/strings/string_number_conversions.h" |
#include "base/template_util.h" |
+#include "base/third_party/valgrind/valgrind.h" |
#include "base/threading/thread.h" |
namespace { |
+bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; } |
+ |
struct CapFreeDeleter { |
inline void operator()(cap_t cap) const { |
int ret = cap_free(cap); |
@@ -146,6 +153,16 @@ bool ChrootToSafeEmptyDir() { |
return is_chrooted; |
} |
+// CHECK() that an attempt to move to a new user namespace raised an expected |
+// errno. |
+void CheckCloneNewUserErrno(int error) { |
+ // EPERM can happen if already in a chroot. EUSERS if too many nested |
+ // namespaces are used. EINVAL for kernels that don't support the feature. |
+ // Valgrind will ENOSYS unshare(). |
+ PCHECK(error == EPERM || error == EUSERS || error == EINVAL || |
+ error == ENOSYS); |
+} |
+ |
} // namespace. |
namespace sandbox { |
@@ -231,6 +248,37 @@ scoped_ptr<std::string> Credentials::GetCurrentCapString() const { |
return scoped_ptr<std::string> (new std::string(cap_text.get())); |
} |
+// static |
+bool Credentials::SupportsNewUserNS() { |
+ // Valgrind will let clone(2) pass-through, but doesn't support unshare(), |
+ // so always consider UserNS unsupported there. |
+ if (IsRunningOnValgrind()) { |
+ return false; |
+ } |
+ |
+ // This is roughly a fork(). |
+ const pid_t pid = syscall(__NR_clone, CLONE_NEWUSER | SIGCHLD, 0, 0, 0); |
+ |
+ if (pid == -1) { |
+ CheckCloneNewUserErrno(errno); |
+ return false; |
+ } |
+ |
+ // 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. |
+ if (pid == 0) { |
+ _exit(0); |
+ } |
+ |
+ // Always reap the child. |
+ siginfo_t infop; |
+ PCHECK(0 == HANDLE_EINTR(waitid(P_PID, pid, &infop, WEXITED))); |
+ |
+ // clone(2) succeeded, we can use CLONE_NEWUSER. |
+ return true; |
+} |
+ |
bool Credentials::MoveToNewUserNS() { |
uid_t uid; |
gid_t gid; |
@@ -241,16 +289,14 @@ bool Credentials::MoveToNewUserNS() { |
return false; |
} |
int ret = unshare(CLONE_NEWUSER); |
- // EPERM can happen if already in a chroot. EUSERS if too many nested |
- // namespaces are used. EINVAL for kernels that don't support the feature. |
- // Valgrind will ENOSYS unshare(). |
- PCHECK(!ret || errno == EPERM || errno == EUSERS || errno == EINVAL || |
- errno == ENOSYS); |
if (ret) { |
+ const int unshare_errno = errno; |
VLOG(1) << "Looks like unprivileged CLONE_NEWUSER may not be available " |
<< "on this kernel."; |
+ CheckCloneNewUserErrno(unshare_errno); |
return false; |
} |
+ |
// 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)); |