Upstream version 7.36.149.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 #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