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 "chrome/browser/extensions/api/messaging/native_message_process_host.h" 6 7 #include "base/bind.h" 8 #include "base/files/file_path.h" 9 #include "base/logging.h" 10 #include "base/platform_file.h" 11 #include "base/prefs/pref_service.h" 12 #include "base/process/kill.h" 13 #include "base/threading/sequenced_worker_pool.h" 14 #include "base/values.h" 15 #include "chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h" 16 #include "chrome/browser/extensions/api/messaging/native_process_launcher.h" 17 #include "chrome/common/chrome_version_info.h" 18 #include "content/public/browser/browser_thread.h" 19 #include "extensions/browser/pref_names.h" 20 #include "extensions/common/constants.h" 21 #include "extensions/common/features/feature.h" 22 #include "net/base/file_stream.h" 23 #include "net/base/io_buffer.h" 24 #include "net/base/net_errors.h" 25 #include "net/base/net_util.h" 26 #include "url/gurl.h" 27 28 namespace { 29 30 // Maximum message size in bytes for messages received from Native Messaging 31 // hosts. Message size is limited mainly to prevent Chrome from crashing when 32 // native application misbehaves (e.g. starts writing garbage to the pipe). 33 const size_t kMaximumMessageSize = 1024 * 1024; 34 35 // Message header contains 4-byte integer size of the message. 36 const size_t kMessageHeaderSize = 4; 37 38 // Size of the buffer to be allocated for each read. 39 const size_t kReadBufferSize = 4096; 40 41 const char kFailedToStartError[] = "Failed to start native messaging host."; 42 const char kInvalidNameError[] = 43 "Invalid native messaging host name specified."; 44 const char kNativeHostExited[] = "Native host has exited."; 45 const char kNotFoundError[] = "Specified native messaging host not found."; 46 const char kForbiddenError[] = 47 "Access to the specified native messaging host is forbidden."; 48 const char kHostInputOuputError[] = 49 "Error when communicating with the native messaging host."; 50 51 } // namespace 52 53 namespace extensions { 54 55 // static 56 NativeMessageProcessHost::PolicyPermission 57 NativeMessageProcessHost::IsHostAllowed(const PrefService* pref_service, 58 const std::string& native_host_name) { 59 NativeMessageProcessHost::PolicyPermission allow_result = ALLOW_ALL; 60 if (pref_service->IsManagedPreference( 61 pref_names::kNativeMessagingUserLevelHosts)) { 62 if (!pref_service->GetBoolean(pref_names::kNativeMessagingUserLevelHosts)) 63 allow_result = ALLOW_SYSTEM_ONLY; 64 } 65 66 // All native messaging hosts are allowed if there is no blacklist. 67 if (!pref_service->IsManagedPreference(pref_names::kNativeMessagingBlacklist)) 68 return allow_result; 69 const base::ListValue* blacklist = 70 pref_service->GetList(pref_names::kNativeMessagingBlacklist); 71 if (!blacklist) 72 return allow_result; 73 74 // Check if the name or the wildcard is in the blacklist. 75 base::StringValue name_value(native_host_name); 76 base::StringValue wildcard_value("*"); 77 if (blacklist->Find(name_value) == blacklist->end() && 78 blacklist->Find(wildcard_value) == blacklist->end()) { 79 return allow_result; 80 } 81 82 // The native messaging host is blacklisted. Check the whitelist. 83 if (pref_service->IsManagedPreference( 84 pref_names::kNativeMessagingWhitelist)) { 85 const base::ListValue* whitelist = 86 pref_service->GetList(pref_names::kNativeMessagingWhitelist); 87 if (whitelist && whitelist->Find(name_value) != whitelist->end()) 88 return allow_result; 89 } 90 91 return DISALLOW; 92 } 93 94 NativeMessageProcessHost::NativeMessageProcessHost( 95 base::WeakPtr<Client> weak_client_ui, 96 const std::string& source_extension_id, 97 const std::string& native_host_name, 98 int destination_port, 99 scoped_ptr<NativeProcessLauncher> launcher) 100 : weak_client_ui_(weak_client_ui), 101 source_extension_id_(source_extension_id), 102 native_host_name_(native_host_name), 103 destination_port_(destination_port), 104 launcher_(launcher.Pass()), 105 closed_(false), 106 process_handle_(base::kNullProcessHandle), 107 #if defined(OS_POSIX) 108 read_file_(base::kInvalidPlatformFileValue), 109 #endif 110 read_pending_(false), 111 write_pending_(false) { 112 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 113 114 // It's safe to use base::Unretained() here because NativeMessagePort always 115 // deletes us on the IO thread. 116 content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, 117 base::Bind(&NativeMessageProcessHost::LaunchHostProcess, 118 base::Unretained(this))); 119 } 120 121 NativeMessageProcessHost::~NativeMessageProcessHost() { 122 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 123 Close(std::string()); 124 } 125 126 // static 127 scoped_ptr<NativeMessageProcessHost> NativeMessageProcessHost::Create( 128 gfx::NativeView native_view, 129 base::WeakPtr<Client> weak_client_ui, 130 const std::string& source_extension_id, 131 const std::string& native_host_name, 132 int destination_port, 133 bool allow_user_level) { 134 return CreateWithLauncher(weak_client_ui, source_extension_id, 135 native_host_name, destination_port, 136 NativeProcessLauncher::CreateDefault( 137 allow_user_level, native_view)); 138 } 139 140 // static 141 scoped_ptr<NativeMessageProcessHost> 142 NativeMessageProcessHost::CreateWithLauncher( 143 base::WeakPtr<Client> weak_client_ui, 144 const std::string& source_extension_id, 145 const std::string& native_host_name, 146 int destination_port, 147 scoped_ptr<NativeProcessLauncher> launcher) { 148 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 149 150 scoped_ptr<NativeMessageProcessHost> process(new NativeMessageProcessHost( 151 weak_client_ui, source_extension_id, native_host_name, 152 destination_port, launcher.Pass())); 153 154 return process.Pass(); 155 } 156 157 void NativeMessageProcessHost::LaunchHostProcess() { 158 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 159 160 GURL origin(std::string(kExtensionScheme) + "://" + source_extension_id_); 161 launcher_->Launch(origin, native_host_name_, 162 base::Bind(&NativeMessageProcessHost::OnHostProcessLaunched, 163 base::Unretained(this))); 164 } 165 166 void NativeMessageProcessHost::OnHostProcessLaunched( 167 NativeProcessLauncher::LaunchResult result, 168 base::ProcessHandle process_handle, 169 base::File read_file, 170 base::File write_file) { 171 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 172 173 switch (result) { 174 case NativeProcessLauncher::RESULT_INVALID_NAME: 175 Close(kInvalidNameError); 176 return; 177 case NativeProcessLauncher::RESULT_NOT_FOUND: 178 Close(kNotFoundError); 179 return; 180 case NativeProcessLauncher::RESULT_FORBIDDEN: 181 Close(kForbiddenError); 182 return; 183 case NativeProcessLauncher::RESULT_FAILED_TO_START: 184 Close(kFailedToStartError); 185 return; 186 case NativeProcessLauncher::RESULT_SUCCESS: 187 break; 188 } 189 190 process_handle_ = process_handle; 191 #if defined(OS_POSIX) 192 // This object is not the owner of the file so it should not keep an fd. 193 read_file_ = read_file.GetPlatformFile(); 194 #endif 195 196 scoped_refptr<base::TaskRunner> task_runner( 197 content::BrowserThread::GetBlockingPool()-> 198 GetTaskRunnerWithShutdownBehavior( 199 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)); 200 201 read_stream_.reset(new net::FileStream(read_file.Pass(), task_runner)); 202 write_stream_.reset(new net::FileStream(write_file.Pass(), task_runner)); 203 204 WaitRead(); 205 DoWrite(); 206 } 207 208 void NativeMessageProcessHost::Send(const std::string& json) { 209 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 210 211 if (closed_) 212 return; 213 214 // Allocate new buffer for the message. 215 scoped_refptr<net::IOBufferWithSize> buffer = 216 new net::IOBufferWithSize(json.size() + kMessageHeaderSize); 217 218 // Copy size and content of the message to the buffer. 219 COMPILE_ASSERT(sizeof(uint32) == kMessageHeaderSize, incorrect_header_size); 220 *reinterpret_cast<uint32*>(buffer->data()) = json.size(); 221 memcpy(buffer->data() + kMessageHeaderSize, json.data(), json.size()); 222 223 // Push new message to the write queue. 224 write_queue_.push(buffer); 225 226 // Send() may be called before the host process is started. In that case the 227 // message will be written when OnHostProcessLaunched() is called. If it's 228 // already started then write the message now. 229 if (write_stream_) 230 DoWrite(); 231 } 232 233 #if defined(OS_POSIX) 234 void NativeMessageProcessHost::OnFileCanReadWithoutBlocking(int fd) { 235 DCHECK_EQ(fd, read_file_); 236 DoRead(); 237 } 238 239 void NativeMessageProcessHost::OnFileCanWriteWithoutBlocking(int fd) { 240 NOTREACHED(); 241 } 242 #endif // !defined(OS_POSIX) 243 244 void NativeMessageProcessHost::ReadNowForTesting() { 245 DoRead(); 246 } 247 248 void NativeMessageProcessHost::WaitRead() { 249 if (closed_) 250 return; 251 252 DCHECK(!read_pending_); 253 254 // On POSIX FileStream::Read() uses blocking thread pool, so it's better to 255 // wait for the file to become readable before calling DoRead(). Otherwise it 256 // would always be consuming one thread in the thread pool. On Windows 257 // FileStream uses overlapped IO, so that optimization isn't necessary there. 258 #if defined(OS_POSIX) 259 base::MessageLoopForIO::current()->WatchFileDescriptor( 260 read_file_, false /* persistent */, 261 base::MessageLoopForIO::WATCH_READ, &read_watcher_, this); 262 #else // defined(OS_POSIX) 263 DoRead(); 264 #endif // defined(!OS_POSIX) 265 } 266 267 void NativeMessageProcessHost::DoRead() { 268 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 269 270 while (!closed_ && !read_pending_) { 271 read_buffer_ = new net::IOBuffer(kReadBufferSize); 272 int result = read_stream_->Read( 273 read_buffer_.get(), 274 kReadBufferSize, 275 base::Bind(&NativeMessageProcessHost::OnRead, base::Unretained(this))); 276 HandleReadResult(result); 277 } 278 } 279 280 void NativeMessageProcessHost::OnRead(int result) { 281 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 282 DCHECK(read_pending_); 283 read_pending_ = false; 284 285 HandleReadResult(result); 286 WaitRead(); 287 } 288 289 void NativeMessageProcessHost::HandleReadResult(int result) { 290 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 291 292 if (closed_) 293 return; 294 295 if (result > 0) { 296 ProcessIncomingData(read_buffer_->data(), result); 297 } else if (result == net::ERR_IO_PENDING) { 298 read_pending_ = true; 299 } else if (result == 0 || result == net::ERR_CONNECTION_RESET) { 300 // On Windows we get net::ERR_CONNECTION_RESET for a broken pipe, while on 301 // Posix read() returns 0 in that case. 302 Close(kNativeHostExited); 303 } else { 304 LOG(ERROR) << "Error when reading from Native Messaging host: " << result; 305 Close(kHostInputOuputError); 306 } 307 } 308 309 void NativeMessageProcessHost::ProcessIncomingData( 310 const char* data, int data_size) { 311 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 312 313 incoming_data_.append(data, data_size); 314 315 while (true) { 316 if (incoming_data_.size() < kMessageHeaderSize) 317 return; 318 319 size_t message_size = 320 *reinterpret_cast<const uint32*>(incoming_data_.data()); 321 322 if (message_size > kMaximumMessageSize) { 323 LOG(ERROR) << "Native Messaging host tried sending a message that is " 324 << message_size << " bytes long."; 325 Close(kHostInputOuputError); 326 return; 327 } 328 329 if (incoming_data_.size() < message_size + kMessageHeaderSize) 330 return; 331 332 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, 333 base::Bind(&Client::PostMessageFromNativeProcess, weak_client_ui_, 334 destination_port_, 335 incoming_data_.substr(kMessageHeaderSize, message_size))); 336 337 incoming_data_.erase(0, kMessageHeaderSize + message_size); 338 } 339 } 340 341 void NativeMessageProcessHost::DoWrite() { 342 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 343 344 while (!write_pending_ && !closed_) { 345 if (!current_write_buffer_.get() || 346 !current_write_buffer_->BytesRemaining()) { 347 if (write_queue_.empty()) 348 return; 349 current_write_buffer_ = new net::DrainableIOBuffer( 350 write_queue_.front().get(), write_queue_.front()->size()); 351 write_queue_.pop(); 352 } 353 354 int result = 355 write_stream_->Write(current_write_buffer_.get(), 356 current_write_buffer_->BytesRemaining(), 357 base::Bind(&NativeMessageProcessHost::OnWritten, 358 base::Unretained(this))); 359 HandleWriteResult(result); 360 } 361 } 362 363 void NativeMessageProcessHost::HandleWriteResult(int result) { 364 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 365 366 if (result <= 0) { 367 if (result == net::ERR_IO_PENDING) { 368 write_pending_ = true; 369 } else { 370 LOG(ERROR) << "Error when writing to Native Messaging host: " << result; 371 Close(kHostInputOuputError); 372 } 373 return; 374 } 375 376 current_write_buffer_->DidConsume(result); 377 } 378 379 void NativeMessageProcessHost::OnWritten(int result) { 380 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 381 382 DCHECK(write_pending_); 383 write_pending_ = false; 384 385 HandleWriteResult(result); 386 DoWrite(); 387 } 388 389 void NativeMessageProcessHost::Close(const std::string& error_message) { 390 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 391 392 if (!closed_) { 393 closed_ = true; 394 read_stream_.reset(); 395 write_stream_.reset(); 396 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, 397 base::Bind(&Client::CloseChannel, weak_client_ui_, 398 destination_port_, error_message)); 399 } 400 401 if (process_handle_ != base::kNullProcessHandle) { 402 // Kill the host process if necessary to make sure we don't leave zombies. 403 // On OSX base::EnsureProcessTerminated() may block, so we have to post a 404 // task on the blocking pool. 405 #if defined(OS_MACOSX) 406 content::BrowserThread::PostBlockingPoolTask( 407 FROM_HERE, base::Bind(&base::EnsureProcessTerminated, process_handle_)); 408 #else 409 base::EnsureProcessTerminated(process_handle_); 410 #endif 411 process_handle_ = base::kNullProcessHandle; 412 } 413 } 414 415 } // namespace extensions 416