Revert "Fix broken channel icon in chrome://help on CrOS" and try again
[chromium-blink-merge.git] / ipc / attachment_broker_privileged_win_unittest.cc
blob610921e1039a14202d91a8997e0e3bfb3ce6380f
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"
7 #include <windows.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"
22 namespace {
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);
29 char buffer[100];
30 DWORD bytes_read;
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.";
41 return nullptr;
44 if (attachment->GetBrokerableType() !=
45 IPC::BrokerableAttachment::WIN_HANDLE) {
46 LOG(INFO) << "Brokerable type not WIN_HANDLE.";
47 return nullptr;
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.";
61 return nullptr;
64 TestHandleWinMsg::Schema::Param p;
65 bool success = TestHandleWinMsg::Read(&message, &p);
66 if (!success) {
67 LOG(INFO) << "Failed to deserialize message.";
68 return nullptr;
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,
78 int index) {
79 // Expect a message with a brokered attachment.
80 if (!message.HasBrokerableAttachments()) {
81 LOG(INFO) << "Message missing brokerable attachment.";
82 return nullptr;
85 TestTwoHandleWinMsg::Schema::Param p;
86 bool success = TestTwoHandleWinMsg::Read(&message, &p);
87 if (!success) {
88 LOG(INFO) << "Failed to deserialize message.";
89 return nullptr;
92 IPC::HandleWin handle_win;
93 if (index == 0)
94 handle_win = base::get<0>(p);
95 else if (index == 1)
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);
104 if (h == nullptr) {
105 LOG(INFO) << "Failed to get handle from TestHandleWinMsg.";
106 return false;
109 std::string contents = ReadFromFile(h);
110 bool success = (contents == std::string(kDataBuffer));
111 if (!success) {
112 LOG(INFO) << "Expected contents: " << std::string(kDataBuffer);
113 LOG(INFO) << "Read contents: " << contents;
115 return success;
118 enum TestResult {
119 RESULT_UNKNOWN,
120 RESULT_SUCCESS,
121 RESULT_FAILURE,
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
126 // dispatched.
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 {
135 public:
136 void ReceivedBrokerableAttachmentWithId(
137 const IPC::BrokerableAttachment::AttachmentId& id) override {
138 id_ = id;
140 IPC::BrokerableAttachment::AttachmentId* get_id() { return &id_; }
142 private:
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 {
149 public:
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 {
157 bool result = false;
158 if (listener_)
159 result = listener_->OnMessageReceived(message);
160 reason_ = MESSAGE_RECEIVED;
161 messages_.push_back(message);
162 base::MessageLoop::current()->QuitNow();
163 return result;
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(); }
177 private:
178 IPC::Listener* listener_;
179 Reason reason_;
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 {
186 public:
187 ResultListener() : result_(RESULT_UNKNOWN) {}
188 ~ResultListener() override {}
190 bool OnMessageReceived(const IPC::Message& message) override {
191 base::PickleIterator iter(message);
193 int result;
194 EXPECT_TRUE(iter.ReadInt(&result));
195 result_ = static_cast<TestResult>(result);
196 return true;
199 TestResult get_result() { return result_; }
201 private:
202 TestResult result_;
205 // The parent process acts as an unprivileged process. The forked process acts
206 // as the privileged process.
207 class IPCAttachmentBrokerPrivilegedWinTest : public IPCTestBase {
208 public:
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);
225 void CommonSetUp() {
226 if (!broker_.get())
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.
238 channel()->Close();
240 EXPECT_TRUE(WaitForClientShutdown());
241 DestroyChannel();
242 broker_.reset();
245 HANDLE CreateTempFile() {
246 EXPECT_NE(-1, WriteFile(temp_path_, kDataBuffer,
247 static_cast<int>(strlen(kDataBuffer))));
249 HANDLE h =
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);
253 return h;
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_; }
266 private:
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
275 // for attachments.
276 class MockBroker : public IPC::AttachmentBrokerUnprivilegedWin {
277 public:
278 MockBroker() {}
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) {
292 Init("SendHandle");
294 CommonSetUp();
295 ResultListener result_listener;
296 get_proxy_listener()->set_listener(&result_listener);
298 HANDLE h = CreateTempFile();
299 SendMessageWithAttachment(h);
300 base::MessageLoop::current()->Run();
302 // Check the result.
303 ASSERT_EQ(ProxyListener::MESSAGE_RECEIVED,
304 get_proxy_listener()->get_reason());
305 ASSERT_EQ(result_listener.get_result(), RESULT_SUCCESS);
307 CommonTearDown();
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");
315 CommonSetUp();
316 ResultListener result_listener;
317 get_proxy_listener()->set_listener(&result_listener);
319 HANDLE h = CreateTempFile();
320 HANDLE h2;
321 BOOL result = ::DuplicateHandle(GetCurrentProcess(), h, GetCurrentProcess(),
322 &h2, 0, FALSE, DUPLICATE_CLOSE_SOURCE);
323 ASSERT_TRUE(result);
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();
329 // Check the result.
330 ASSERT_EQ(ProxyListener::MESSAGE_RECEIVED,
331 get_proxy_listener()->get_reason());
332 ASSERT_EQ(result_listener.get_result(), RESULT_SUCCESS);
334 CommonTearDown();
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);
344 CommonSetUp();
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);
362 EXPECT_NE(h2, h);
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));
369 CommonTearDown();
372 // Similar to SendHandle, but sends a message with the same handle twice.
373 TEST_F(IPCAttachmentBrokerPrivilegedWinTest, SendTwoHandles) {
374 Init("SendTwoHandles");
376 CommonSetUp();
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();
387 // Check the result.
388 ASSERT_EQ(ProxyListener::MESSAGE_RECEIVED,
389 get_proxy_listener()->get_reason());
390 ASSERT_EQ(result_listener.get_result(), RESULT_SUCCESS);
392 CommonTearDown();
395 // Similar to SendHandle, but sends the same message twice.
396 TEST_F(IPCAttachmentBrokerPrivilegedWinTest, SendHandleTwice) {
397 Init("SendHandleTwice");
399 CommonSetUp();
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();
408 // Check the result.
409 ASSERT_EQ(ProxyListener::MESSAGE_RECEIVED,
410 get_proxy_listener()->get_reason());
411 ASSERT_EQ(result_listener.get_result(), RESULT_SUCCESS);
413 CommonTearDown();
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());
432 while (true) {
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)
437 break;
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.";
448 return 0;
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);
463 if (h != nullptr) {
464 SetFilePointer(h, 0, nullptr, FILE_BEGIN);
466 char buffer[100];
467 DWORD bytes_read;
468 BOOL success =
469 ::ReadFile(h, buffer, static_cast<DWORD>(strlen(kDataBuffer)),
470 &bytes_read, nullptr);
471 if (!success && GetLastError() == ERROR_ACCESS_DENIED) {
472 SendControlMessage(sender, true);
473 return;
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,
492 "SendHandleToSelf");
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);
501 return;
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);
510 return;
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);
519 if (!success) {
520 SendControlMessage(sender, false);
521 return;
524 SetFilePointer(h2, 0, nullptr, FILE_BEGIN);
525 char buffer[100];
526 DWORD bytes_read;
527 success = ::ReadFile(h2, buffer, static_cast<DWORD>(strlen(text)),
528 &bytes_read, nullptr);
529 if (!success) {
530 SendControlMessage(sender, false);
531 return;
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.
543 static int i = 0;
544 static bool success = true;
545 success &= CheckContentsOfTestMessage(message);
546 if (i == 0) {
547 LOG(INFO) << "Received first message.";
548 ++i;
549 } else {
550 LOG(INFO) << "Received second message.";
551 SendControlMessage(sender, success);
555 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendHandleTwice) {
556 return CommonPrivilegedProcessMain(&SendHandleTwiceCallback,
557 "SendHandleTwice");
560 } // namespace