From 0e161112cc06b7d77d8c9b73b334b209d79e12d1 Mon Sep 17 00:00:00 2001 From: "agl@chromium.org" Date: Fri, 10 Jul 2009 18:17:32 +0000 Subject: Linux: use a temp directory for the chroot. Ubuntu systems (at least) wipe /var/run at boot time, which is deleting our sandbox directory. Instead, we have the SUID helper create a temp directory in /tmp, unlink it and use that for the chroot directory. A file descriptor is passed to the zygote process for it to fchdir into. (Thanks to fta for discussions on this.) BUG=16363 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@20388 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/browser/zygote_host_linux.cc | 11 ++------- chrome/browser/zygote_main_linux.cc | 31 ++++++++++++++++++++---- sandbox/linux/suid/sandbox.cc | 47 ++++++++++++++++++++++++++++++++----- 3 files changed, 69 insertions(+), 20 deletions(-) diff --git a/chrome/browser/zygote_host_linux.cc b/chrome/browser/zygote_host_linux.cc index bfb9d7b..bc6eba1 100644 --- a/chrome/browser/zygote_host_linux.cc +++ b/chrome/browser/zygote_host_linux.cc @@ -47,24 +47,17 @@ ZygoteHost::ZygoteHost() { cmd_line.PrependWrapper(prefix); } - const std::string kSandboxPath = - WideToASCII(std::wstring(L"/var/run/") + - chrome::kBrowserProcessExecutableName + - L"-sandbox"); - struct stat st; if (stat(kSandboxBinary, &st) == 0) { if (access(kSandboxBinary, X_OK) == 0 && (st.st_mode & S_ISUID) && - (st.st_mode & S_IXOTH) && - access(kSandboxPath.c_str(), F_OK) == 0) { + (st.st_mode & S_IXOTH)) { cmd_line.PrependWrapper(ASCIIToWide(kSandboxBinary)); } else { LOG(FATAL) << "The SUID sandbox helper binary was found, but is not " "configured correctly. Rather than run without sandboxing " "I'm aborting now. You need to make sure that " - << kSandboxBinary << " is mode 4755 and that " - << kSandboxPath << " exists"; + << kSandboxBinary << " is mode 4755."; } } diff --git a/chrome/browser/zygote_main_linux.cc b/chrome/browser/zygote_main_linux.cc index c6854e9..9b8ef98 100644 --- a/chrome/browser/zygote_main_linux.cc +++ b/chrome/browser/zygote_main_linux.cc @@ -215,16 +215,35 @@ static bool MaybeEnterChroot() { static const char kChrootMe = 'C'; static const char kChrootMeSuccess = 'O'; - if (HANDLE_EINTR(write(fd, &kChrootMe, 1)) != 1) + if (HANDLE_EINTR(write(fd, &kChrootMe, 1)) != 1) { + LOG(ERROR) << "Failed to write to chroot pipe: " << errno; return false; + } char reply; - if (HANDLE_EINTR(read(fd, &reply, 1)) != 1) + std::vector fds; + if (!base::RecvMsg(fd, &reply, 1, &fds)) { + LOG(ERROR) << "Failed to read from chroot pipe: " << errno; + return false; + } + if (reply != kChrootMeSuccess) { + LOG(ERROR) << "Error code reply from chroot helper"; + for (size_t i = 0; i < fds.size(); ++i) + HANDLE_EINTR(close(fds[i])); return false; - if (reply != kChrootMeSuccess) + } + if (fds.size() != 1) { + LOG(ERROR) << "Bad number of file descriptors from chroot helper"; + for (size_t i = 0; i < fds.size(); ++i) + HANDLE_EINTR(close(fds[i])); return false; - if (chdir("/") == -1) + } + if (fchdir(fds[0]) == -1) { + LOG(ERROR) << "Failed to chdir to root directory: " << errno; + HANDLE_EINTR(close(fds[0])); return false; + } + HANDLE_EINTR(close(fds[0])); static const int kMagicSandboxIPCDescriptor = 5; SkiaFontConfigUseIPCImplementation(kMagicSandboxIPCDescriptor); @@ -243,8 +262,10 @@ static bool MaybeEnterChroot() { // exists at this point and we can set the non-dumpable flag which is // inherited by all our renderer children. prctl(PR_SET_DUMPABLE, 0, 0, 0, 0); - if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) + if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) { + LOG(ERROR) << "Failed to set non-dumpable flag"; return false; + } } else { SkiaFontConfigUseDirectImplementation(); } diff --git a/sandbox/linux/suid/sandbox.cc b/sandbox/linux/suid/sandbox.cc index 1ea2af3..abd066c 100644 --- a/sandbox/linux/suid/sandbox.cc +++ b/sandbox/linux/suid/sandbox.cc @@ -2,8 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// http://code.google.com/p/chromium/wiki/LinuxSUIDSandbox + #include #include +#include #include #include #include @@ -22,7 +25,6 @@ #define CLONE_NEWPID 0x20000000 #endif -static const char kSandboxPath[] = "/var/run/chrome-sandbox"; static const char kChromeBinary[] = "/opt/google/chrome/chrome"; static const char kSandboxDescriptorEnvironmentVarName[] = "SBX_D"; @@ -62,6 +64,23 @@ static int CloneChrootHelperProcess() { } if (pid == 0) { + // We create a temp directory for our chroot. Nobody should ever write into + // it, so it's root:root 0555. + char kTempDirectoryTemplate[] = "/tmp/chrome-sandbox-chroot-XXXXXX"; + const char* temp_dir = mkdtemp(kTempDirectoryTemplate); + if (!temp_dir) + FatalError("Failed to create temp directory for chroot"); + + const int chroot_dir_fd = open(temp_dir, O_DIRECTORY | O_RDONLY); + if (chroot_dir_fd < 0) { + rmdir(temp_dir); + FatalError("Failed to open chroot temp directory"); + } + + rmdir(temp_dir); + fchown(chroot_dir_fd, 0, 0); + fchmod(chroot_dir_fd, 0555); + // We share our files structure with an untrusted process. As a security in // depth measure, we make sure that we can't open anything by mistake. // TODO: drop CAP_SYS_RESOURCE @@ -87,25 +106,41 @@ static int CloneChrootHelperProcess() { if (msg != kMsgChrootMe) FatalError("Unknown message from sandboxed process"); - if (chdir(kSandboxPath)) - FatalError("Cannot chdir into %s", kSandboxPath); + if (fchdir(chroot_dir_fd)) + FatalError("Cannot chdir into chroot temp directory"); struct stat st; if (stat(".", &st)) FatalError("stat"); if (st.st_uid || st.st_gid || st.st_mode & S_IWOTH) - FatalError("Bad permissions on chroot directory (%s)", kSandboxPath); + FatalError("Bad permissions on chroot temp directory"); if (chroot(".")) - FatalError("Cannot chroot into %s", kSandboxPath); + FatalError("Cannot chroot into temp directory"); if (chdir("/")) FatalError("Cannot chdir to / after chroot"); const char reply = kMsgChrootSuccessful; do { - bytes = write(sv[0], &reply, 1); + struct msghdr msg = {0}; + struct iovec iov = {(char *) &reply, 1}; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + char control_buffer[CMSG_SPACE(sizeof(int))]; + msg.msg_control = control_buffer; + msg.msg_controllen = sizeof(control_buffer); + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cmsg), &chroot_dir_fd, sizeof(int)); + msg.msg_controllen = cmsg->cmsg_len; + + bytes = sendmsg(sv[0], &msg, 0); } while (bytes == -1 && errno == EINTR); if (bytes != 1) -- cgit v1.1