1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "content/browser/utility_process_host_impl.h" 6 7#include "base/bind.h" 8#include "base/bind_helpers.h" 9#include "base/command_line.h" 10#include "base/lazy_instance.h" 11#include "base/message_loop/message_loop.h" 12#include "base/run_loop.h" 13#include "base/sequenced_task_runner.h" 14#include "base/strings/utf_string_conversions.h" 15#include "base/synchronization/lock.h" 16#include "base/synchronization/waitable_event.h" 17#include "content/browser/browser_child_process_host_impl.h" 18#include "content/browser/renderer_host/render_process_host_impl.h" 19#include "content/common/child_process_host_impl.h" 20#include "content/common/utility_messages.h" 21#include "content/public/browser/browser_thread.h" 22#include "content/public/browser/content_browser_client.h" 23#include "content/public/browser/utility_process_host_client.h" 24#include "content/public/common/content_switches.h" 25#include "content/public/common/process_type.h" 26#include "content/public/common/sandboxed_process_launcher_delegate.h" 27#include "ipc/ipc_switches.h" 28#include "ui/base/ui_base_switches.h" 29 30namespace content { 31 32// NOTE: changes to this class need to be reviewed by the security team. 33class UtilitySandboxedProcessLauncherDelegate 34 : public SandboxedProcessLauncherDelegate { 35 public: 36 UtilitySandboxedProcessLauncherDelegate(const base::FilePath& exposed_dir, 37 bool launch_elevated, bool no_sandbox, 38 base::EnvironmentMap& env, 39 ChildProcessHost* host) 40 : exposed_dir_(exposed_dir), 41#if defined(OS_WIN) 42 launch_elevated_(launch_elevated) 43#elif defined(OS_POSIX) 44 env_(env), 45 no_sandbox_(no_sandbox), 46 ipc_fd_(host->TakeClientFileDescriptor()) 47#endif // OS_WIN 48 {} 49 50 virtual ~UtilitySandboxedProcessLauncherDelegate() {} 51 52#if defined(OS_WIN) 53 virtual bool ShouldLaunchElevated() OVERRIDE { 54 return launch_elevated_; 55 } 56 virtual void PreSandbox(bool* disable_default_policy, 57 base::FilePath* exposed_dir) OVERRIDE { 58 *exposed_dir = exposed_dir_; 59 } 60#elif defined(OS_POSIX) 61 62 virtual bool ShouldUseZygote() OVERRIDE { 63 return !no_sandbox_ && exposed_dir_.empty(); 64 } 65 virtual base::EnvironmentMap GetEnvironment() OVERRIDE { 66 return env_; 67 } 68 virtual int GetIpcFd() OVERRIDE { 69 return ipc_fd_; 70 } 71#endif // OS_WIN 72 73 private: 74 75 base::FilePath exposed_dir_; 76 77#if defined(OS_WIN) 78 bool launch_elevated_; 79#elif defined(OS_POSIX) 80 base::EnvironmentMap env_; 81 bool no_sandbox_; 82 int ipc_fd_; 83#endif // OS_WIN 84}; 85 86UtilityMainThreadFactoryFunction g_utility_main_thread_factory = NULL; 87 88UtilityProcessHost* UtilityProcessHost::Create( 89 const scoped_refptr<UtilityProcessHostClient>& client, 90 const scoped_refptr<base::SequencedTaskRunner>& client_task_runner) { 91 return new UtilityProcessHostImpl(client, client_task_runner); 92} 93 94void UtilityProcessHostImpl::RegisterUtilityMainThreadFactory( 95 UtilityMainThreadFactoryFunction create) { 96 g_utility_main_thread_factory = create; 97} 98 99UtilityProcessHostImpl::UtilityProcessHostImpl( 100 const scoped_refptr<UtilityProcessHostClient>& client, 101 const scoped_refptr<base::SequencedTaskRunner>& client_task_runner) 102 : client_(client), 103 client_task_runner_(client_task_runner), 104 is_batch_mode_(false), 105 is_mdns_enabled_(false), 106 no_sandbox_(false), 107 run_elevated_(false), 108#if defined(OS_LINUX) 109 child_flags_(ChildProcessHost::CHILD_ALLOW_SELF), 110#else 111 child_flags_(ChildProcessHost::CHILD_NORMAL), 112#endif 113 started_(false) { 114} 115 116UtilityProcessHostImpl::~UtilityProcessHostImpl() { 117 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 118 if (is_batch_mode_) 119 EndBatchMode(); 120} 121 122bool UtilityProcessHostImpl::Send(IPC::Message* message) { 123 if (!StartProcess()) 124 return false; 125 126 return process_->Send(message); 127} 128 129bool UtilityProcessHostImpl::StartBatchMode() { 130 CHECK(!is_batch_mode_); 131 is_batch_mode_ = StartProcess(); 132 Send(new UtilityMsg_BatchMode_Started()); 133 return is_batch_mode_; 134} 135 136void UtilityProcessHostImpl::EndBatchMode() { 137 CHECK(is_batch_mode_); 138 is_batch_mode_ = false; 139 Send(new UtilityMsg_BatchMode_Finished()); 140} 141 142void UtilityProcessHostImpl::SetExposedDir(const base::FilePath& dir) { 143 exposed_dir_ = dir; 144} 145 146void UtilityProcessHostImpl::EnableMDns() { 147 is_mdns_enabled_ = true; 148} 149 150void UtilityProcessHostImpl::DisableSandbox() { 151 no_sandbox_ = true; 152} 153 154#if defined(OS_WIN) 155void UtilityProcessHostImpl::ElevatePrivileges() { 156 no_sandbox_ = true; 157 run_elevated_ = true; 158} 159#endif 160 161const ChildProcessData& UtilityProcessHostImpl::GetData() { 162 return process_->GetData(); 163} 164 165#if defined(OS_POSIX) 166 167void UtilityProcessHostImpl::SetEnv(const base::EnvironmentMap& env) { 168 env_ = env; 169} 170 171#endif // OS_POSIX 172 173bool UtilityProcessHostImpl::StartProcess() { 174 if (started_) 175 return true; 176 started_ = true; 177 178 if (is_batch_mode_) 179 return true; 180 181 // Name must be set or metrics_service will crash in any test which 182 // launches a UtilityProcessHost. 183 process_.reset(new BrowserChildProcessHostImpl(PROCESS_TYPE_UTILITY, this)); 184 process_->SetName(base::ASCIIToUTF16("utility process")); 185 186 std::string channel_id = process_->GetHost()->CreateChannel(); 187 if (channel_id.empty()) 188 return false; 189 190 if (RenderProcessHost::run_renderer_in_process()) { 191 DCHECK(g_utility_main_thread_factory); 192 // See comment in RenderProcessHostImpl::Init() for the background on why we 193 // support single process mode this way. 194 in_process_thread_.reset(g_utility_main_thread_factory(channel_id)); 195 in_process_thread_->Start(); 196 } else { 197 const base::CommandLine& browser_command_line = 198 *base::CommandLine::ForCurrentProcess(); 199 int child_flags = child_flags_; 200 201#if defined(OS_POSIX) 202 bool has_cmd_prefix = browser_command_line.HasSwitch( 203 switches::kUtilityCmdPrefix); 204 205 // When running under gdb, forking /proc/self/exe ends up forking the gdb 206 // executable instead of Chromium. It is almost safe to assume that no 207 // updates will happen while a developer is running with 208 // |switches::kUtilityCmdPrefix|. See ChildProcessHost::GetChildPath() for 209 // a similar case with Valgrind. 210 if (has_cmd_prefix) 211 child_flags = ChildProcessHost::CHILD_NORMAL; 212#endif 213 214 base::FilePath exe_path = ChildProcessHost::GetChildPath(child_flags); 215 if (exe_path.empty()) { 216 NOTREACHED() << "Unable to get utility process binary name."; 217 return false; 218 } 219 220 base::CommandLine* cmd_line = new base::CommandLine(exe_path); 221 cmd_line->AppendSwitchASCII(switches::kProcessType, 222 switches::kUtilityProcess); 223 cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id); 224 std::string locale = GetContentClient()->browser()->GetApplicationLocale(); 225 cmd_line->AppendSwitchASCII(switches::kLang, locale); 226 227 if (no_sandbox_ || browser_command_line.HasSwitch(switches::kNoSandbox)) 228 cmd_line->AppendSwitch(switches::kNoSandbox); 229#if defined(OS_MACOSX) 230 if (browser_command_line.HasSwitch(switches::kEnableSandboxLogging)) 231 cmd_line->AppendSwitch(switches::kEnableSandboxLogging); 232#endif 233 if (browser_command_line.HasSwitch(switches::kDebugPluginLoading)) 234 cmd_line->AppendSwitch(switches::kDebugPluginLoading); 235 236#if defined(OS_POSIX) 237 if (has_cmd_prefix) { 238 // Launch the utility child process with some prefix 239 // (usually "xterm -e gdb --args"). 240 cmd_line->PrependWrapper(browser_command_line.GetSwitchValueNative( 241 switches::kUtilityCmdPrefix)); 242 } 243 244 if (!exposed_dir_.empty()) { 245 cmd_line->AppendSwitchPath(switches::kUtilityProcessAllowedDir, 246 exposed_dir_); 247 } 248#endif 249 250 if (is_mdns_enabled_) 251 cmd_line->AppendSwitch(switches::kUtilityProcessEnableMDns); 252 253#if defined(OS_WIN) 254 // Let the utility process know if it is intended to be elevated. 255 if (run_elevated_) 256 cmd_line->AppendSwitch(switches::kUtilityProcessRunningElevated); 257#endif 258 259 process_->Launch( 260 new UtilitySandboxedProcessLauncherDelegate(exposed_dir_, 261 run_elevated_, 262 no_sandbox_, env_, 263 process_->GetHost()), 264 cmd_line); 265 } 266 267 return true; 268} 269 270bool UtilityProcessHostImpl::OnMessageReceived(const IPC::Message& message) { 271 if (!client_.get()) 272 return true; 273 274 client_task_runner_->PostTask( 275 FROM_HERE, 276 base::Bind( 277 base::IgnoreResult(&UtilityProcessHostClient::OnMessageReceived), 278 client_.get(), 279 message)); 280 281 return true; 282} 283 284void UtilityProcessHostImpl::OnProcessLaunchFailed() { 285 if (!client_.get()) 286 return; 287 288 client_task_runner_->PostTask( 289 FROM_HERE, 290 base::Bind(&UtilityProcessHostClient::OnProcessLaunchFailed, 291 client_.get())); 292} 293 294void UtilityProcessHostImpl::OnProcessCrashed(int exit_code) { 295 if (!client_.get()) 296 return; 297 298 client_task_runner_->PostTask( 299 FROM_HERE, 300 base::Bind(&UtilityProcessHostClient::OnProcessCrashed, client_.get(), 301 exit_code)); 302} 303 304} // namespace content 305