diff --git a/atom/app/atom_main_delegate.cc b/atom/app/atom_main_delegate.cc index 2d31b1c91f2e..42782366cca9 100644 --- a/atom/app/atom_main_delegate.cc +++ b/atom/app/atom_main_delegate.cc @@ -11,6 +11,7 @@ #include "atom/browser/atom_browser_client.h" #include "atom/browser/relauncher.h" #include "atom/common/google_api_key.h" +#include "atom/common/options_switches.h" #include "atom/renderer/atom_renderer_client.h" #include "atom/utility/atom_content_utility_client.h" #include "base/command_line.h" @@ -29,7 +30,7 @@ namespace { const char* kRelauncherProcess = "relauncher"; bool IsBrowserProcess(base::CommandLine* cmd) { - std::string process_type = cmd->GetSwitchValueASCII(switches::kProcessType); + std::string process_type = cmd->GetSwitchValueASCII(::switches::kProcessType); return process_type.empty(); } @@ -72,7 +73,7 @@ bool AtomMainDelegate::BasicStartupComplete(int* exit_code) { // Only enable logging when --enable-logging is specified. std::unique_ptr env(base::Environment::Create()); - if (!command_line->HasSwitch(switches::kEnableLogging) && + if (!command_line->HasSwitch(::switches::kEnableLogging) && !env->HasVar("ELECTRON_ENABLE_LOGGING")) { settings.logging_dest = logging::LOG_NONE; logging::SetMinLogLevel(logging::LOG_NUM_SEVERITIES); @@ -115,17 +116,17 @@ void AtomMainDelegate::PreSandboxStartup() { auto command_line = base::CommandLine::ForCurrentProcess(); std::string process_type = command_line->GetSwitchValueASCII( - switches::kProcessType); + ::switches::kProcessType); // Only append arguments for browser process. if (!IsBrowserProcess(command_line)) return; // Disable renderer sandbox for most of node's functions. - command_line->AppendSwitch(switches::kNoSandbox); + command_line->AppendSwitch(::switches::kNoSandbox); // Allow file:// URIs to read other file:// URIs by default. - command_line->AppendSwitch(switches::kAllowFileAccessFromFiles); + command_line->AppendSwitch(::switches::kAllowFileAccessFromFiles); #if defined(OS_MACOSX) // Enable AVFoundation. @@ -140,7 +141,13 @@ content::ContentBrowserClient* AtomMainDelegate::CreateContentBrowserClient() { content::ContentRendererClient* AtomMainDelegate::CreateContentRendererClient() { - renderer_client_.reset(new AtomRendererClient); + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableSandbox)) { + renderer_client_.reset(new content::ContentRendererClient); + } else { + renderer_client_.reset(new AtomRendererClient); + } + return renderer_client_.get(); } diff --git a/atom/browser/atom_browser_client.cc b/atom/browser/atom_browser_client.cc index edaf20dbd13e..832223b89c97 100644 --- a/atom/browser/atom_browser_client.cc +++ b/atom/browser/atom_browser_client.cc @@ -99,6 +99,44 @@ content::WebContents* AtomBrowserClient::GetWebContentsFromProcessID( return WebContentsPreferences::GetWebContentsFromProcessID(process_id); } +bool AtomBrowserClient::ShouldCreateNewSiteInstance( + content::BrowserContext* browser_context, + content::SiteInstance* current_instance, + const GURL& url) { + + if (url.SchemeIs(url::kJavaScriptScheme)) + // "javacript:" scheme should always use same SiteInstance + return false; + + if (!IsRendererSandboxed(current_instance->GetProcess()->GetID())) + // non-sandboxed renderers should always create a new SiteInstance + return true; + + // Create new a SiteInstance if navigating to a different site. + auto src_url = current_instance->GetSiteURL(); + return + !content::SiteInstance::IsSameWebSite(browser_context, src_url, url) && + // `IsSameWebSite` doesn't seem to work for some URIs such as `file:`, + // handle these scenarios by comparing only the site as defined by + // `GetSiteForURL`. + content::SiteInstance::GetSiteForURL(browser_context, url) != src_url; +} + +void AtomBrowserClient::AddSandboxedRendererId(int process_id) { + base::AutoLock auto_lock(sandboxed_renderers_lock_); + sandboxed_renderers_.insert(process_id); +} + +void AtomBrowserClient::RemoveSandboxedRendererId(int process_id) { + base::AutoLock auto_lock(sandboxed_renderers_lock_); + sandboxed_renderers_.erase(process_id); +} + +bool AtomBrowserClient::IsRendererSandboxed(int process_id) { + base::AutoLock auto_lock(sandboxed_renderers_lock_); + return sandboxed_renderers_.count(process_id); +} + void AtomBrowserClient::RenderProcessWillLaunch( content::RenderProcessHost* host) { int process_id = host->GetID(); @@ -106,6 +144,13 @@ void AtomBrowserClient::RenderProcessWillLaunch( host->AddFilter(new TtsMessageFilter(process_id, host->GetBrowserContext())); host->AddFilter( new WidevineCdmMessageFilter(process_id, host->GetBrowserContext())); + + content::WebContents* web_contents = GetWebContentsFromProcessID(process_id); + if (WebContentsPreferences::IsSandboxed(web_contents)) { + AddSandboxedRendererId(host->GetID()); + // ensure the sandboxed renderer id is removed later + host->AddObserver(this); + } } content::SpeechRecognitionManagerDelegate* @@ -155,8 +200,7 @@ void AtomBrowserClient::OverrideSiteInstanceForNavigation( return; } - // Restart renderer process for all navigations except "javacript:" scheme. - if (url.SchemeIs(url::kJavaScriptScheme)) + if (!ShouldCreateNewSiteInstance(browser_context, current_instance, url)) return; scoped_refptr site_instance = @@ -281,6 +325,11 @@ bool AtomBrowserClient::CanCreateWindow( bool* no_javascript_access) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + if (IsRendererSandboxed(render_process_id)) { + *no_javascript_access = false; + return true; + } + if (delegate_) { content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, base::Bind(&api::App::OnCreateWindow, @@ -338,6 +387,7 @@ void AtomBrowserClient::RenderProcessHostDestroyed( break; } } + RemoveSandboxedRendererId(process_id); } } // namespace atom diff --git a/atom/browser/atom_browser_client.h b/atom/browser/atom_browser_client.h index 5a43dbad30b7..b6e928ce8e69 100644 --- a/atom/browser/atom_browser_client.h +++ b/atom/browser/atom_browser_client.h @@ -6,6 +6,7 @@ #define ATOM_BROWSER_ATOM_BROWSER_CLIENT_H_ #include +#include #include #include @@ -108,8 +109,19 @@ class AtomBrowserClient : public brightray::BrowserClient, void RenderProcessHostDestroyed(content::RenderProcessHost* host) override; private: + bool ShouldCreateNewSiteInstance(content::BrowserContext* browser_context, + content::SiteInstance* current_instance, + const GURL& dest_url); + // Add/remove a process id to `sandboxed_renderers_`. + void AddSandboxedRendererId(int process_id); + void RemoveSandboxedRendererId(int process_id); + bool IsRendererSandboxed(int process_id); + // pending_render_process => current_render_process. std::map pending_processes_; + // Set that contains the process ids of all sandboxed renderers + std::set sandboxed_renderers_; + base::Lock sandboxed_renderers_lock_; std::unique_ptr resource_dispatcher_host_delegate_; diff --git a/atom/browser/web_contents_preferences.cc b/atom/browser/web_contents_preferences.cc index 5628c19e9e9a..a9e7fb448435 100644 --- a/atom/browser/web_contents_preferences.cc +++ b/atom/browser/web_contents_preferences.cc @@ -97,6 +97,12 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches( command_line->AppendSwitchASCII(switches::kNodeIntegration, node_integration ? "true" : "false"); + // If the `sandbox` option was passed to the BrowserWindow's webPreferences, + // pass `--enable-sandbox` to the renderer so it won't have any node.js + // integration. + if (IsSandboxed(web_contents)) + command_line->AppendSwitch(switches::kEnableSandbox); + // The preload script. base::FilePath::StringType preload; if (web_preferences.GetString(options::kPreloadScript, &preload)) { @@ -194,6 +200,21 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches( command_line->AppendSwitch(cc::switches::kEnableBeginFrameScheduling); } +bool WebContentsPreferences::IsSandboxed(content::WebContents* web_contents) { + WebContentsPreferences* self; + if (!web_contents) + return false; + + self = FromWebContents(web_contents); + if (!self) + return false; + + base::DictionaryValue& web_preferences = self->web_preferences_; + bool sandboxed = false; + web_preferences.GetBoolean("sandbox", &sandboxed); + return sandboxed; +} + // static void WebContentsPreferences::OverrideWebkitPrefs( content::WebContents* web_contents, content::WebPreferences* prefs) { diff --git a/atom/browser/web_contents_preferences.h b/atom/browser/web_contents_preferences.h index daf1f6e84de5..3a04ea9eea0b 100644 --- a/atom/browser/web_contents_preferences.h +++ b/atom/browser/web_contents_preferences.h @@ -36,6 +36,8 @@ class WebContentsPreferences static void AppendExtraCommandLineSwitches( content::WebContents* web_contents, base::CommandLine* command_line); + static bool IsSandboxed(content::WebContents* web_contents); + // Modify the WebPreferences according to |web_contents|'s preferences. static void OverrideWebkitPrefs( content::WebContents* web_contents, content::WebPreferences* prefs); diff --git a/atom/common/options_switches.cc b/atom/common/options_switches.cc index 6b514599f085..599efbee4296 100644 --- a/atom/common/options_switches.cc +++ b/atom/common/options_switches.cc @@ -117,6 +117,9 @@ const char kDisableBlinkFeatures[] = "disableBlinkFeatures"; namespace switches { +// Enable chromium sandbox. +const char kEnableSandbox[] = "enable-sandbox"; + // Enable plugins. const char kEnablePlugins[] = "enable-plugins"; diff --git a/atom/common/options_switches.h b/atom/common/options_switches.h index 54c638772887..259f4e17b307 100644 --- a/atom/common/options_switches.h +++ b/atom/common/options_switches.h @@ -67,6 +67,7 @@ extern const char kDisableBlinkFeatures[]; namespace switches { +extern const char kEnableSandbox[]; extern const char kEnablePlugins[]; extern const char kPpapiFlashPath[]; extern const char kPpapiFlashVersion[];