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.
5 #include "chrome/browser/extensions/api/messaging/native_message_process_host.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"
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;
34 // Message header contains 4-byte integer size of the message.
35 const size_t kMessageHeaderSize
= 4;
37 // Size of the buffer to be allocated for each read.
38 const size_t kReadBufferSize
= 4096;
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.";
52 namespace extensions
{
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
;
65 // All native messaging hosts are allowed if there is no blacklist.
66 if (!pref_service
->IsManagedPreference(pref_names::kNativeMessagingBlacklist
))
68 const base::ListValue
* blacklist
=
69 pref_service
->GetList(pref_names::kNativeMessagingBlacklist
);
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()) {
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())
93 NativeMessageProcessHost::NativeMessageProcessHost(
94 base::WeakPtr
<Client
> weak_client_ui
,
95 const std::string
& source_extension_id
,
96 const std::string
& native_host_name
,
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()),
105 process_handle_(base::kNullProcessHandle
),
106 #if defined(OS_POSIX)
109 read_pending_(false),
110 write_pending_(false) {
111 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
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)));
120 NativeMessageProcessHost::~NativeMessageProcessHost() {
121 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
122 Close(std::string());
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
));
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
);
149 scoped_ptr
<NativeMessageProcessHost
> process(new NativeMessageProcessHost(
150 weak_client_ui
, source_extension_id
, native_host_name
,
151 destination_port
, launcher
.Pass()));
153 return process
.Pass();
156 void NativeMessageProcessHost::LaunchHostProcess() {
157 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
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)));
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
);
173 case NativeProcessLauncher::RESULT_INVALID_NAME
:
174 Close(kInvalidNameError
);
176 case NativeProcessLauncher::RESULT_NOT_FOUND
:
177 Close(kNotFoundError
);
179 case NativeProcessLauncher::RESULT_FORBIDDEN
:
180 Close(kForbiddenError
);
182 case NativeProcessLauncher::RESULT_FAILED_TO_START
:
183 Close(kFailedToStartError
);
185 case NativeProcessLauncher::RESULT_SUCCESS
:
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();
195 scoped_refptr
<base::TaskRunner
> task_runner(
196 content::BrowserThread::GetBlockingPool()->
197 GetTaskRunnerWithShutdownBehavior(
198 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN
));
200 read_stream_
.reset(new net::FileStream(read_file
.Pass(), task_runner
));
201 write_stream_
.reset(new net::FileStream(write_file
.Pass(), task_runner
));
207 void NativeMessageProcessHost::Send(const std::string
& json
) {
208 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
213 // Allocate new buffer for the message.
214 scoped_refptr
<net::IOBufferWithSize
> buffer
=
215 new net::IOBufferWithSize(json
.size() + kMessageHeaderSize
);
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());
222 // Push new message to the write queue.
223 write_queue_
.push(buffer
);
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.
232 #if defined(OS_POSIX)
233 void NativeMessageProcessHost::OnFileCanReadWithoutBlocking(int fd
) {
234 DCHECK_EQ(fd
, read_file_
);
238 void NativeMessageProcessHost::OnFileCanWriteWithoutBlocking(int fd
) {
241 #endif // !defined(OS_POSIX)
243 void NativeMessageProcessHost::ReadNowForTesting() {
247 void NativeMessageProcessHost::WaitRead() {
251 DCHECK(!read_pending_
);
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)
263 #endif // defined(!OS_POSIX)
266 void NativeMessageProcessHost::DoRead() {
267 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
269 while (!closed_
&& !read_pending_
) {
270 read_buffer_
= new net::IOBuffer(kReadBufferSize
);
271 int result
= read_stream_
->Read(
274 base::Bind(&NativeMessageProcessHost::OnRead
, base::Unretained(this)));
275 HandleReadResult(result
);
279 void NativeMessageProcessHost::OnRead(int result
) {
280 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
281 DCHECK(read_pending_
);
282 read_pending_
= false;
284 HandleReadResult(result
);
288 void NativeMessageProcessHost::HandleReadResult(int result
) {
289 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
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
);
303 LOG(ERROR
) << "Error when reading from Native Messaging host: " << result
;
304 Close(kHostInputOuputError
);
308 void NativeMessageProcessHost::ProcessIncomingData(
309 const char* data
, int data_size
) {
310 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
312 incoming_data_
.append(data
, data_size
);
315 if (incoming_data_
.size() < kMessageHeaderSize
)
318 size_t message_size
=
319 *reinterpret_cast<const uint32
*>(incoming_data_
.data());
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
);
328 if (incoming_data_
.size() < message_size
+ kMessageHeaderSize
)
331 content::BrowserThread::PostTask(content::BrowserThread::UI
, FROM_HERE
,
332 base::Bind(&Client::PostMessageFromNativeProcess
, weak_client_ui_
,
334 incoming_data_
.substr(kMessageHeaderSize
, message_size
)));
336 incoming_data_
.erase(0, kMessageHeaderSize
+ message_size
);
340 void NativeMessageProcessHost::DoWrite() {
341 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
343 while (!write_pending_
&& !closed_
) {
344 if (!current_write_buffer_
.get() ||
345 !current_write_buffer_
->BytesRemaining()) {
346 if (write_queue_
.empty())
348 current_write_buffer_
= new net::DrainableIOBuffer(
349 write_queue_
.front().get(), write_queue_
.front()->size());
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
);
362 void NativeMessageProcessHost::HandleWriteResult(int result
) {
363 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
366 if (result
== net::ERR_IO_PENDING
) {
367 write_pending_
= true;
369 LOG(ERROR
) << "Error when writing to Native Messaging host: " << result
;
370 Close(kHostInputOuputError
);
375 current_write_buffer_
->DidConsume(result
);
378 void NativeMessageProcessHost::OnWritten(int result
) {
379 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
381 DCHECK(write_pending_
);
382 write_pending_
= false;
384 HandleWriteResult(result
);
388 void NativeMessageProcessHost::Close(const std::string
& error_message
) {
389 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
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
));
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_
));
408 base::EnsureProcessTerminated(process_handle_
);
410 process_handle_
= base::kNullProcessHandle
;
414 } // namespace extensions