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
IsHostAllowed(const PrefService * pref_service,const std::string & native_host_name)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
NativeMessageProcessHost(base::WeakPtr<Client> weak_client_ui,const std::string & source_extension_id,const std::string & native_host_name,int destination_port,scoped_ptr<NativeProcessLauncher> launcher)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
~NativeMessageProcessHost()121 NativeMessageProcessHost::~NativeMessageProcessHost() {
122 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
123 Close(std::string());
124 }
125
126 // static
Create(gfx::NativeView native_view,base::WeakPtr<Client> weak_client_ui,const std::string & source_extension_id,const std::string & native_host_name,int destination_port,bool allow_user_level)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>
CreateWithLauncher(base::WeakPtr<Client> weak_client_ui,const std::string & source_extension_id,const std::string & native_host_name,int destination_port,scoped_ptr<NativeProcessLauncher> launcher)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
LaunchHostProcess()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
OnHostProcessLaunched(NativeProcessLauncher::LaunchResult result,base::ProcessHandle process_handle,base::File read_file,base::File write_file)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
Send(const std::string & json)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)
OnFileCanReadWithoutBlocking(int fd)234 void NativeMessageProcessHost::OnFileCanReadWithoutBlocking(int fd) {
235 DCHECK_EQ(fd, read_file_);
236 DoRead();
237 }
238
OnFileCanWriteWithoutBlocking(int fd)239 void NativeMessageProcessHost::OnFileCanWriteWithoutBlocking(int fd) {
240 NOTREACHED();
241 }
242 #endif // !defined(OS_POSIX)
243
ReadNowForTesting()244 void NativeMessageProcessHost::ReadNowForTesting() {
245 DoRead();
246 }
247
WaitRead()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
DoRead()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
OnRead(int result)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
HandleReadResult(int result)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
ProcessIncomingData(const char * data,int data_size)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
DoWrite()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
HandleWriteResult(int result)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
OnWritten(int result)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
Close(const std::string & error_message)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