blob: 4e2818de72516323207a76625a5d6964f41db325 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/host/security_key/security_key_auth_handler.h"
#include <cstdint>
#include <memory>
#include <string>
#include "base/bind.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "ipc/ipc_channel.h"
#include "ipc/ipc_listener.h"
#include "ipc/ipc_message.h"
#include "ipc/ipc_message_macros.h"
#include "remoting/host/host_mock_objects.h"
#include "remoting/host/security_key/fake_security_key_ipc_client.h"
#include "remoting/host/security_key/fake_security_key_ipc_server.h"
#include "remoting/host/security_key/security_key_ipc_constants.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
const int kConnectionId1 = 1;
const int kConnectionId2 = 2;
} // namespace
namespace remoting {
class SecurityKeyAuthHandlerWinTest : public testing::Test {
public:
SecurityKeyAuthHandlerWinTest();
~SecurityKeyAuthHandlerWinTest() override;
// Passed to the object used for testing to be called back to signal
// completion of an IPC channel state change or reception of an IPC message.
void OperationComplete();
protected:
// Waits until the current |run_loop_| instance is signaled, then resets it.
void WaitForOperationComplete();
// Used as a callback given to the object under test, expected to be called
// back when a security key request is received by it.
void SendMessageToClient(int connection_id, const std::string& data);
// Creates a new security key connection on the object under test.
void CreateSecurityKeyConnection(
const mojo::NamedPlatformChannel::ServerName& server_name);
// Uses |fake_ipc_client| to connect to the IPC server channel, it then
// validates internal state of the object under test and closes the connection
// based on |close_connection|.
void EstablishIpcConnection(
FakeSecurityKeyIpcClient* fake_ipc_client,
int expected_connection_id,
const mojo::NamedPlatformChannel::ServerName& server_name,
bool close_connection);
// Sends a security key response message using |fake_ipc_server| and
// validates the state of the object under test.
void SendRequestToSecurityKeyAuthHandler(
const base::WeakPtr<FakeSecurityKeyIpcServer>& fake_ipc_server,
int connection_id,
const std::string& request_payload);
// Sends a security key response message to |fake_ipc_server| and validates
// the state of the object under test.
void SendResponseViaSecurityKeyAuthHandler(
const base::WeakPtr<FakeSecurityKeyIpcServer>& fake_ipc_server,
int connection_id,
const std::string& response_payload);
// Closes a security key session IPC channel and validates state.
void CloseSecurityKeySessionIpcChannel(
const base::WeakPtr<FakeSecurityKeyIpcServer>& fake_ipc_server,
int connection_id);
// Returns a unique IPC channel handle which prevents conflicts when running
// tests concurrently.
mojo::NamedPlatformChannel::ServerName GetUniqueTestChannelHandle();
// IPC tests require a valid MessageLoop to run.
base::MessageLoopForIO message_loop_;
// Used to allow |message_loop_| to run during tests. The instance is reset
// after each stage of the tests has been completed.
std::unique_ptr<base::RunLoop> run_loop_;
// The object under test.
std::unique_ptr<SecurityKeyAuthHandler> auth_handler_;
// Set as the default factory to create SecurityKeyIpcServerFactory
// instances, this class will track each objects creation and allow the tests
// to access it and use it for driving tests and validating state.
FakeSecurityKeyIpcServerFactory ipc_server_factory_;
// Used to validate the object under test uses the correct ID when
// communicating over the IPC channel.
int last_connection_id_received_ = -1;
// Stores the contents of the last IPC message received for validation.
std::string last_message_received_;
private:
testing::NiceMock<MockClientSessionDetails> mock_client_session_details_;
DISALLOW_COPY_AND_ASSIGN(SecurityKeyAuthHandlerWinTest);
};
SecurityKeyAuthHandlerWinTest::SecurityKeyAuthHandlerWinTest()
: run_loop_(new base::RunLoop()) {
auth_handler_ = remoting::SecurityKeyAuthHandler::Create(
&mock_client_session_details_,
base::Bind(&SecurityKeyAuthHandlerWinTest::SendMessageToClient,
base::Unretained(this)),
/*file_task_runner=*/nullptr);
}
SecurityKeyAuthHandlerWinTest::~SecurityKeyAuthHandlerWinTest() {}
void SecurityKeyAuthHandlerWinTest::OperationComplete() {
run_loop_->Quit();
}
void SecurityKeyAuthHandlerWinTest::WaitForOperationComplete() {
run_loop_->Run();
run_loop_.reset(new base::RunLoop());
}
void SecurityKeyAuthHandlerWinTest::SendMessageToClient(
int connection_id,
const std::string& data) {
last_connection_id_received_ = connection_id;
last_message_received_ = data;
OperationComplete();
}
void SecurityKeyAuthHandlerWinTest::CreateSecurityKeyConnection(
const mojo::NamedPlatformChannel::ServerName& server_name) {
ASSERT_EQ(0u, auth_handler_->GetActiveConnectionCountForTest());
remoting::SetSecurityKeyIpcChannelForTest(server_name);
// Create a new SecurityKey IPC Server connection.
auth_handler_->CreateSecurityKeyConnection();
}
void SecurityKeyAuthHandlerWinTest::EstablishIpcConnection(
FakeSecurityKeyIpcClient* fake_ipc_client,
int expected_connection_id,
const mojo::NamedPlatformChannel::ServerName& server_name,
bool close_connection) {
size_t expected_connection_count =
auth_handler_->GetActiveConnectionCountForTest() + 1;
ASSERT_FALSE(auth_handler_->IsValidConnectionId(expected_connection_id));
fake_ipc_client->set_on_channel_connected_callback(
base::Bind(&SecurityKeyAuthHandlerWinTest::OperationComplete,
base::Unretained(this)));
ASSERT_TRUE(fake_ipc_client->ConnectViaIpc(server_name));
WaitForOperationComplete();
// Retrieve the IPC server instance created when the client connected.
base::WeakPtr<FakeSecurityKeyIpcServer> fake_ipc_server =
ipc_server_factory_.GetIpcServerObject(expected_connection_id);
ASSERT_TRUE(fake_ipc_server.get());
fake_ipc_server->SendConnectionReadyMessage();
WaitForOperationComplete();
// Verify the internal state of the SecurityKeyAuthHandler is correct.
ASSERT_TRUE(auth_handler_->IsValidConnectionId(expected_connection_id));
ASSERT_EQ(expected_connection_count,
auth_handler_->GetActiveConnectionCountForTest());
if (close_connection) {
fake_ipc_client->CloseIpcConnection();
WaitForOperationComplete();
}
}
void SecurityKeyAuthHandlerWinTest::SendRequestToSecurityKeyAuthHandler(
const base::WeakPtr<FakeSecurityKeyIpcServer>& fake_ipc_server,
int connection_id,
const std::string& request_payload) {
size_t expected_connection_count =
auth_handler_->GetActiveConnectionCountForTest();
// Send a security key request using the fake IPC server.
fake_ipc_server->SendRequest(request_payload);
WaitForOperationComplete();
// Verify the FakeSecurityKeyIpcServer instance was not destroyed.
ASSERT_TRUE(fake_ipc_server.get());
// Verify the request was received.
ASSERT_EQ(connection_id, last_connection_id_received_);
ASSERT_EQ(request_payload, last_message_received_);
// Verify the internal state of the SecurityKeyAuthHandler is still correct.
ASSERT_TRUE(auth_handler_->IsValidConnectionId(connection_id));
ASSERT_EQ(expected_connection_count,
auth_handler_->GetActiveConnectionCountForTest());
}
void SecurityKeyAuthHandlerWinTest::SendResponseViaSecurityKeyAuthHandler(
const base::WeakPtr<FakeSecurityKeyIpcServer>& fake_ipc_server,
int connection_id,
const std::string& response_payload) {
size_t expected_connection_count =
auth_handler_->GetActiveConnectionCountForTest();
// Send a security key response using the new IPC channel.
auth_handler_->SendClientResponse(connection_id, response_payload);
WaitForOperationComplete();
// Verify the security key response was received.
ASSERT_EQ(response_payload, fake_ipc_server->last_message_received());
// Verify the internal state of the SecurityKeyAuthHandler is still correct.
ASSERT_TRUE(auth_handler_->IsValidConnectionId(connection_id));
ASSERT_EQ(expected_connection_count,
auth_handler_->GetActiveConnectionCountForTest());
}
void SecurityKeyAuthHandlerWinTest::CloseSecurityKeySessionIpcChannel(
const base::WeakPtr<FakeSecurityKeyIpcServer>& fake_ipc_server,
int connection_id) {
size_t expected_connection_count =
auth_handler_->GetActiveConnectionCountForTest() - 1;
fake_ipc_server->CloseChannel();
// Verify the internal state has been updated.
ASSERT_FALSE(auth_handler_->IsValidConnectionId(connection_id));
ASSERT_EQ(expected_connection_count,
auth_handler_->GetActiveConnectionCountForTest());
// Verify the FakeSecurityKeyIpcServer instance was destroyed.
ASSERT_FALSE(fake_ipc_server.get());
}
mojo::NamedPlatformChannel::ServerName
SecurityKeyAuthHandlerWinTest::GetUniqueTestChannelHandle() {
std::string channel_name("Uber_Awesome_Super_Mega_Test_Channel.");
channel_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
return mojo::NamedPlatformChannel::ServerNameFromUTF8(channel_name);
}
TEST_F(SecurityKeyAuthHandlerWinTest, HandleSingleSecurityKeyRequest) {
mojo::NamedPlatformChannel::ServerName server_name =
GetUniqueTestChannelHandle();
CreateSecurityKeyConnection(server_name);
ASSERT_FALSE(auth_handler_->IsValidConnectionId(kConnectionId1));
// Create a fake client and connect to the IPC server channel.
FakeSecurityKeyIpcClient fake_ipc_client(
base::Bind(&SecurityKeyAuthHandlerWinTest::OperationComplete,
base::Unretained(this)));
EstablishIpcConnection(&fake_ipc_client, kConnectionId1, server_name,
/*close_connection=*/true);
// Retrieve the IPC server instance created when the client connected.
base::WeakPtr<FakeSecurityKeyIpcServer> fake_ipc_server =
ipc_server_factory_.GetIpcServerObject(kConnectionId1);
ASSERT_TRUE(fake_ipc_server.get());
fake_ipc_server->set_send_response_callback(
base::Bind(&SecurityKeyAuthHandlerWinTest::OperationComplete,
base::Unretained(this)));
// Send a security key request using the fake IPC server.
SendRequestToSecurityKeyAuthHandler(fake_ipc_server, kConnectionId1,
"0123456789");
// Send a security key response using the new IPC channel.
SendResponseViaSecurityKeyAuthHandler(fake_ipc_server, kConnectionId1,
"9876543210");
CloseSecurityKeySessionIpcChannel(fake_ipc_server, kConnectionId1);
}
TEST_F(SecurityKeyAuthHandlerWinTest, HandleConcurrentSecurityKeyRequests) {
mojo::NamedPlatformChannel::ServerName server_name =
GetUniqueTestChannelHandle();
CreateSecurityKeyConnection(server_name);
// Create fake clients and connect each to the IPC server channel.
FakeSecurityKeyIpcClient fake_ipc_client_1(
base::Bind(&SecurityKeyAuthHandlerWinTest::OperationComplete,
base::Unretained(this)));
FakeSecurityKeyIpcClient fake_ipc_client_2(
base::Bind(&SecurityKeyAuthHandlerWinTest::OperationComplete,
base::Unretained(this)));
EstablishIpcConnection(&fake_ipc_client_1, kConnectionId1, server_name,
/*close_connection=*/true);
EstablishIpcConnection(&fake_ipc_client_2, kConnectionId2, server_name,
/*close_connection=*/true);
base::WeakPtr<FakeSecurityKeyIpcServer> fake_ipc_server_1 =
ipc_server_factory_.GetIpcServerObject(kConnectionId1);
ASSERT_TRUE(fake_ipc_server_1.get());
base::WeakPtr<FakeSecurityKeyIpcServer> fake_ipc_server_2 =
ipc_server_factory_.GetIpcServerObject(kConnectionId2);
ASSERT_TRUE(fake_ipc_server_2.get());
fake_ipc_server_1->set_send_response_callback(
base::Bind(&SecurityKeyAuthHandlerWinTest::OperationComplete,
base::Unretained(this)));
fake_ipc_server_2->set_send_response_callback(
base::Bind(&SecurityKeyAuthHandlerWinTest::OperationComplete,
base::Unretained(this)));
// Connect and send a security key request using the first IPC channel.
SendRequestToSecurityKeyAuthHandler(fake_ipc_server_1, kConnectionId1,
"aaaaaaaaaa");
// Send a security key request using the second IPC channel.
SendRequestToSecurityKeyAuthHandler(fake_ipc_server_1, kConnectionId1,
"bbbbbbbbbb");
// Send a security key response using the first IPC channel.
SendResponseViaSecurityKeyAuthHandler(fake_ipc_server_2, kConnectionId2,
"cccccccccc");
// Send a security key response using the second IPC channel.
SendResponseViaSecurityKeyAuthHandler(fake_ipc_server_2, kConnectionId2,
"dddddddddd");
// Close the IPC channels.
CloseSecurityKeySessionIpcChannel(fake_ipc_server_1, kConnectionId1);
CloseSecurityKeySessionIpcChannel(fake_ipc_server_2, kConnectionId2);
}
TEST_F(SecurityKeyAuthHandlerWinTest, HandleSequentialSecurityKeyRequests) {
mojo::NamedPlatformChannel::ServerName server_name =
GetUniqueTestChannelHandle();
CreateSecurityKeyConnection(server_name);
// Create fake clients to connect to the IPC server channel.
FakeSecurityKeyIpcClient fake_ipc_client_1(
base::Bind(&SecurityKeyAuthHandlerWinTest::OperationComplete,
base::Unretained(this)));
EstablishIpcConnection(&fake_ipc_client_1, kConnectionId1, server_name,
/*close_connection=*/true);
base::WeakPtr<FakeSecurityKeyIpcServer> fake_ipc_server_1 =
ipc_server_factory_.GetIpcServerObject(kConnectionId1);
ASSERT_TRUE(fake_ipc_server_1.get());
fake_ipc_server_1->set_send_response_callback(
base::Bind(&SecurityKeyAuthHandlerWinTest::OperationComplete,
base::Unretained(this)));
// Send a security key request using the first IPC channel.
SendRequestToSecurityKeyAuthHandler(fake_ipc_server_1, kConnectionId1,
"aaaaaaaaaa");
// Send a security key response using the first IPC channel.
SendResponseViaSecurityKeyAuthHandler(fake_ipc_server_1, kConnectionId1,
"cccccccccc");
// Close the IPC channel.
CloseSecurityKeySessionIpcChannel(fake_ipc_server_1, kConnectionId1);
// Now connect with a second client.
FakeSecurityKeyIpcClient fake_ipc_client_2(
base::Bind(&SecurityKeyAuthHandlerWinTest::OperationComplete,
base::Unretained(this)));
EstablishIpcConnection(&fake_ipc_client_2, kConnectionId2, server_name,
/*close_connection=*/true);
base::WeakPtr<FakeSecurityKeyIpcServer> fake_ipc_server_2 =
ipc_server_factory_.GetIpcServerObject(kConnectionId2);
ASSERT_TRUE(fake_ipc_server_2.get());
fake_ipc_server_2->set_send_response_callback(
base::Bind(&SecurityKeyAuthHandlerWinTest::OperationComplete,
base::Unretained(this)));
// Send a security key request using the second IPC channel.
SendRequestToSecurityKeyAuthHandler(fake_ipc_server_2, kConnectionId2,
"bbbbbbbbbb");
// Send a security key response using the second IPC channel.
SendResponseViaSecurityKeyAuthHandler(fake_ipc_server_2, kConnectionId2,
"dddddddddd");
// Close the IPC channel.
CloseSecurityKeySessionIpcChannel(fake_ipc_server_2, kConnectionId2);
}
TEST_F(SecurityKeyAuthHandlerWinTest, HandleSecurityKeyErrorResponse) {
mojo::NamedPlatformChannel::ServerName server_name =
GetUniqueTestChannelHandle();
CreateSecurityKeyConnection(server_name);
ASSERT_EQ(0u, auth_handler_->GetActiveConnectionCountForTest());
// Create a fake client and connect to the IPC server channel.
FakeSecurityKeyIpcClient fake_ipc_client(
base::Bind(&SecurityKeyAuthHandlerWinTest::OperationComplete,
base::Unretained(this)));
EstablishIpcConnection(&fake_ipc_client, kConnectionId1, server_name,
/*close_connection=*/true);
// Retrieve the IPC server instance created when the client connected.
base::WeakPtr<FakeSecurityKeyIpcServer> fake_ipc_server =
ipc_server_factory_.GetIpcServerObject(kConnectionId1);
ASSERT_TRUE(fake_ipc_server.get());
fake_ipc_server->set_send_response_callback(
base::Bind(&SecurityKeyAuthHandlerWinTest::OperationComplete,
base::Unretained(this)));
// Send a security key request using the fake IPC server.
SendRequestToSecurityKeyAuthHandler(fake_ipc_server, kConnectionId1,
"0123456789");
// Simulate a security key error from the client.
auth_handler_->SendErrorAndCloseConnection(kConnectionId1);
// Wait for the ipc server channel to be torn down.
WaitForOperationComplete();
// Verify the connection was cleaned up.
ASSERT_FALSE(fake_ipc_server.get());
ASSERT_FALSE(auth_handler_->IsValidConnectionId(kConnectionId1));
ASSERT_EQ(0u, auth_handler_->GetActiveConnectionCountForTest());
// Attempt to connect again after the error.
EstablishIpcConnection(&fake_ipc_client, kConnectionId2, server_name,
/*close_connection=*/true);
}
} // namespace remoting