diff options
-rw-r--r-- | chrome/app/generated_resources.grd | 21 | ||||
-rw-r--r-- | chrome/browser/browser_about_handler.cc | 67 | ||||
-rw-r--r-- | chrome/browser/zygote_host_linux.cc | 30 | ||||
-rw-r--r-- | chrome/browser/zygote_host_linux.h | 22 | ||||
-rw-r--r-- | chrome/browser/zygote_main_linux.cc | 29 | ||||
-rw-r--r-- | sandbox/linux/suid/sandbox.c | 15 |
6 files changed, 179 insertions, 5 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 08756be..716c289 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -8682,6 +8682,27 @@ Keep your key file in a safe place. You will need it to create new versions of y <p>But you can still configure via the command line. Please see <code>man <ph name="PRODUCT_BINARY_NAME">$2<ex>google-chrome</ex></ph></code> for more information on flags and environment variables.</p> </message> + <message name="IDS_ABOUT_SANDBOX_TITLE" desc="Title of HTML page which contains the status of the sandbox."> + Sandbox Status + </message> + <message name="IDS_ABOUT_SANDBOX_SUID_SANDBOX" desc="The name of a type of sandbox used by Chrome on UNIX like systems. The name 'SUID' stands for 'Set User ID', however it's a technical term and may be best left untranslated."> + SUID Sandbox + </message> + <message name="IDS_ABOUT_SANDBOX_PID_NAMESPACES" desc="This a technical term for an attribute of the SUID sandbox. PID stands for 'Process ID' but, as a technical term, may be best left untranslated. A namespace is another technical term which refers to set of names for objects which are disjoint from the members of all other namespaces."> + PID namespaces + </message> + <message name="IDS_ABOUT_SANDBOX_NET_NAMESPACES" desc="This a technical term for an attribute of the SUID sandbox. A namespace is a technical term which refers to set of names for objects which are disjoint from the members of all other namespaces."> + Network namespaces + </message> + <message name="IDS_ABOUT_SANDBOX_SECCOMP_SANDBOX" desc="The name of a type of sandbox used by Chrome on UNIX like systems. 'Seccomp' is a technical term which should be left untranslated."> + Seccomp sandbox + </message> + <message name="IDS_ABOUT_SANDBOX_OK" desc="A message telling the user that their sandbox is sufficient."> + You are adequately sandboxed. + </message> + <message name="IDS_ABOUT_SANDBOX_BAD" desc="A message telling the user that their sandbox is insufficient."> + You are not adequately sandboxed! + </message> </if> </messages> diff --git a/chrome/browser/browser_about_handler.cc b/chrome/browser/browser_about_handler.cc index 888f2914..62df49d 100644 --- a/chrome/browser/browser_about_handler.cc +++ b/chrome/browser/browser_about_handler.cc @@ -63,6 +63,8 @@ #include "chrome/browser/chromeos/version_loader.h" #elif defined(OS_MACOSX) #include "chrome/browser/cocoa/about_ipc_dialog.h" +#elif defined(OS_LINUX) +#include "chrome/browser/zygote_host_linux.h" #endif #if defined(USE_TCMALLOC) @@ -103,6 +105,7 @@ const char kPluginsPath[] = "plugins"; #if defined(OS_LINUX) const char kLinuxProxyConfigPath[] = "linux-proxy-config"; +const char kSandboxPath[] = "sandbox"; #endif #if defined(OS_CHROMEOS) @@ -128,6 +131,7 @@ const char *kAllAboutPaths[] = { kVersionPath, #if defined(OS_LINUX) kLinuxProxyConfigPath, + kSandboxPath, #endif #if defined(OS_CHROMEOS) kNetworkPath, @@ -527,6 +531,67 @@ std::string AboutLinuxProxyConfig() { data.append("</body></html>\n"); return data; } + +void AboutSandboxRow(std::string* data, const std::string& prefix, int name_id, + bool good) { + data->append("<tr><td>"); + data->append(prefix); + data->append(l10n_util::GetStringUTF8(name_id)); + if (good) { + data->append("</td><td style=\"color: green;\">"); + data->append( + l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL)); + } else { + data->append("</td><td style=\"color: red;\">"); + data->append( + l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL)); + } + data->append("</td></tr>"); +} + +std::string AboutSandbox() { + std::string data; + data.append("<!DOCTYPE HTML>\n"); + data.append("<html><head><meta charset=\"utf-8\"><title>"); + data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE)); + data.append("</title>"); + data.append("</head><body>\n"); + data.append("<h1>"); + data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE)); + data.append("</h1>"); + + const int status = Singleton<ZygoteHost>()->sandbox_status(); + + data.append("<table>"); + + AboutSandboxRow(&data, "", IDS_ABOUT_SANDBOX_SUID_SANDBOX, + status & ZygoteHost::kSandboxSUID); + if (status & ZygoteHost::kSandboxPIDNS) { + AboutSandboxRow(&data, " ", IDS_ABOUT_SANDBOX_PID_NAMESPACES, + status & ZygoteHost::kSandboxPIDNS); + AboutSandboxRow(&data, " ", IDS_ABOUT_SANDBOX_NET_NAMESPACES, + status & ZygoteHost::kSandboxNetNS); + } + AboutSandboxRow(&data, "", IDS_ABOUT_SANDBOX_SECCOMP_SANDBOX, + status & ZygoteHost::kSandboxSeccomp); + + data.append("</table>"); + + bool good = ((status & ZygoteHost::kSandboxSUID) && + (status & ZygoteHost::kSandboxPIDNS)) || + (status & ZygoteHost::kSandboxSeccomp); + if (good) { + data.append("<p style=\"color: green\">"); + data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_OK)); + } else { + data.append("<p style=\"color: red\">"); + data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_BAD)); + } + data.append("</p>"); + + data.append("</body></html>\n"); + return data; +} #endif std::string AboutVersion(DictionaryValue* localized_strings) { @@ -816,6 +881,8 @@ void AboutSource::StartDataRequest(const std::string& path_raw, #if defined(OS_LINUX) } else if (path == kLinuxProxyConfigPath) { response = AboutLinuxProxyConfig(); + } else if (path == kSandboxPath) { + response = AboutSandbox(); #endif } else if (path == kSyncPath) { response = AboutSync(); diff --git a/chrome/browser/zygote_host_linux.cc b/chrome/browser/zygote_host_linux.cc index de8590d..9423598 100644 --- a/chrome/browser/zygote_host_linux.cc +++ b/chrome/browser/zygote_host_linux.cc @@ -50,7 +50,8 @@ static void SaveSUIDUnsafeEnvironmentVariables() { ZygoteHost::ZygoteHost() : pid_(-1), init_(false), - using_suid_sandbox_(false) { + using_suid_sandbox_(false), + have_read_sandbox_status_word_(false) { } ZygoteHost::~ZygoteHost() { @@ -188,6 +189,29 @@ void ZygoteHost::Init(const std::string& sandbox_cmd) { close(fds[1]); control_fd_ = fds[0]; + + Pickle pickle; + pickle.WriteInt(kCmdGetSandboxStatus); + std::vector<int> empty_fds; + if (!base::SendMsg(control_fd_, pickle.data(), pickle.size(), empty_fds)) + LOG(FATAL) << "Cannot communicate with zygote"; + // We don't wait for the reply. We'll read it in ReadReply. +} + +ssize_t ZygoteHost::ReadReply(void* buf, size_t buf_len) { + // At startup we send a kCmdGetSandboxStatus request to the zygote, but don't + // wait for the reply. Thus, the first time that we read from the zygote, we + // get the reply to that request. + if (!have_read_sandbox_status_word_) { + if (HANDLE_EINTR(read(control_fd_, &sandbox_status_, + sizeof(sandbox_status_))) != + sizeof(sandbox_status_)) { + return -1; + } + have_read_sandbox_status_word_ = true; + } + + return HANDLE_EINTR(read(control_fd_, buf, buf_len)); } pid_t ZygoteHost::ForkRenderer( @@ -217,7 +241,7 @@ pid_t ZygoteHost::ForkRenderer( if (!base::SendMsg(control_fd_, pickle.data(), pickle.size(), fds)) return base::kNullProcessHandle; - if (HANDLE_EINTR(read(control_fd_, &pid, sizeof(pid))) != sizeof(pid)) + if (ReadReply(&pid, sizeof(pid)) != sizeof(pid)) return base::kNullProcessHandle; } @@ -302,7 +326,7 @@ bool ZygoteHost::DidProcessCrash(base::ProcessHandle handle, if (HANDLE_EINTR(write(control_fd_, pickle.data(), pickle.size())) < 0) PLOG(ERROR) << "write"; - len = HANDLE_EINTR(read(control_fd_, buf, sizeof(buf))); + len = ReadReply(buf, sizeof(buf)); } if (len == -1) { diff --git a/chrome/browser/zygote_host_linux.h b/chrome/browser/zygote_host_linux.h index 580770a..545df8d 100644 --- a/chrome/browser/zygote_host_linux.h +++ b/chrome/browser/zygote_host_linux.h @@ -44,15 +44,35 @@ class ZygoteHost { kCmdFork = 0, // Fork off a new renderer. kCmdReap = 1, // Reap a renderer child. kCmdDidProcessCrash = 2, // Check if child process crashed. + kCmdGetSandboxStatus = 3, // Read a bitmask of kSandbox* + }; + + // These form a bitmask which describes the conditions of the sandbox that + // the zygote finds itself in. + enum { + kSandboxSUID = 1 << 0, // SUID sandbox active + kSandboxPIDNS = 1 << 1, // SUID sandbox is using the PID namespace + kSandboxNetNS = 1 << 2, // SUID sandbox is using the network namespace + kSandboxSeccomp = 1 << 3, // seccomp sandbox active. }; pid_t pid() const { return pid_; } + // Returns an int which is a bitmask of kSandbox* values. Only valid after + // the first render has been forked. + int sandbox_status() const { + if (have_read_sandbox_status_word_) + return sandbox_status_; + return 0; + } + private: friend struct DefaultSingletonTraits<ZygoteHost>; ZygoteHost(); ~ZygoteHost(); + ssize_t ReadReply(void* buf, size_t buflen); + int control_fd_; // the socket to the zygote // A lock protecting all communication with the zygote. This lock must be // acquired before sending a command and released after the result has been @@ -62,6 +82,8 @@ class ZygoteHost { bool init_; bool using_suid_sandbox_; std::string sandbox_binary_; + bool have_read_sandbox_status_word_; + int sandbox_status_; }; #endif // CHROME_BROWSER_ZYGOTE_HOST_LINUX_H_ diff --git a/chrome/browser/zygote_main_linux.cc b/chrome/browser/zygote_main_linux.cc index 1dbbbf5..5717390 100644 --- a/chrome/browser/zygote_main_linux.cc +++ b/chrome/browser/zygote_main_linux.cc @@ -93,6 +93,10 @@ static void SELinuxTransitionToTypeOrDie(const char* type) { // runs it. class Zygote { public: + explicit Zygote(int sandbox_flags) + : sandbox_flags_(sandbox_flags) { + } + bool ProcessRequests() { // A SOCK_SEQPACKET socket is installed in fd 3. We get commands from the // browser on it. @@ -166,6 +170,9 @@ class Zygote { break; HandleDidProcessCrash(fd, pickle, iter); return false; + case ZygoteHost::kCmdGetSandboxStatus: + HandleGetSandboxStatus(fd, pickle, iter); + return false; default: NOTREACHED(); break; @@ -351,11 +358,22 @@ class Zygote { return false; } + bool HandleGetSandboxStatus(int fd, const Pickle& pickle, void* iter) { + if (HANDLE_EINTR(write(fd, &sandbox_flags_, sizeof(sandbox_flags_)) != + sizeof(sandbox_flags_))) { + PLOG(ERROR) << "write"; + } + + return false; + } + // In the SUID sandbox, we try to use a new PID namespace. Thus the PIDs // fork() returns are not the real PIDs, so we need to map the Real PIDS // into the sandbox PID namespace. typedef base::hash_map<base::ProcessHandle, base::ProcessHandle> ProcessMap; ProcessMap real_pids_to_sandbox_pids; + + const int sandbox_flags_; }; // With SELinux we can carve out a precise sandbox, so we don't have to play @@ -655,6 +673,14 @@ bool ZygoteMain(const MainFunctionParams& params) { return false; } + int sandbox_flags = 0; + if (getenv("SBX_D")) + sandbox_flags |= ZygoteHost::kSandboxSUID; + if (getenv("SBX_PID_NS")) + sandbox_flags |= ZygoteHost::kSandboxPIDNS; + if (getenv("SBX_NET_NS")) + sandbox_flags |= ZygoteHost::kSandboxNetNS; + #if defined(SECCOMP_SANDBOX) // The seccomp sandbox will be turned on when the renderers start. But we can // already check if sufficient support is available so that we only need to @@ -670,11 +696,12 @@ bool ZygoteMain(const MainFunctionParams& params) { "sandboxing disabled."; } else { LOG(INFO) << "Enabling experimental Seccomp sandbox."; + sandbox_flags |= ZygoteHost::kSandboxSeccomp; } } #endif // SECCOMP_SANDBOX - Zygote zygote; + Zygote zygote(sandbox_flags); // This function call can return multiple times, once per fork(). return zygote.ProcessRequests(); } diff --git a/sandbox/linux/suid/sandbox.c b/sandbox/linux/suid/sandbox.c index be6176a..0c92ad2 100644 --- a/sandbox/linux/suid/sandbox.c +++ b/sandbox/linux/suid/sandbox.c @@ -282,8 +282,21 @@ static bool MoveToNewNamespaces() { if (pid > 0) _exit(0); - if (pid == 0) + if (pid == 0) { + if (kCloneExtraFlags[i] & CLONE_NEWPID) { + setenv("SBX_PID_NS", "", 1 /* overwrite */); + } else { + unsetenv("SBX_PID_NS"); + } + + if (kCloneExtraFlags[i] & CLONE_NEWPID) { + setenv("SBX_NET_NS", "", 1 /* overwrite */); + } else { + unsetenv("SBX_NET_NS"); + } + break; + } if (errno != EINVAL) { perror("Failed to move to new PID namespace"); |