1 // Copyright 2015 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 "build/build_config.h"
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "ipc/attachment_broker_privileged_win.h"
14 #include "ipc/attachment_broker_unprivileged_win.h"
15 #include "ipc/handle_attachment_win.h"
16 #include "ipc/handle_win.h"
17 #include "ipc/ipc_listener.h"
18 #include "ipc/ipc_message.h"
19 #include "ipc/ipc_test_base.h"
20 #include "ipc/ipc_test_messages.h"
24 const char kDataBuffer
[] = "This is some test data to write to the file.";
26 // Returns the contents of the file represented by |h| as a std::string.
27 std::string
ReadFromFile(HANDLE h
) {
28 SetFilePointer(h
, 0, nullptr, FILE_BEGIN
);
31 BOOL success
= ::ReadFile(h
, buffer
, static_cast<DWORD
>(strlen(kDataBuffer
)),
32 &bytes_read
, nullptr);
33 return success
? std::string(buffer
, bytes_read
) : std::string();
36 HANDLE
GetHandleFromBrokeredAttachment(
37 const scoped_refptr
<IPC::BrokerableAttachment
>& attachment
) {
38 if (attachment
->GetType() !=
39 IPC::BrokerableAttachment::TYPE_BROKERABLE_ATTACHMENT
) {
40 LOG(INFO
) << "Attachment type not TYPE_BROKERABLE_ATTACHMENT.";
44 if (attachment
->GetBrokerableType() !=
45 IPC::BrokerableAttachment::WIN_HANDLE
) {
46 LOG(INFO
) << "Brokerable type not WIN_HANDLE.";
50 IPC::internal::HandleAttachmentWin
* received_handle_attachment
=
51 static_cast<IPC::internal::HandleAttachmentWin
*>(attachment
.get());
52 return received_handle_attachment
->get_handle();
55 // |message| must be deserializable as a TestHandleWinMsg. Returns the HANDLE,
56 // or nullptr if deserialization failed.
57 HANDLE
GetHandleFromTestHandleWinMsg(const IPC::Message
& message
) {
58 // Expect a message with a brokered attachment.
59 if (!message
.HasBrokerableAttachments()) {
60 LOG(INFO
) << "Message missing brokerable attachment.";
64 TestHandleWinMsg::Schema::Param p
;
65 bool success
= TestHandleWinMsg::Read(&message
, &p
);
67 LOG(INFO
) << "Failed to deserialize message.";
71 IPC::HandleWin handle_win
= base::get
<1>(p
);
72 return handle_win
.get_handle();
75 // |message| must be deserializable as a TestTwoHandleWinMsg. Returns the
76 // HANDLE, or nullptr if deserialization failed.
77 HANDLE
GetHandleFromTestTwoHandleWinMsg(const IPC::Message
& message
,
79 // Expect a message with a brokered attachment.
80 if (!message
.HasBrokerableAttachments()) {
81 LOG(INFO
) << "Message missing brokerable attachment.";
85 TestTwoHandleWinMsg::Schema::Param p
;
86 bool success
= TestTwoHandleWinMsg::Read(&message
, &p
);
88 LOG(INFO
) << "Failed to deserialize message.";
92 IPC::HandleWin handle_win
;
94 handle_win
= base::get
<0>(p
);
96 handle_win
= base::get
<1>(p
);
97 return handle_win
.get_handle();
100 // |message| must be deserializable as a TestHandleWinMsg. Returns true if the
101 // attached file HANDLE has contents |kDataBuffer|.
102 bool CheckContentsOfTestMessage(const IPC::Message
& message
) {
103 HANDLE h
= GetHandleFromTestHandleWinMsg(message
);
105 LOG(INFO
) << "Failed to get handle from TestHandleWinMsg.";
109 std::string contents
= ReadFromFile(h
);
110 bool success
= (contents
== std::string(kDataBuffer
));
112 LOG(INFO
) << "Expected contents: " << std::string(kDataBuffer
);
113 LOG(INFO
) << "Read contents: " << contents
;
124 // Once the test is finished, send a control message to the parent process with
125 // the result. The message may require the runloop to be run before its
127 void SendControlMessage(IPC::Sender
* sender
, bool success
) {
128 IPC::Message
* message
= new IPC::Message(0, 2, IPC::Message::PRIORITY_NORMAL
);
129 TestResult result
= success
? RESULT_SUCCESS
: RESULT_FAILURE
;
130 message
->WriteInt(result
);
131 sender
->Send(message
);
134 class MockObserver
: public IPC::AttachmentBroker::Observer
{
136 void ReceivedBrokerableAttachmentWithId(
137 const IPC::BrokerableAttachment::AttachmentId
& id
) override
{
140 IPC::BrokerableAttachment::AttachmentId
* get_id() { return &id_
; }
143 IPC::BrokerableAttachment::AttachmentId id_
;
146 // Forwards all messages to |listener_|. Quits the message loop after a
147 // message is received, or the channel has an error.
148 class ProxyListener
: public IPC::Listener
{
150 ProxyListener() : listener_(nullptr), reason_(MESSAGE_RECEIVED
) {}
151 ~ProxyListener() override
{}
153 // The reason for exiting the message loop.
154 enum Reason
{ MESSAGE_RECEIVED
, CHANNEL_ERROR
};
156 bool OnMessageReceived(const IPC::Message
& message
) override
{
159 result
= listener_
->OnMessageReceived(message
);
160 reason_
= MESSAGE_RECEIVED
;
161 messages_
.push_back(message
);
162 base::MessageLoop::current()->QuitNow();
166 void OnChannelError() override
{
167 reason_
= CHANNEL_ERROR
;
168 base::MessageLoop::current()->QuitNow();
171 void set_listener(IPC::Listener
* listener
) { listener_
= listener
; }
172 Reason
get_reason() { return reason_
; }
173 IPC::Message
get_first_message() { return messages_
[0]; }
174 void pop_first_message() { messages_
.erase(messages_
.begin()); }
175 bool has_message() { return !messages_
.empty(); }
178 IPC::Listener
* listener_
;
180 std::vector
<IPC::Message
> messages_
;
183 // Waits for a result to be sent over the channel. Quits the message loop
184 // after a message is received, or the channel has an error.
185 class ResultListener
: public IPC::Listener
{
187 ResultListener() : result_(RESULT_UNKNOWN
) {}
188 ~ResultListener() override
{}
190 bool OnMessageReceived(const IPC::Message
& message
) override
{
191 base::PickleIterator
iter(message
);
194 EXPECT_TRUE(iter
.ReadInt(&result
));
195 result_
= static_cast<TestResult
>(result
);
199 TestResult
get_result() { return result_
; }
205 // The parent process acts as an unprivileged process. The forked process acts
206 // as the privileged process.
207 class IPCAttachmentBrokerPrivilegedWinTest
: public IPCTestBase
{
209 IPCAttachmentBrokerPrivilegedWinTest() {}
210 ~IPCAttachmentBrokerPrivilegedWinTest() override
{}
212 void SetUp() override
{
213 IPCTestBase::SetUp();
214 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
215 ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_
.path(), &temp_path_
));
218 void TearDown() override
{ IPCTestBase::TearDown(); }
220 // Takes ownership of |broker|. Has no effect if called after CommonSetUp().
221 void set_broker(IPC::AttachmentBrokerUnprivilegedWin
* broker
) {
222 broker_
.reset(broker
);
227 set_broker(new IPC::AttachmentBrokerUnprivilegedWin
);
228 broker_
->AddObserver(&observer_
);
229 set_attachment_broker(broker_
.get());
230 CreateChannel(&proxy_listener_
);
231 broker_
->DesignateBrokerCommunicationChannel(channel());
232 ASSERT_TRUE(ConnectChannel());
233 ASSERT_TRUE(StartClient());
236 void CommonTearDown() {
237 // Close the channel so the client's OnChannelError() gets fired.
240 EXPECT_TRUE(WaitForClientShutdown());
245 HANDLE
CreateTempFile() {
246 EXPECT_NE(-1, WriteFile(temp_path_
, kDataBuffer
,
247 static_cast<int>(strlen(kDataBuffer
))));
250 CreateFile(temp_path_
.value().c_str(), GENERIC_READ
| GENERIC_WRITE
, 0,
251 nullptr, OPEN_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, nullptr);
252 EXPECT_NE(h
, INVALID_HANDLE_VALUE
);
256 void SendMessageWithAttachment(HANDLE h
) {
257 IPC::HandleWin
handle_win(h
, IPC::HandleWin::FILE_READ_WRITE
);
258 IPC::Message
* message
= new TestHandleWinMsg(100, handle_win
, 200);
259 sender()->Send(message
);
262 ProxyListener
* get_proxy_listener() { return &proxy_listener_
; }
263 IPC::AttachmentBrokerUnprivilegedWin
* get_broker() { return broker_
.get(); }
264 MockObserver
* get_observer() { return &observer_
; }
267 base::ScopedTempDir temp_dir_
;
268 base::FilePath temp_path_
;
269 ProxyListener proxy_listener_
;
270 scoped_ptr
<IPC::AttachmentBrokerUnprivilegedWin
> broker_
;
271 MockObserver observer_
;
274 // A broker which always sets the current process as the destination process
276 class MockBroker
: public IPC::AttachmentBrokerUnprivilegedWin
{
279 ~MockBroker() override
{}
280 bool SendAttachmentToProcess(const IPC::BrokerableAttachment
* attachment
,
281 base::ProcessId destination_process
) override
{
282 return IPC::AttachmentBrokerUnprivilegedWin::SendAttachmentToProcess(
283 attachment
, base::Process::Current().Pid());
287 // An unprivileged process makes a file HANDLE, and writes a string to it. The
288 // file HANDLE is sent to the privileged process using the attachment broker.
289 // The privileged process dups the HANDLE into its own HANDLE table. This test
290 // checks that the file has the same contents in the privileged process.
291 TEST_F(IPCAttachmentBrokerPrivilegedWinTest
, SendHandle
) {
295 ResultListener result_listener
;
296 get_proxy_listener()->set_listener(&result_listener
);
298 HANDLE h
= CreateTempFile();
299 SendMessageWithAttachment(h
);
300 base::MessageLoop::current()->Run();
303 ASSERT_EQ(ProxyListener::MESSAGE_RECEIVED
,
304 get_proxy_listener()->get_reason());
305 ASSERT_EQ(result_listener
.get_result(), RESULT_SUCCESS
);
310 // Similar to SendHandle, except the file HANDLE attached to the message has
311 // neither read nor write permissions.
312 TEST_F(IPCAttachmentBrokerPrivilegedWinTest
, SendHandleWithoutPermissions
) {
313 Init("SendHandleWithoutPermissions");
316 ResultListener result_listener
;
317 get_proxy_listener()->set_listener(&result_listener
);
319 HANDLE h
= CreateTempFile();
321 BOOL result
= ::DuplicateHandle(GetCurrentProcess(), h
, GetCurrentProcess(),
322 &h2
, 0, FALSE
, DUPLICATE_CLOSE_SOURCE
);
324 IPC::HandleWin
handle_win(h2
, IPC::HandleWin::DUPLICATE
);
325 IPC::Message
* message
= new TestHandleWinMsg(100, handle_win
, 200);
326 sender()->Send(message
);
327 base::MessageLoop::current()->Run();
330 ASSERT_EQ(ProxyListener::MESSAGE_RECEIVED
,
331 get_proxy_listener()->get_reason());
332 ASSERT_EQ(result_listener
.get_result(), RESULT_SUCCESS
);
337 // Similar to SendHandle, except the attachment's destination process is this
338 // process. This is an unrealistic scenario, but simulates an unprivileged
339 // process sending an attachment to another unprivileged process.
340 TEST_F(IPCAttachmentBrokerPrivilegedWinTest
, SendHandleToSelf
) {
341 Init("SendHandleToSelf");
343 set_broker(new MockBroker
);
345 // Technically, the channel is an endpoint, but we need the proxy listener to
346 // receive the messages so that it can quit the message loop.
347 channel()->SetAttachmentBrokerEndpoint(false);
348 get_proxy_listener()->set_listener(get_broker());
350 HANDLE h
= CreateTempFile();
351 SendMessageWithAttachment(h
);
352 base::MessageLoop::current()->Run();
354 // Get the received attachment.
355 IPC::BrokerableAttachment::AttachmentId
* id
= get_observer()->get_id();
356 scoped_refptr
<IPC::BrokerableAttachment
> received_attachment
;
357 get_broker()->GetAttachmentWithId(*id
, &received_attachment
);
358 ASSERT_NE(received_attachment
.get(), nullptr);
360 // Check that it's a new entry in the HANDLE table.
361 HANDLE h2
= GetHandleFromBrokeredAttachment(received_attachment
);
363 EXPECT_NE(h2
, nullptr);
365 // But it still points to the same file.
366 std::string contents
= ReadFromFile(h
);
367 EXPECT_EQ(contents
, std::string(kDataBuffer
));
372 // Similar to SendHandle, but sends a message with the same handle twice.
373 TEST_F(IPCAttachmentBrokerPrivilegedWinTest
, SendTwoHandles
) {
374 Init("SendTwoHandles");
377 ResultListener result_listener
;
378 get_proxy_listener()->set_listener(&result_listener
);
380 HANDLE h
= CreateTempFile();
381 IPC::HandleWin
handle_win1(h
, IPC::HandleWin::FILE_READ_WRITE
);
382 IPC::HandleWin
handle_win2(h
, IPC::HandleWin::FILE_READ_WRITE
);
383 IPC::Message
* message
= new TestTwoHandleWinMsg(handle_win1
, handle_win2
);
384 sender()->Send(message
);
385 base::MessageLoop::current()->Run();
388 ASSERT_EQ(ProxyListener::MESSAGE_RECEIVED
,
389 get_proxy_listener()->get_reason());
390 ASSERT_EQ(result_listener
.get_result(), RESULT_SUCCESS
);
395 // Similar to SendHandle, but sends the same message twice.
396 TEST_F(IPCAttachmentBrokerPrivilegedWinTest
, SendHandleTwice
) {
397 Init("SendHandleTwice");
400 ResultListener result_listener
;
401 get_proxy_listener()->set_listener(&result_listener
);
403 HANDLE h
= CreateTempFile();
404 SendMessageWithAttachment(h
);
405 SendMessageWithAttachment(h
);
406 base::MessageLoop::current()->Run();
409 ASSERT_EQ(ProxyListener::MESSAGE_RECEIVED
,
410 get_proxy_listener()->get_reason());
411 ASSERT_EQ(result_listener
.get_result(), RESULT_SUCCESS
);
416 using OnMessageReceivedCallback
= void (*)(IPC::Sender
* sender
,
417 const IPC::Message
& message
);
419 int CommonPrivilegedProcessMain(OnMessageReceivedCallback callback
,
420 const char* channel_name
) {
421 LOG(INFO
) << "Privileged process start.";
422 base::MessageLoopForIO main_message_loop
;
423 ProxyListener listener
;
425 // Set up IPC channel.
426 IPC::AttachmentBrokerPrivilegedWin broker
;
427 scoped_ptr
<IPC::Channel
> channel(IPC::Channel::CreateClient(
428 IPCTestBase::GetChannelName(channel_name
), &listener
, &broker
));
429 broker
.RegisterCommunicationChannel(channel
.get());
430 CHECK(channel
->Connect());
433 LOG(INFO
) << "Privileged process spinning run loop.";
434 base::MessageLoop::current()->Run();
435 ProxyListener::Reason reason
= listener
.get_reason();
436 if (reason
== ProxyListener::CHANNEL_ERROR
)
439 while (listener
.has_message()) {
440 LOG(INFO
) << "Privileged process running callback.";
441 callback(channel
.get(), listener
.get_first_message());
442 LOG(INFO
) << "Privileged process finishing callback.";
443 listener
.pop_first_message();
447 LOG(INFO
) << "Privileged process end.";
451 void SendHandleCallback(IPC::Sender
* sender
, const IPC::Message
& message
) {
452 bool success
= CheckContentsOfTestMessage(message
);
453 SendControlMessage(sender
, success
);
456 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendHandle
) {
457 return CommonPrivilegedProcessMain(&SendHandleCallback
, "SendHandle");
460 void SendHandleWithoutPermissionsCallback(IPC::Sender
* sender
,
461 const IPC::Message
& message
) {
462 HANDLE h
= GetHandleFromTestHandleWinMsg(message
);
464 SetFilePointer(h
, 0, nullptr, FILE_BEGIN
);
469 ::ReadFile(h
, buffer
, static_cast<DWORD
>(strlen(kDataBuffer
)),
470 &bytes_read
, nullptr);
471 if (!success
&& GetLastError() == ERROR_ACCESS_DENIED
) {
472 SendControlMessage(sender
, true);
477 SendControlMessage(sender
, false);
480 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendHandleWithoutPermissions
) {
481 return CommonPrivilegedProcessMain(&SendHandleWithoutPermissionsCallback
,
482 "SendHandleWithoutPermissions");
485 void SendHandleToSelfCallback(IPC::Sender
* sender
, const IPC::Message
&) {
486 // Do nothing special. The default behavior already runs the
487 // AttachmentBrokerPrivilegedWin.
490 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendHandleToSelf
) {
491 return CommonPrivilegedProcessMain(&SendHandleToSelfCallback
,
495 void SendTwoHandlesCallback(IPC::Sender
* sender
, const IPC::Message
& message
) {
496 // Check for two handles.
497 HANDLE h1
= GetHandleFromTestTwoHandleWinMsg(message
, 0);
498 HANDLE h2
= GetHandleFromTestTwoHandleWinMsg(message
, 1);
499 if (h1
== nullptr || h2
== nullptr) {
500 SendControlMessage(sender
, false);
504 // Check that their contents are correct.
505 std::string contents1
= ReadFromFile(h1
);
506 std::string contents2
= ReadFromFile(h2
);
507 if (contents1
!= std::string(kDataBuffer
) ||
508 contents2
!= std::string(kDataBuffer
)) {
509 SendControlMessage(sender
, false);
513 // Check that the handles point to the same file.
514 const char text
[] = "katy perry";
515 DWORD bytes_written
= 0;
516 SetFilePointer(h1
, 0, nullptr, FILE_BEGIN
);
517 BOOL success
= ::WriteFile(h1
, text
, static_cast<DWORD
>(strlen(text
)),
518 &bytes_written
, nullptr);
520 SendControlMessage(sender
, false);
524 SetFilePointer(h2
, 0, nullptr, FILE_BEGIN
);
527 success
= ::ReadFile(h2
, buffer
, static_cast<DWORD
>(strlen(text
)),
528 &bytes_read
, nullptr);
530 SendControlMessage(sender
, false);
533 success
= std::string(buffer
, bytes_read
) == std::string(text
);
534 SendControlMessage(sender
, success
);
537 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendTwoHandles
) {
538 return CommonPrivilegedProcessMain(&SendTwoHandlesCallback
, "SendTwoHandles");
541 void SendHandleTwiceCallback(IPC::Sender
* sender
, const IPC::Message
& message
) {
542 // We expect the same message twice.
544 static bool success
= true;
545 success
&= CheckContentsOfTestMessage(message
);
547 LOG(INFO
) << "Received first message.";
550 LOG(INFO
) << "Received second message.";
551 SendControlMessage(sender
, success
);
555 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendHandleTwice
) {
556 return CommonPrivilegedProcessMain(&SendHandleTwiceCallback
,