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