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/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