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 30 namespace content { 31 32 // NOTE: changes to this class need to be reviewed by the security team. 33 class 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 86 UtilityMainThreadFactoryFunction g_utility_main_thread_factory = NULL; 87 88 UtilityProcessHost* 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 94 void UtilityProcessHostImpl::RegisterUtilityMainThreadFactory( 95 UtilityMainThreadFactoryFunction create) { 96 g_utility_main_thread_factory = create; 97 } 98 99 UtilityProcessHostImpl::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 116 UtilityProcessHostImpl::~UtilityProcessHostImpl() { 117 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 118 if (is_batch_mode_) 119 EndBatchMode(); 120 } 121 122 bool UtilityProcessHostImpl::Send(IPC::Message* message) { 123 if (!StartProcess()) 124 return false; 125 126 return process_->Send(message); 127 } 128 129 bool 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 136 void UtilityProcessHostImpl::EndBatchMode() { 137 CHECK(is_batch_mode_); 138 is_batch_mode_ = false; 139 Send(new UtilityMsg_BatchMode_Finished()); 140 } 141 142 void UtilityProcessHostImpl::SetExposedDir(const base::FilePath& dir) { 143 exposed_dir_ = dir; 144 } 145 146 void UtilityProcessHostImpl::EnableMDns() { 147 is_mdns_enabled_ = true; 148 } 149 150 void UtilityProcessHostImpl::DisableSandbox() { 151 no_sandbox_ = true; 152 } 153 154 #if defined(OS_WIN) 155 void UtilityProcessHostImpl::ElevatePrivileges() { 156 no_sandbox_ = true; 157 run_elevated_ = true; 158 } 159 #endif 160 161 const ChildProcessData& UtilityProcessHostImpl::GetData() { 162 return process_->GetData(); 163 } 164 165 #if defined(OS_POSIX) 166 167 void UtilityProcessHostImpl::SetEnv(const base::EnvironmentMap& env) { 168 env_ = env; 169 } 170 171 #endif // OS_POSIX 172 173 bool 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 270 bool 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 284 void 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 294 void 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