Home | History | Annotate | Download | only in messaging
      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