Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / messaging / native_message_process_host.cc
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       read_file_(base::kInvalidPlatformFileValue),
108       read_pending_(false),
109       write_pending_(false) {
110   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
111
112   // It's safe to use base::Unretained() here because NativeMessagePort always
113   // deletes us on the IO thread.
114   content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
115       base::Bind(&NativeMessageProcessHost::LaunchHostProcess,
116                  base::Unretained(this)));
117 }
118
119 NativeMessageProcessHost::~NativeMessageProcessHost() {
120   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
121   Close(std::string());
122 }
123
124 // static
125 scoped_ptr<NativeMessageProcessHost> NativeMessageProcessHost::Create(
126     gfx::NativeView native_view,
127     base::WeakPtr<Client> weak_client_ui,
128     const std::string& source_extension_id,
129     const std::string& native_host_name,
130     int destination_port,
131     bool allow_user_level) {
132   return CreateWithLauncher(weak_client_ui, source_extension_id,
133                             native_host_name, destination_port,
134                             NativeProcessLauncher::CreateDefault(
135                                 allow_user_level, native_view));
136 }
137
138 // static
139 scoped_ptr<NativeMessageProcessHost>
140 NativeMessageProcessHost::CreateWithLauncher(
141     base::WeakPtr<Client> weak_client_ui,
142     const std::string& source_extension_id,
143     const std::string& native_host_name,
144     int destination_port,
145     scoped_ptr<NativeProcessLauncher> launcher) {
146   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
147
148   scoped_ptr<NativeMessageProcessHost> process(new NativeMessageProcessHost(
149       weak_client_ui, source_extension_id, native_host_name,
150       destination_port, launcher.Pass()));
151
152   return process.Pass();
153 }
154
155 void NativeMessageProcessHost::LaunchHostProcess() {
156   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
157
158   GURL origin(std::string(kExtensionScheme) + "://" + source_extension_id_);
159   launcher_->Launch(origin, native_host_name_,
160                     base::Bind(&NativeMessageProcessHost::OnHostProcessLaunched,
161                                base::Unretained(this)));
162 }
163
164 void NativeMessageProcessHost::OnHostProcessLaunched(
165     NativeProcessLauncher::LaunchResult result,
166     base::ProcessHandle process_handle,
167     base::PlatformFile read_file,
168     base::PlatformFile write_file) {
169   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
170
171   switch (result) {
172     case NativeProcessLauncher::RESULT_INVALID_NAME:
173       Close(kInvalidNameError);
174       return;
175     case NativeProcessLauncher::RESULT_NOT_FOUND:
176       Close(kNotFoundError);
177       return;
178     case NativeProcessLauncher::RESULT_FORBIDDEN:
179       Close(kForbiddenError);
180       return;
181     case NativeProcessLauncher::RESULT_FAILED_TO_START:
182       Close(kFailedToStartError);
183       return;
184     case NativeProcessLauncher::RESULT_SUCCESS:
185       break;
186   }
187
188   process_handle_ = process_handle;
189   read_file_ = read_file;
190
191   scoped_refptr<base::TaskRunner> task_runner(
192       content::BrowserThread::GetBlockingPool()->
193           GetTaskRunnerWithShutdownBehavior(
194               base::SequencedWorkerPool::SKIP_ON_SHUTDOWN));
195
196   read_stream_.reset(new net::FileStream(
197       read_file, base::PLATFORM_FILE_READ | base::PLATFORM_FILE_ASYNC, NULL,
198       task_runner));
199   write_stream_.reset(new net::FileStream(
200       write_file, base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_ASYNC, NULL,
201       task_runner));
202
203   WaitRead();
204   DoWrite();
205 }
206
207 void NativeMessageProcessHost::Send(const std::string& json) {
208   DCHECK(content::BrowserThread::CurrentlyOn(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 */, base::MessageLoopForIO::WATCH_READ,
260     &read_watcher_, this);
261 #else  // defined(OS_POSIX)
262   DoRead();
263 #endif  // defined(!OS_POSIX)
264 }
265
266 void NativeMessageProcessHost::DoRead() {
267   DCHECK(content::BrowserThread::CurrentlyOn(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(content::BrowserThread::CurrentlyOn(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(content::BrowserThread::CurrentlyOn(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(content::BrowserThread::CurrentlyOn(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(content::BrowserThread::CurrentlyOn(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(content::BrowserThread::CurrentlyOn(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(content::BrowserThread::CurrentlyOn(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(content::BrowserThread::CurrentlyOn(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