blob: f0f3f654be24ad1d9d5c2e989553fd742633e680 [file] [log] [blame]
// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Unit tests for Service and ServiceMonolithic
#include "cryptohome/service_monolithic.h"
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <base/at_exit.h>
#include <base/files/file_util.h>
#include <base/strings/sys_string_conversions.h>
#include <base/threading/platform_thread.h>
#include <base/time/time.h>
#include <brillo/bind_lambda.h>
#include <brillo/cryptohome.h>
#include <brillo/secure_blob.h>
#include <chaps/token_manager_client_mock.h>
#include <chromeos/constants/cryptohome.h>
#include <glib-object.h>
#include <gtest/gtest.h>
#include <policy/libpolicy.h>
#include <policy/mock_device_policy.h>
#include "cryptohome/crypto.h"
#include "cryptohome/interface.h"
#include "cryptohome/make_tests.h"
#include "cryptohome/mock_attestation.h"
#include "cryptohome/mock_boot_attributes.h"
#include "cryptohome/mock_boot_lockbox.h"
#include "cryptohome/mock_crypto.h"
#include "cryptohome/mock_dbus_transition.h"
#include "cryptohome/mock_firmware_management_parameters.h"
#include "cryptohome/mock_homedirs.h"
#include "cryptohome/mock_install_attributes.h"
#include "cryptohome/mock_mount.h"
#include "cryptohome/mock_mount_factory.h"
#include "cryptohome/mock_platform.h"
#include "cryptohome/mock_tpm.h"
#include "cryptohome/mock_tpm_init.h"
#include "cryptohome/mock_vault_keyset.h"
#include "cryptohome/user_oldest_activity_timestamp_cache.h"
#include "cryptohome/username_passkey.h"
using ::testing::DoAll;
using ::testing::EndsWith;
using ::testing::Invoke;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::SaveArgPointee;
using ::testing::SetArgPointee;
using ::testing::StrEq;
using ::testing::WithArgs;
using ::testing::_;
using base::FilePath;
using base::PlatformThread;
using brillo::SecureBlob;
namespace {
const FilePath kImageDir("test_image_dir");
const FilePath kSaltFile("test_image_dir/salt");
// Keep in sync with service.cc
const int64_t kNotifyDiskSpaceThreshold = 1 << 30;
class FakeEventSourceSink : public cryptohome::CryptohomeEventSourceSink {
public:
FakeEventSourceSink() = default;
~FakeEventSourceSink() override = default;
void NotifyEvent(cryptohome::CryptohomeEventBase* result) override {
if (strcmp(result->GetEventName(),
cryptohome::kMountTaskResultEventType)) {
return;
}
cryptohome::MountTaskResult* r =
static_cast<cryptohome::MountTaskResult*>(result);
completed_tasks_.push_back(*r);
}
std::vector<cryptohome::MountTaskResult> completed_tasks_;
};
bool AssignSalt(size_t size, SecureBlob* salt) {
SecureBlob fake_salt(size, 'S');
salt->swap(fake_salt);
return true;
}
} // namespace
namespace cryptohome {
// Tests that need to do more setup work before calling Service::Initialize can
// use this instead of ServiceTest.
class ServiceTestNotInitialized : public ::testing::Test {
public:
ServiceTestNotInitialized() = default;
~ServiceTestNotInitialized() override = default;
void SetUp() override {
service_.set_crypto(&crypto_);
service_.set_attestation(&attest_);
service_.set_homedirs(&homedirs_);
service_.set_install_attrs(&attrs_);
service_.set_initialize_tpm(false);
service_.set_use_tpm(false);
service_.set_platform(&platform_);
service_.set_chaps_client(&chaps_client_);
service_.set_boot_lockbox(&lockbox_);
service_.set_boot_attributes(&boot_attributes_);
service_.set_firmware_management_parameters(&fwmp_);
service_.set_reply_factory(&reply_factory_);
service_.set_event_source_sink(&event_sink_);
test_helper_.SetUpSystemSalt();
ON_CALL(homedirs_, FreeDiskSpace()).WillByDefault(Return(true));
ON_CALL(homedirs_, Init(_, _, _)).WillByDefault(Return(true));
ON_CALL(homedirs_, AmountOfFreeDiskSpace()).WillByDefault(
Return(kNotifyDiskSpaceThreshold));
ON_CALL(boot_attributes_, Load()).WillByDefault(Return(true));
// Empty token list by default. The effect is that there are no attempts
// to unload tokens unless a test explicitly sets up the token list.
ON_CALL(chaps_client_, GetTokenList(_, _)).WillByDefault(Return(true));
// Skip CleanUpStaleMounts by default.
ON_CALL(platform_, GetMountsBySourcePrefix(_, _))
.WillByDefault(Return(false));
// Setup fake salt by default.
ON_CALL(crypto_, GetOrCreateSalt(_, _, _, _))
.WillByDefault(WithArgs<1, 3>(Invoke(AssignSalt)));
// Skip StatefulRecovery by default.
ON_CALL(platform_,
ReadFileToString(
Property(&FilePath::value, EndsWith("decrypt_stateful")),
_))
.WillByDefault(Return(false));
}
void SetupMount(const std::string& username) {
mount_ = new NiceMock<MockMount>();
service_.set_mount_for_user(username, mount_.get());
}
void TearDown() override {
test_helper_.TearDownSystemSalt();
}
protected:
MakeTests test_helper_;
NiceMock<MockCrypto> crypto_;
NiceMock<MockAttestation> attest_;
NiceMock<MockHomeDirs> homedirs_;
NiceMock<MockInstallAttributes> attrs_;
NiceMock<MockBootLockbox> lockbox_;
NiceMock<MockBootAttributes> boot_attributes_;
NiceMock<MockFirmwareManagementParameters> fwmp_;
NiceMock<MockPlatform> platform_;
NiceMock<MockDBusReplyFactory> reply_factory_;
NiceMock<chaps::TokenManagerClientMock> chaps_client_;
FakeEventSourceSink event_sink_;
scoped_refptr<MockMount> mount_;
// Declare service_ last so it gets destroyed before all the mocks. This is
// important because otherwise the background thread may call into mocks that
// have already been destroyed.
ServiceMonolithic service_{std::string()};
private:
DISALLOW_COPY_AND_ASSIGN(ServiceTestNotInitialized);
};
class ServiceTest : public ServiceTestNotInitialized {
public:
ServiceTest() = default;
~ServiceTest() override = default;
void SetUp() override {
ServiceTestNotInitialized::SetUp();
ASSERT_TRUE(service_.Initialize());
}
private:
DISALLOW_COPY_AND_ASSIGN(ServiceTest);
};
TEST_F(ServiceTest, ValidAbeDataTest) {
brillo::SecureBlob abe_data;
ASSERT_TRUE(ServiceMonolithic::GetAttestationBasedEnterpriseEnrollmentData(
"2eac34fa74994262b907c15a3a1462e349e5108ca0d0e807f4b1a3ee741a5594",
&abe_data));
ASSERT_EQ(32, abe_data.size());
}
TEST_F(ServiceTest, InvalidAbeDataTest) {
brillo::SecureBlob abe_data;
ASSERT_FALSE(ServiceMonolithic::GetAttestationBasedEnterpriseEnrollmentData(
"2eac34fa74994262b907c15a3a1462e349e5108c",
&abe_data));
ASSERT_EQ(0, abe_data.size());
ASSERT_FALSE(ServiceMonolithic::GetAttestationBasedEnterpriseEnrollmentData(
"garbage", &abe_data));
ASSERT_EQ(0, abe_data.size());
ASSERT_FALSE(ServiceMonolithic::GetAttestationBasedEnterpriseEnrollmentData(
"", &abe_data));
ASSERT_EQ(0, abe_data.size());
}
TEST_F(ServiceTest, CheckKeySuccessTest) {
char user[] = "chromeos-user";
char key[] = "274146c6e8886a843ddfea373e2dc71b";
SetupMount(user);
EXPECT_CALL(*mount_, AreSameUser(_))
.WillOnce(Return(false));
EXPECT_CALL(homedirs_, AreCredentialsValid(_))
.WillOnce(Return(true));
gboolean out = FALSE;
GError *error = NULL;
EXPECT_TRUE(service_.CheckKey(user, key, &out, &error));
EXPECT_TRUE(out);
}
TEST_F(ServiceTest, CheckKeyMountTest) {
static const char kUser[] = "chromeos-user";
static const char kKey[] = "274146c6e8886a843ddfea373e2dc71b";
SetupMount(kUser);
std::unique_ptr<AccountIdentifier> id(new AccountIdentifier);
std::unique_ptr<AuthorizationRequest> auth(new AuthorizationRequest);
std::unique_ptr<CheckKeyRequest> req(new CheckKeyRequest);
id->set_account_id(kUser);
auth->mutable_key()->set_secret(kKey);
// event_source_ will delete reply on cleanup.
std::string* base_reply_ptr = NULL;
MockDBusReply* reply = new MockDBusReply();
EXPECT_CALL(reply_factory_, NewReply(NULL, _))
.WillOnce(DoAll(SaveArg<1>(&base_reply_ptr), Return(reply)));
EXPECT_CALL(*mount_, AreSameUser(_))
.WillOnce(Return(true));
EXPECT_CALL(*mount_, AreValid(_))
.WillOnce(Return(true));
// Run will never be called because we aren't running the event loop.
service_.DoCheckKeyEx(id.get(), auth.get(), req.get(), NULL);
// Expect an empty reply as success.
BaseReply expected_reply;
std::string expected_reply_str;
expected_reply.SerializeToString(&expected_reply_str);
ASSERT_TRUE(base_reply_ptr);
EXPECT_EQ(expected_reply_str, *base_reply_ptr);
delete base_reply_ptr;
base_reply_ptr = NULL;
// Rinse and repeat but fail.
EXPECT_CALL(*mount_, AreSameUser(_))
.WillOnce(Return(true));
EXPECT_CALL(*mount_, AreValid(_))
.WillOnce(Return(false));
EXPECT_CALL(homedirs_, Exists(_))
.WillRepeatedly(Return(true));
EXPECT_CALL(homedirs_, AreCredentialsValid(_))
.WillOnce(Return(false));
// event_source_ will delete reply on cleanup.
reply = new MockDBusReply();
EXPECT_CALL(reply_factory_, NewReply(NULL, _))
.WillOnce(DoAll(SaveArg<1>(&base_reply_ptr), Return(reply)));
service_.DoCheckKeyEx(id.get(), auth.get(), req.get(), NULL);
// Expect an empty reply as success.
expected_reply.Clear();
expected_reply.set_error(CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED);
expected_reply.SerializeToString(&expected_reply_str);
ASSERT_TRUE(base_reply_ptr);
EXPECT_EQ(expected_reply_str, *base_reply_ptr);
delete base_reply_ptr;
base_reply_ptr = NULL;
}
TEST_F(ServiceTest, CheckKeyHomedirsTest) {
static const char kUser[] = "chromeos-user";
static const char kKey[] = "274146c6e8886a843ddfea373e2dc71b";
SetupMount(kUser);
std::unique_ptr<AccountIdentifier> id(new AccountIdentifier);
std::unique_ptr<AuthorizationRequest> auth(new AuthorizationRequest);
std::unique_ptr<CheckKeyRequest> req(new CheckKeyRequest);
// Expect an error about missing email.
// |error| will be cleaned up by event_source_
MockDBusReply* reply = new MockDBusReply();
std::string* base_reply_ptr = NULL;
id->set_account_id(kUser);
auth->mutable_key()->set_secret(kKey);
EXPECT_CALL(*mount_, AreSameUser(_))
.WillRepeatedly(Return(false));
EXPECT_CALL(homedirs_, Exists(_))
.WillRepeatedly(Return(true));
EXPECT_CALL(homedirs_, AreCredentialsValid(_))
.WillOnce(Return(true));
// Run will never be called because we aren't running the event loop.
EXPECT_CALL(reply_factory_, NewReply(NULL, _))
.WillOnce(DoAll(SaveArg<1>(&base_reply_ptr), Return(reply)));
service_.DoCheckKeyEx(id.get(), auth.get(), req.get(), NULL);
// Expect an empty reply as success.
BaseReply expected_reply;
std::string expected_reply_str;
expected_reply.SerializeToString(&expected_reply_str);
ASSERT_TRUE(base_reply_ptr);
EXPECT_EQ(expected_reply_str, *base_reply_ptr);
delete base_reply_ptr;
base_reply_ptr = NULL;
// Ensure failure
EXPECT_CALL(homedirs_, AreCredentialsValid(_))
.WillOnce(Return(false));
// event_source_ will delete reply on cleanup.
reply = new MockDBusReply();
EXPECT_CALL(reply_factory_, NewReply(NULL, _))
.WillOnce(DoAll(SaveArg<1>(&base_reply_ptr), Return(reply)));
service_.DoCheckKeyEx(id.get(), auth.get(), req.get(), NULL);
// Expect an empty reply as success.
expected_reply.Clear();
expected_reply.set_error(CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED);
expected_reply.SerializeToString(&expected_reply_str);
ASSERT_TRUE(base_reply_ptr);
EXPECT_EQ(expected_reply_str, *base_reply_ptr);
delete base_reply_ptr;
base_reply_ptr = NULL;
}
TEST_F(ServiceTestNotInitialized, CheckAsyncTestCredentials) {
// Setup a real homedirs instance (making this a pseudo-integration test).
test_helper_.InjectSystemSalt(&platform_, kSaltFile);
test_helper_.InitTestData(kImageDir, kDefaultUsers, 1,
false /* force_ecryptfs */);
TestUser* user = &test_helper_.users[0];
user->InjectKeyset(&platform_);
SecureBlob passkey;
cryptohome::Crypto::PasswordToPasskey(user->password,
test_helper_.system_salt, &passkey);
std::string passkey_string = passkey.to_string();
Crypto real_crypto(&platform_);
real_crypto.set_use_tpm(false);
real_crypto.Init(nullptr);
HomeDirs real_homedirs;
real_homedirs.set_crypto(&real_crypto);
real_homedirs.set_shadow_root(kImageDir);
real_homedirs.set_platform(&platform_);
policy::PolicyProvider policy_provider(
new NiceMock<policy::MockDevicePolicy>);
real_homedirs.set_policy_provider(&policy_provider);
// Avoid calling FreeDiskSpace routine.
EXPECT_CALL(platform_, AmountOfFreeDiskSpace(_))
.WillRepeatedly(Return(kMinFreeSpaceInBytes * 2));
service_.set_homedirs(&real_homedirs);
service_.set_crypto(&real_crypto);
service_.Initialize();
gboolean out = FALSE;
GError *error = NULL;
gint async_id = -1;
EXPECT_TRUE(service_.AsyncCheckKey(
const_cast<gchar*>(static_cast<const gchar*>(user->username)),
const_cast<gchar*>(static_cast<const gchar*>(passkey_string.c_str())),
&async_id,
&error));
EXPECT_NE(-1, async_id);
for (size_t i = 0; i < 64; i++) {
bool found = false;
service_.DispatchEvents();
for (const auto& completed_task : event_sink_.completed_tasks_) {
if (completed_task.sequence_id() == async_id) {
out = completed_task.return_status();
found = true;
}
}
if (found) {
break;
}
PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
}
EXPECT_TRUE(out);
}
TEST_F(ServiceTest, GetPublicMountPassKey) {
const char kPublicUser1[] = "public_user_1";
const char kPublicUser2[] = "public_user_2";
std::string public_user1_passkey;
service_.GetPublicMountPassKey(kPublicUser1, &public_user1_passkey);
std::string public_user2_passkey;
service_.GetPublicMountPassKey(kPublicUser2, &public_user2_passkey);
// The passkey should be different for different user.
EXPECT_NE(public_user1_passkey, public_user2_passkey);
std::string public_user1_passkey2;
service_.GetPublicMountPassKey(kPublicUser1, &public_user1_passkey2);
// The passkey should be the same for the same user.
EXPECT_EQ(public_user1_passkey, public_user1_passkey2);
}
TEST_F(ServiceTest, GetSanitizedUsername) {
char username[] = "chromeos-user";
gchar *sanitized = NULL;
GError *error = NULL;
EXPECT_TRUE(service_.GetSanitizedUsername(username, &sanitized, &error));
EXPECT_TRUE(error == NULL);
ASSERT_TRUE(sanitized);
const std::string expected(
brillo::cryptohome::home::SanitizeUserName(username));
EXPECT_FALSE(expected.empty());
EXPECT_EQ(expected, sanitized);
g_free(sanitized);
}
TEST_F(ServiceTestNotInitialized, CheckAutoCleanupCallback) {
// Checks that AutoCleanupCallback() is called periodically.
// Service will schedule periodic clean-ups. Wait a bit and make
// sure that we had at least 3 executed.
EXPECT_CALL(homedirs_, FreeDiskSpace())
.Times(::testing::AtLeast(3));
SetupMount("some-user-to-clean-up");
EXPECT_CALL(*mount_, UpdateCurrentUserActivityTimestamp(0))
.Times(::testing::AtLeast(3));
service_.set_auto_cleanup_period(2); // 2ms = 500HZ
service_.set_update_user_activity_period(2); // 2 x 5ms = 25HZ
service_.Initialize();
PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
}
TEST_F(ServiceTestNotInitialized, CheckAutoCleanupCallbackFirst) {
// Checks that AutoCleanupCallback() is called first right after init.
// Service will schedule first cleanup right after its init.
EXPECT_CALL(homedirs_, FreeDiskSpace())
.Times(1);
service_.set_auto_cleanup_period(1000); // 1s - long enough
service_.Initialize();
// short delay to see the first invocation
PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10));
}
static gboolean SignalCounter(GSignalInvocationHint *ihint,
guint n_param_values,
const GValue *param_values,
gpointer data) {
int *count = reinterpret_cast<int *>(data);
(*count)++;
return true;
}
TEST_F(ServiceTestNotInitialized, CheckLowDiskCallback) {
// Checks that LowDiskCallback is called periodically.
EXPECT_CALL(homedirs_, AmountOfFreeDiskSpace()).Times(::testing::AtLeast(3))
.WillOnce(Return(kNotifyDiskSpaceThreshold + 1))
.WillOnce(Return(kNotifyDiskSpaceThreshold - 1))
.WillRepeatedly(Return(kNotifyDiskSpaceThreshold + 1));
service_.set_low_disk_notification_period_ms(2);
guint low_disk_space_signal = g_signal_lookup("low_disk_space",
gobject::cryptohome_get_type());
if (!low_disk_space_signal) {
low_disk_space_signal = g_signal_new("low_disk_space",
gobject::cryptohome_get_type(),
G_SIGNAL_RUN_LAST,
0,
NULL,
NULL,
nullptr,
G_TYPE_NONE,
1,
G_TYPE_UINT64);
}
int count = 0;
gulong hook_id = g_signal_add_emission_hook(
low_disk_space_signal, 0, SignalCounter, &count, NULL);
service_.Initialize();
PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
EXPECT_EQ(1, count);
g_signal_remove_emission_hook(low_disk_space_signal, hook_id);
}
TEST_F(ServiceTest, NoDeadlocksInInitializeTpmComplete) {
char user[] = "chromeos-user";
SetupMount(user);
// Put a task on mount_thread_ that starts before InitializeTpmComplete
// and finishes after it exits. Verify it doesn't wait for
// InitializeTpmComplete forever.
base::WaitableEvent event(false, false); // auto-reset
base::WaitableEvent event_stop(true, false); // manual reset
bool finished = false;
service_.mount_thread_.message_loop()->PostTask(FROM_HERE,
base::Bind([](bool* finished,
base::WaitableEvent* event,
base::WaitableEvent* event_stop) {
event->Signal(); // Signal "Ready to start"
// Wait up to 2s for InitializeTpmComplete to finish
*finished = event_stop->TimedWait(base::TimeDelta::FromSeconds(2));
event->Signal(); // Signal "Result ready"
}, &finished, &event, &event_stop));
event.Wait(); // Wait for "Ready to start"
service_.OwnershipCallback(true, true);
event_stop.Signal();
event.Wait(); // Wait for "Result ready"
ASSERT_TRUE(finished);
}
struct Mounts {
const FilePath src;
const FilePath dst;
};
const struct Mounts kShadowMounts[] = {
{ FilePath("/home/.shadow/a"), FilePath("/home/user/0")},
{ FilePath("/home/.shadow/a"), FilePath("/home/root/0")},
{ FilePath("/home/.shadow/b"), FilePath("/home/user/1")},
{ FilePath("/home/.shadow/a"), FilePath("/home/chronos/user")},
{ FilePath("/home/.shadow/b"), FilePath("/home/root/1")},
};
const int kShadowMountsCount = 5;
bool StaleShadowMounts(
const FilePath& from_prefix,
std::multimap<const FilePath, const FilePath>* mounts) {
LOG(INFO) << "StaleShadowMounts(" << from_prefix.value()
<< "): called";
if (from_prefix.value() == "/home/.shadow") {
if (!mounts)
return true;
const struct Mounts* m = &kShadowMounts[0];
for (int i = 0; i < kShadowMountsCount; ++i, ++m) {
mounts->insert(
std::pair<const FilePath, const FilePath>(m->src, m->dst));
LOG(INFO) << "Inserting " << m->src.value() << ":" << m->dst.value();
}
return true;
}
return false;
}
TEST_F(ServiceTest, CleanUpStale_EmptyMap_NoOpenFiles_ShadowOnly) {
// Check that when we have a bunch of stale shadow mounts, no active mounts,
// and no open filehandles, all stale mounts are unmounted.
EXPECT_CALL(platform_, GetMountsBySourcePrefix(_, _))
.Times(3)
.WillRepeatedly(Invoke(StaleShadowMounts));
EXPECT_CALL(platform_, GetProcessesWithOpenFiles(_, _))
.Times(kShadowMountsCount);
EXPECT_CALL(platform_, Unmount(_, true, _))
.Times(kShadowMountsCount)
.WillRepeatedly(Return(true));
EXPECT_FALSE(service_.CleanUpStaleMounts(false));
}
TEST_F(ServiceTest, CleanUpStale_EmptyMap_OpenLegacy_ShadowOnly) {
// Check that when we have a bunch of stale shadow mounts, no active mounts,
// and some open filehandles to the legacy homedir, all mounts without
// filehandles are unmounted.
EXPECT_CALL(platform_, GetMountsBySourcePrefix(_, _))
.Times(3)
.WillRepeatedly(Invoke(StaleShadowMounts));
std::vector<ProcessInformation> processes(1);
processes[0].set_process_id(1);
EXPECT_CALL(platform_, GetProcessesWithOpenFiles(_, _))
.Times(kShadowMountsCount - 1);
EXPECT_CALL(platform_,
GetProcessesWithOpenFiles(FilePath("/home/chronos/user"), _))
.Times(1)
.WillRepeatedly(SetArgPointee<1>(processes));
EXPECT_CALL(platform_,
Unmount(Property(&FilePath::value, EndsWith("/1")), true, _))
.Times(2)
.WillRepeatedly(Return(true));
EXPECT_TRUE(service_.CleanUpStaleMounts(false));
}
TEST_F(ServiceTestNotInitialized,
CleanUpStale_FilledMap_NoOpenFiles_ShadowOnly) {
// Checks that when we have a bunch of stale shadow mounts, some active
// mounts, and no open filehandles, all inactive mounts are unmounted.
// ownership handed off to the Service MountMap
MockMountFactory mount_factory;
MockMount* mount = new MockMount();
EXPECT_CALL(mount_factory, New())
.WillOnce(Return(mount));
service_.set_mount_factory(&mount_factory);
EXPECT_CALL(platform_, GetMountsBySourcePrefix(_, _))
.Times(3)
.WillRepeatedly(Return(false));
ASSERT_TRUE(service_.Initialize());
EXPECT_CALL(*mount, Init(&platform_, service_.crypto(), _))
.WillOnce(Return(true));
EXPECT_CALL(*mount, MountCryptohome(_, _, _))
.WillOnce(Return(true));
EXPECT_CALL(*mount, UpdateCurrentUserActivityTimestamp(_))
.WillOnce(Return(true));
EXPECT_CALL(platform_, GetMountsBySourcePrefix(_, _))
.Times(3)
.WillRepeatedly(Return(false));
gint error_code = 0;
gboolean result = FALSE;
ASSERT_TRUE(service_.Mount("foo@bar.net", "key", true, false,
&error_code, &result, NULL));
ASSERT_EQ(TRUE, result);
EXPECT_CALL(platform_, GetMountsBySourcePrefix(_, _))
.Times(3)
.WillRepeatedly(Invoke(StaleShadowMounts));
EXPECT_CALL(platform_, GetProcessesWithOpenFiles(_, _))
.Times(kShadowMountsCount);
EXPECT_CALL(*mount, OwnsMountPoint(_))
.WillRepeatedly(Return(false));
EXPECT_CALL(*mount, OwnsMountPoint(FilePath("/home/user/1")))
.WillOnce(Return(true));
EXPECT_CALL(*mount, OwnsMountPoint(FilePath("/home/root/1")))
.WillOnce(Return(true));
EXPECT_CALL(platform_,
Unmount(Property(&FilePath::value, EndsWith("/0")), true, _))
.Times(2)
.WillRepeatedly(Return(true));
EXPECT_CALL(platform_, Unmount(FilePath("/home/chronos/user"), true, _))
.WillOnce(Return(true));
std::vector<std::string> fake_token_list;
fake_token_list.push_back("/home/chronos/user/token");
fake_token_list.push_back("/home/user/1/token");
fake_token_list.push_back("/home/root/1/token");
EXPECT_CALL(chaps_client_, GetTokenList(_, _))
.WillRepeatedly(DoAll(SetArgPointee<1>(fake_token_list),
Return(true)));
EXPECT_CALL(chaps_client_,
UnloadToken(_, FilePath("/home/chronos/user/token")))
.Times(1);
// Expect that CleanUpStaleMounts() tells us it skipped no mounts.
EXPECT_FALSE(service_.CleanUpStaleMounts(false));
}
TEST_F(ServiceTest, StoreEnrollmentState) {
brillo::glib::ScopedArray test_array(g_array_new(FALSE, FALSE, 1));
std::string data = "123456";
g_array_append_vals(test_array.get(), data.data(), data.length());
// Helper strings for setting install attributes.
static const char true_str[] = "true";
const brillo::Blob true_value(true_str, true_str + arraysize(true_str));
static const char false_str[] = "false";
const brillo::Blob false_value(false_str, false_str + arraysize(false_str));
gboolean success;
GError* error = NULL;
// Set us as non-enterprise enrolled.
EXPECT_CALL(attrs_, Get("enterprise.owned", _)).WillOnce(
DoAll(SetArgPointee<1>(false_value), Return(true)));
service_.DetectEnterpriseOwnership();
// Should not enterprise-enroll this device.
EXPECT_TRUE(service_.StoreEnrollmentState(test_array.get(), &success,
&error));
EXPECT_FALSE(success);
// Set us as enterprise enrolled.
EXPECT_CALL(attrs_, Get("enterprise.owned", _)).WillOnce(
DoAll(SetArgPointee<1>(true_value), Return(true)));
service_.DetectEnterpriseOwnership();
std::string encrypted_data = "so_encrypted";
// Test successful encryption.
EXPECT_CALL(crypto_, EncryptWithTpm(_, _)).WillOnce(DoAll(
SetArgPointee<1>(encrypted_data), Return(true)));
// Should write file as this device is enterprise enrolled.
EXPECT_CALL(platform_,
WriteStringToFileAtomicDurable(
FilePath(
"/mnt/stateful_partition/unencrypted/preserve/enrollment_state.epb"),
encrypted_data, _))
.WillOnce(Return(true));
EXPECT_TRUE(service_.StoreEnrollmentState(test_array.get(), &success,
&error));
EXPECT_TRUE(success);
EXPECT_TRUE(service_.homedirs()->enterprise_owned());
}
TEST_F(ServiceTest, LoadEnrollmentState) {
gboolean success;
GError* error = NULL;
brillo::glib::ScopedArray output;
// Convert to blob -- this is what we're reading from the file.
std::string data = "123456";
const brillo::Blob data_blob(data.c_str(), data.c_str() + data.length());
SecureBlob decrypted_blob("decrypted");
// Assume the data is there, we should return the value and success.
EXPECT_CALL(platform_,
ReadFile(
FilePath(
"/mnt/stateful_partition/unencrypted/preserve/enrollment_state.epb"),
_))
.WillOnce(DoAll(SetArgPointee<1>(data_blob), Return(true)));
EXPECT_CALL(crypto_, DecryptWithTpm(_, _)).WillOnce(DoAll(
SetArgPointee<1>(decrypted_blob), Return(TRUE)));
EXPECT_TRUE(service_.LoadEnrollmentState(
&(brillo::Resetter(&output).lvalue()), &success, &error));
EXPECT_TRUE(success);
// Convert output array to a blob for comparison.
SecureBlob output_blob(output->data, output->data + output->len);
EXPECT_EQ(decrypted_blob, output_blob);
// Assume we fail to read the data, we should not return success.
EXPECT_CALL(platform_,
ReadFile(
FilePath(
"/mnt/stateful_partition/unencrypted/preserve/enrollment_state.epb"),
_))
.WillOnce(Return(false));
EXPECT_TRUE(service_.LoadEnrollmentState(
&(brillo::Resetter(&output).lvalue()), &success, &error));
EXPECT_FALSE(success);
}
class ServiceExTest : public ServiceTest {
public:
ServiceExTest() = default;
~ServiceExTest() override = default;
void TearDown() override {
if (g_error_) {
g_error_free(g_error_);
g_error_ = NULL;
}
ServiceTest::TearDown();
}
void SetupErrorReply() {
g_error_ = NULL;
// |error| will be cleaned up by event_source_
MockDBusErrorReply *error = new MockDBusErrorReply();
EXPECT_CALL(reply_factory_, NewErrorReply(NULL, _))
.WillOnce(DoAll(SaveArg<1>(&g_error_), Return(error)));
}
void SetupReply() {
EXPECT_CALL(reply_factory_, NewReply(NULL, _))
.WillOnce(DoAll(SaveArg<1>(&reply_), Return(new MockDBusReply())));
}
BaseReply GetLastReply() {
BaseReply reply;
CHECK(reply_);
CHECK(reply.ParseFromString(*reply_));
delete reply_;
reply_ = NULL;
return reply;
}
void PrepareArguments() {
id_.reset(new AccountIdentifier);
auth_.reset(new AuthorizationRequest);
add_req_.reset(new AddKeyRequest);
check_req_.reset(new CheckKeyRequest);
mount_req_.reset(new MountRequest);
remove_req_.reset(new RemoveKeyRequest);
list_keys_req_.reset(new ListKeysRequest);
}
template<class ProtoBuf>
GArray* GArrayFromProtoBuf(const ProtoBuf& pb) {
guint len = pb.ByteSize();
GArray* ary = g_array_sized_new(FALSE, FALSE, 1, len);
g_array_set_size(ary, len);
if (!pb.SerializeToArray(ary->data, len)) {
printf("Failed to serialize protocol buffer.\n");
return NULL;
}
return ary;
}
VaultKeyset* GetNiceMockVaultKeyset(const Credentials& credentials) const {
std::unique_ptr<VaultKeyset> mvk(new NiceMock<MockVaultKeyset>);
*(mvk->mutable_serialized()->mutable_key_data()) = credentials.key_data();
return mvk.release();
}
template<class ProtoBuf>
brillo::SecureBlob BlobFromProtobuf(const ProtoBuf& pb) {
std::string serialized;
CHECK(pb.SerializeToString(&serialized));
return brillo::SecureBlob(serialized);
}
protected:
std::unique_ptr<AccountIdentifier> id_;
std::unique_ptr<AuthorizationRequest> auth_;
std::unique_ptr<AddKeyRequest> add_req_;
std::unique_ptr<CheckKeyRequest> check_req_;
std::unique_ptr<MountRequest> mount_req_;
std::unique_ptr<RemoveKeyRequest> remove_req_;
std::unique_ptr<ListKeysRequest> list_keys_req_;
GError* g_error_{nullptr};
std::string* reply_{nullptr};
private:
DISALLOW_COPY_AND_ASSIGN(ServiceExTest);
};
TEST_F(ServiceExTest, MountInvalidArgsNoEmail) {
SetupErrorReply();
PrepareArguments();
// Run will never be called because we aren't running the event loop.
// For the same reason, DoMountEx is called directly.
service_.DoMountEx(id_.get(), auth_.get(), mount_req_.get(), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STREQ("No email supplied", g_error_->message);
}
TEST_F(ServiceExTest, MountInvalidArgsNoSecret) {
SetupErrorReply();
PrepareArguments();
id_->set_account_id("foo@gmail.com");
service_.DoMountEx(id_.get(), auth_.get(), mount_req_.get(), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STREQ("No key secret supplied", g_error_->message);
}
TEST_F(ServiceExTest, MountInvalidArgsEmptySecret) {
SetupErrorReply();
PrepareArguments();
id_->set_account_id("foo@gmail.com");
auth_->mutable_key()->set_secret("");
service_.DoMountEx(id_.get(), auth_.get(), mount_req_.get(), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STREQ("No key secret supplied", g_error_->message);
}
TEST_F(ServiceExTest, MountInvalidArgsCreateWithNoKey) {
SetupErrorReply();
PrepareArguments();
id_->set_account_id("foo@gmail.com");
auth_->mutable_key()->set_secret("blerg");
mount_req_->mutable_create();
service_.DoMountEx(id_.get(), auth_.get(), mount_req_.get(), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STREQ("CreateRequest supplied with no keys", g_error_->message);
}
TEST_F(ServiceExTest, MountInvalidArgsCreateWithEmptyKey) {
SetupErrorReply();
PrepareArguments();
id_->set_account_id("foo@gmail.com");
auth_->mutable_key()->set_secret("blerg");
mount_req_->mutable_create()->add_keys();
// TODO(wad) Add remaining missing field tests and NULL tests
service_.DoMountEx(id_.get(), auth_.get(), mount_req_.get(), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STREQ("CreateRequest Keys are not fully specified",
g_error_->message);
}
TEST_F(ServiceExTest, MountPublicWithExistingMounts) {
constexpr char kUser[] = "chromeos-user";
SetupReply();
PrepareArguments();
SetupMount("foo@gmail.com");
id_->set_account_id(kUser);
mount_req_->set_public_mount(true);
EXPECT_CALL(homedirs_, Exists(_)).WillOnce(Return(true));
service_.DoMountEx(id_.get(), auth_.get(), mount_req_.get(), NULL);
BaseReply reply = GetLastReply();
EXPECT_TRUE(reply.has_error());
EXPECT_EQ(CRYPTOHOME_ERROR_MOUNT_MOUNT_POINT_BUSY, reply.error());
}
TEST_F(ServiceExTest, MountPublicUsesPublicMountPasskey) {
constexpr char kUser[] = "chromeos-user";
SetupReply();
PrepareArguments();
id_->set_account_id(kUser);
mount_req_->set_public_mount(true);
EXPECT_CALL(homedirs_, Exists(_)).WillOnce(testing::InvokeWithoutArgs([&]() {
SetupMount(kUser);
EXPECT_CALL(*mount_, MountCryptohome(_, _, _))
.WillOnce(testing::Invoke([](const Credentials& credentials,
const Mount::MountArgs& mount_args,
MountError* error) {
brillo::SecureBlob passkey;
credentials.GetPasskey(&passkey);
// Tests that the passkey is filled when public_mount is set.
EXPECT_FALSE(passkey.empty());
return true;
}));
return true;
}));
service_.DoMountEx(id_.get(), auth_.get(), mount_req_.get(), NULL);
BaseReply reply = GetLastReply();
EXPECT_FALSE(reply.has_error());
}
TEST_F(ServiceExTest, AddKeyInvalidArgsNoEmail) {
SetupErrorReply();
PrepareArguments();
// Run will never be called because we aren't running the event loop.
// For the same reason, DoMountEx is called directly.
service_.DoAddKeyEx(id_.get(), auth_.get(), add_req_.get(), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STREQ("No email supplied", g_error_->message);
}
TEST_F(ServiceExTest, AddKeyInvalidArgsNoSecret) {
SetupErrorReply();
PrepareArguments();
id_->set_account_id("foo@gmail.com");
service_.DoAddKeyEx(id_.get(), auth_.get(), add_req_.get(), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STREQ("No key secret supplied", g_error_->message);
}
TEST_F(ServiceExTest, AddKeyInvalidArgsNoNewKeySet) {
SetupErrorReply();
PrepareArguments();
id_->set_account_id("foo@gmail.com");
auth_->mutable_key()->set_secret("blerg");
add_req_->clear_key();
service_.DoAddKeyEx(id_.get(), auth_.get(), add_req_.get(), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STREQ("No new key supplied", g_error_->message);
}
TEST_F(ServiceExTest, AddKeyInvalidArgsNoKeyFilled) {
SetupErrorReply();
PrepareArguments();
id_->set_account_id("foo@gmail.com");
auth_->mutable_key()->set_secret("blerg");
add_req_->mutable_key();
service_.DoAddKeyEx(id_.get(), auth_.get(), add_req_.get(), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STREQ("No new key supplied", g_error_->message);
}
TEST_F(ServiceExTest, AddKeyInvalidArgsNoNewKeyLabel) {
SetupErrorReply();
PrepareArguments();
id_->set_account_id("foo@gmail.com");
auth_->mutable_key()->set_secret("blerg");
add_req_->mutable_key();
// No label
add_req_->mutable_key()->set_secret("some secret");
service_.DoAddKeyEx(id_.get(), auth_.get(), add_req_.get(), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STREQ("No new key label supplied", g_error_->message);
}
TEST_F(ServiceExTest, CheckKeyInvalidArgsNoEmail) {
SetupErrorReply();
PrepareArguments();
// Run will never be called because we aren't running the event loop.
// For the same reason, DoMountEx is called directly.
service_.DoCheckKeyEx(id_.get(), auth_.get(), check_req_.get(), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STREQ("No email supplied", g_error_->message);
}
TEST_F(ServiceExTest, CheckKeyInvalidArgsNoSecret) {
SetupErrorReply();
PrepareArguments();
id_->set_account_id("foo@gmail.com");
service_.DoCheckKeyEx(id_.get(), auth_.get(), check_req_.get(), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STREQ("No key secret supplied", g_error_->message);
}
TEST_F(ServiceExTest, CheckKeyInvalidArgsEmptySecret) {
SetupErrorReply();
PrepareArguments();
id_->set_account_id("foo@gmail.com");
auth_->mutable_key()->set_secret("");
service_.DoCheckKeyEx(id_.get(), auth_.get(), check_req_.get(), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STREQ("No key secret supplied", g_error_->message);
}
TEST_F(ServiceExTest, RemoveKeyInvalidArgsNoEmail) {
SetupErrorReply();
PrepareArguments();
// Run will never be called because we aren't running the event loop.
// For the same reason, DoMountEx is called directly.
service_.DoRemoveKeyEx(id_.get(), auth_.get(), remove_req_.get(), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STREQ("No email supplied", g_error_->message);
}
TEST_F(ServiceExTest, RemoveKeyInvalidArgsNoSecret) {
SetupErrorReply();
PrepareArguments();
id_->set_account_id("foo@gmail.com");
service_.DoRemoveKeyEx(id_.get(), auth_.get(), remove_req_.get(), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STREQ("No key secret supplied", g_error_->message);
}
TEST_F(ServiceExTest, RemoveKeyInvalidArgsEmptySecret) {
SetupErrorReply();
PrepareArguments();
id_->set_account_id("foo@gmail.com");
auth_->mutable_key()->set_secret("");
service_.DoRemoveKeyEx(id_.get(), auth_.get(), remove_req_.get(), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STREQ("No key secret supplied", g_error_->message);
}
TEST_F(ServiceExTest, RemoveKeyInvalidArgsEmptyRemoveLabel) {
SetupErrorReply();
PrepareArguments();
id_->set_account_id("foo@gmail.com");
auth_->mutable_key()->set_secret("some secret");
remove_req_->mutable_key()->mutable_data();
service_.DoRemoveKeyEx(id_.get(), auth_.get(), remove_req_.get(), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STREQ("No label provided for target key", g_error_->message);
}
TEST_F(ServiceExTest, BootLockboxSignSuccess) {
SetupReply();
SecureBlob test_signature("test");
EXPECT_CALL(lockbox_, Sign(_, _))
.WillRepeatedly(DoAll(SetArgPointee<1>(test_signature),
Return(true)));
SignBootLockboxRequest request;
request.set_data("test_data");
service_.DoSignBootLockbox(BlobFromProtobuf(request), NULL);
BaseReply reply = GetLastReply();
EXPECT_FALSE(reply.has_error());
EXPECT_TRUE(reply.HasExtension(SignBootLockboxReply::reply));
EXPECT_EQ("test",
reply.GetExtension(SignBootLockboxReply::reply).signature());
}
TEST_F(ServiceExTest, BootLockboxSignBadArgs) {
// Try with bad proto data.
SetupErrorReply();
service_.DoSignBootLockbox(SecureBlob("not_a_protobuf"), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STRNE("", g_error_->message);
// Try with |data| not set.
SetupErrorReply();
SignBootLockboxRequest request;
service_.DoSignBootLockbox(BlobFromProtobuf(request), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STRNE("", g_error_->message);
}
TEST_F(ServiceExTest, BootLockboxSignError) {
SetupReply();
EXPECT_CALL(lockbox_, Sign(_, _))
.WillRepeatedly(Return(false));
SignBootLockboxRequest request;
request.set_data("test_data");
service_.DoSignBootLockbox(BlobFromProtobuf(request), NULL);
BaseReply reply = GetLastReply();
EXPECT_TRUE(reply.has_error());
EXPECT_EQ(CRYPTOHOME_ERROR_LOCKBOX_CANNOT_SIGN, reply.error());
EXPECT_FALSE(reply.HasExtension(SignBootLockboxReply::reply));
}
TEST_F(ServiceExTest, BootLockboxVerifySuccess) {
SetupReply();
EXPECT_CALL(lockbox_, Verify(_, _))
.WillRepeatedly(Return(true));
VerifyBootLockboxRequest request;
request.set_data("test_data");
request.set_signature("test_signature");
service_.DoVerifyBootLockbox(BlobFromProtobuf(request), NULL);
BaseReply reply = GetLastReply();
EXPECT_FALSE(reply.has_error());
EXPECT_FALSE(reply.HasExtension(SignBootLockboxReply::reply));
}
TEST_F(ServiceExTest, BootLockboxVerifyBadArgs) {
// Try with bad proto data.
SetupErrorReply();
service_.DoVerifyBootLockbox(SecureBlob("not_a_protobuf"), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STRNE("", g_error_->message);
// Try with |signature| not set.
SetupErrorReply();
VerifyBootLockboxRequest request;
request.set_data("test_data");
service_.DoVerifyBootLockbox(BlobFromProtobuf(request), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STRNE("", g_error_->message);
// Try with |data| not set.
SetupErrorReply();
VerifyBootLockboxRequest request2;
request2.set_signature("test_data");
service_.DoVerifyBootLockbox(BlobFromProtobuf(request2), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STRNE("", g_error_->message);
}
TEST_F(ServiceExTest, BootLockboxVerifyError) {
SetupReply();
EXPECT_CALL(lockbox_, Verify(_, _))
.WillRepeatedly(Return(false));
VerifyBootLockboxRequest request;
request.set_data("test_data");
request.set_signature("test_signature");
service_.DoVerifyBootLockbox(BlobFromProtobuf(request), NULL);
BaseReply reply = GetLastReply();
EXPECT_TRUE(reply.has_error());
EXPECT_EQ(CRYPTOHOME_ERROR_LOCKBOX_SIGNATURE_INVALID, reply.error());
}
TEST_F(ServiceExTest, BootLockboxFinalizeSuccess) {
SetupReply();
EXPECT_CALL(lockbox_, FinalizeBoot())
.WillRepeatedly(Return(true));
FinalizeBootLockboxRequest request;
service_.DoFinalizeBootLockbox(BlobFromProtobuf(request), NULL);
BaseReply reply = GetLastReply();
EXPECT_FALSE(reply.has_error());
EXPECT_FALSE(reply.HasExtension(SignBootLockboxReply::reply));
}
TEST_F(ServiceExTest, BootLockboxFinalizeBadArgs) {
// Try with bad proto data.
SetupErrorReply();
service_.DoFinalizeBootLockbox(SecureBlob("not_a_protobuf"), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STRNE("", g_error_->message);
}
TEST_F(ServiceExTest, BootLockboxFinalizeError) {
SetupReply();
EXPECT_CALL(lockbox_, FinalizeBoot())
.WillRepeatedly(Return(false));
FinalizeBootLockboxRequest request;
service_.DoFinalizeBootLockbox(BlobFromProtobuf(request), NULL);
BaseReply reply = GetLastReply();
EXPECT_TRUE(reply.has_error());
EXPECT_EQ(CRYPTOHOME_ERROR_TPM_COMM_ERROR, reply.error());
}
TEST_F(ServiceExTest, GetBootAttributeSuccess) {
SetupReply();
EXPECT_CALL(boot_attributes_, Get(_, _))
.WillRepeatedly(DoAll(SetArgPointee<1>("1234"), Return(true)));
GetBootAttributeRequest request;
request.set_name("test");
service_.DoGetBootAttribute(BlobFromProtobuf(request), NULL);
BaseReply reply = GetLastReply();
EXPECT_FALSE(reply.has_error());
EXPECT_TRUE(reply.HasExtension(GetBootAttributeReply::reply));
EXPECT_EQ("1234",
reply.GetExtension(GetBootAttributeReply::reply).value());
}
TEST_F(ServiceExTest, GetBootAttributeBadArgs) {
// Try with bad proto data.
SetupErrorReply();
service_.DoGetBootAttribute(SecureBlob("not_a_protobuf"), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STRNE("", g_error_->message);
}
TEST_F(ServiceExTest, GetBootAttributeError) {
SetupReply();
EXPECT_CALL(boot_attributes_, Get(_, _))
.WillRepeatedly(Return(false));
GetBootAttributeRequest request;
request.set_name("test");
service_.DoGetBootAttribute(BlobFromProtobuf(request), NULL);
BaseReply reply = GetLastReply();
EXPECT_TRUE(reply.has_error());
EXPECT_EQ(CRYPTOHOME_ERROR_BOOT_ATTRIBUTE_NOT_FOUND, reply.error());
}
TEST_F(ServiceExTest, SetBootAttributeSuccess) {
SetupReply();
SetBootAttributeRequest request;
request.set_name("test");
request.set_value("1234");
service_.DoSetBootAttribute(BlobFromProtobuf(request), NULL);
BaseReply reply = GetLastReply();
EXPECT_FALSE(reply.has_error());
}
TEST_F(ServiceExTest, SetBootAttributeBadArgs) {
// Try with bad proto data.
SetupErrorReply();
service_.DoSetBootAttribute(SecureBlob("not_a_protobuf"), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STRNE("", g_error_->message);
}
TEST_F(ServiceExTest, FlushAndSignBootAttributesSuccess) {
SetupReply();
EXPECT_CALL(boot_attributes_, FlushAndSign())
.WillRepeatedly(Return(true));
FlushAndSignBootAttributesRequest request;
service_.DoFlushAndSignBootAttributes(BlobFromProtobuf(request), NULL);
BaseReply reply = GetLastReply();
EXPECT_FALSE(reply.has_error());
}
TEST_F(ServiceExTest, FlushAndSignBootAttributesBadArgs) {
// Try with bad proto data.
SetupErrorReply();
service_.DoFlushAndSignBootAttributes(SecureBlob("not_a_protobuf"), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STRNE("", g_error_->message);
}
TEST_F(ServiceExTest, FlushAndSignBootAttributesError) {
SetupReply();
EXPECT_CALL(boot_attributes_, FlushAndSign())
.WillRepeatedly(Return(false));
FlushAndSignBootAttributesRequest request;
service_.DoFlushAndSignBootAttributes(BlobFromProtobuf(request), NULL);
BaseReply reply = GetLastReply();
EXPECT_TRUE(reply.has_error());
EXPECT_EQ(CRYPTOHOME_ERROR_BOOT_ATTRIBUTES_CANNOT_SIGN, reply.error());
}
TEST_F(ServiceExTest, GetLoginStatusSuccess) {
SetupReply();
EXPECT_CALL(homedirs_, GetPlainOwner(_))
.WillOnce(Return(true));
EXPECT_CALL(lockbox_, IsFinalized())
.WillOnce(Return(false));
GetLoginStatusRequest request;
service_.DoGetLoginStatus(BlobFromProtobuf(request), NULL);
BaseReply reply = GetLastReply();
EXPECT_FALSE(reply.has_error());
EXPECT_TRUE(reply.HasExtension(GetLoginStatusReply::reply));
EXPECT_TRUE(
reply.GetExtension(GetLoginStatusReply::reply).owner_user_exists());
EXPECT_FALSE(
reply.GetExtension(GetLoginStatusReply::reply).boot_lockbox_finalized());
}
TEST_F(ServiceExTest, GetLoginStatusBadArgs) {
// Try with bad proto data.
SetupErrorReply();
service_.DoVerifyBootLockbox(SecureBlob("not_a_protobuf"), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STRNE("", g_error_->message);
}
TEST_F(ServiceExTest, GetKeyDataExNoMatch) {
SetupReply();
PrepareArguments();
EXPECT_CALL(homedirs_, Exists(_))
.WillRepeatedly(Return(true));
id_->set_account_id("unittest@example.com");
GetKeyDataRequest req;
req.mutable_key()->mutable_data()->set_label("non-existent label");
// Ensure there are no matches.
EXPECT_CALL(homedirs_, GetVaultKeyset(_))
.Times(1)
.WillRepeatedly(Return(static_cast<VaultKeyset*>(NULL)));
service_.DoGetKeyDataEx(id_.get(), auth_.get(), &req, NULL);
BaseReply reply = GetLastReply();
EXPECT_FALSE(reply.has_error());
GetKeyDataReply sub_reply = reply.GetExtension(GetKeyDataReply::reply);
EXPECT_EQ(0, sub_reply.key_data_size());
}
TEST_F(ServiceExTest, GetKeyDataExOneMatch) {
// Request the single key by label.
SetupReply();
PrepareArguments();
static const char *kExpectedLabel = "find-me";
GetKeyDataRequest req;
req.mutable_key()->mutable_data()->set_label(kExpectedLabel);
EXPECT_CALL(homedirs_, Exists(_))
.WillRepeatedly(Return(true));
EXPECT_CALL(homedirs_, GetVaultKeyset(_))
.Times(1)
.WillRepeatedly(Invoke(this, &ServiceExTest::GetNiceMockVaultKeyset));
id_->set_account_id("unittest@example.com");
service_.DoGetKeyDataEx(id_.get(), auth_.get(), &req, NULL);
BaseReply reply = GetLastReply();
EXPECT_FALSE(reply.has_error());
GetKeyDataReply sub_reply = reply.GetExtension(GetKeyDataReply::reply);
ASSERT_EQ(1, sub_reply.key_data_size());
EXPECT_EQ(std::string(kExpectedLabel), sub_reply.key_data(0).label());
}
TEST_F(ServiceExTest, GetKeyDataInvalidArgsNoEmail) {
SetupErrorReply();
PrepareArguments();
GetKeyDataRequest req;
service_.DoGetKeyDataEx(id_.get(), auth_.get(), &req, NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STREQ("No email supplied", g_error_->message);
}
TEST_F(ServiceExTest, ListKeysInvalidArgsNoEmail) {
SetupErrorReply();
PrepareArguments();
// Run will never be called because we aren't running the event loop.
// For the same reason, DoListKeysEx is called directly.
service_.DoListKeysEx(id_.get(), auth_.get(), list_keys_req_.get(), NULL);
ASSERT_NE(g_error_, reinterpret_cast<void *>(0));
EXPECT_STREQ("No email supplied", g_error_->message);
}
TEST_F(ServiceExTest, GetFirmwareManagementParametersSuccess) {
brillo::SecureBlob hash("its_a_hash");
SetupReply();
EXPECT_CALL(fwmp_, Load())
.WillOnce(Return(true));
EXPECT_CALL(fwmp_, GetFlags(_))
.WillRepeatedly(DoAll(SetArgPointee<0>(0x1234), Return(true)));
EXPECT_CALL(fwmp_, GetDeveloperKeyHash(_))
.WillRepeatedly(DoAll(SetArgPointee<0>(hash), Return(true)));
GetFirmwareManagementParametersRequest request;
service_.DoGetFirmwareManagementParameters(BlobFromProtobuf(request), NULL);
BaseReply reply = GetLastReply();
EXPECT_FALSE(reply.has_error());
EXPECT_TRUE(reply.HasExtension(GetFirmwareManagementParametersReply::reply));
EXPECT_EQ(
reply.GetExtension(GetFirmwareManagementParametersReply::reply).flags(),
0x1234);
EXPECT_EQ(reply.GetExtension(
GetFirmwareManagementParametersReply::reply).developer_key_hash(),
hash.to_string());
}
TEST_F(ServiceExTest, GetFirmwareManagementParametersError) {
SetupReply();
EXPECT_CALL(fwmp_, Load())
.WillRepeatedly(Return(false));
GetFirmwareManagementParametersRequest request;
service_.DoGetFirmwareManagementParameters(BlobFromProtobuf(request), NULL);
BaseReply reply = GetLastReply();
EXPECT_TRUE(reply.has_error());
EXPECT_EQ(CRYPTOHOME_ERROR_FIRMWARE_MANAGEMENT_PARAMETERS_INVALID,
reply.error());
}
TEST_F(ServiceExTest, SetFirmwareManagementParametersSuccess) {
brillo::SecureBlob hash("its_a_hash");
brillo::Blob out_hash;
SetupReply();
EXPECT_CALL(fwmp_, Create())
.WillOnce(Return(true));
EXPECT_CALL(fwmp_, Store(0x1234, _))
.WillOnce(DoAll(SaveArgPointee<1>(&out_hash), Return(true)));
SetFirmwareManagementParametersRequest request;
request.set_flags(0x1234);
request.set_developer_key_hash(hash.to_string());
service_.DoSetFirmwareManagementParameters(BlobFromProtobuf(request), NULL);
BaseReply reply = GetLastReply();
EXPECT_FALSE(reply.has_error());
EXPECT_EQ(hash, out_hash);
}
TEST_F(ServiceExTest, SetFirmwareManagementParametersNoHash) {
brillo::Blob hash(0);
SetupReply();
EXPECT_CALL(fwmp_, Create())
.WillOnce(Return(true));
EXPECT_CALL(fwmp_, Store(0x1234, NULL))
.WillOnce(Return(true));
SetFirmwareManagementParametersRequest request;
request.set_flags(0x1234);
service_.DoSetFirmwareManagementParameters(BlobFromProtobuf(request), NULL);
BaseReply reply = GetLastReply();
EXPECT_FALSE(reply.has_error());
}
TEST_F(ServiceExTest, SetFirmwareManagementParametersCreateError) {
brillo::SecureBlob hash("its_a_hash");
SetupReply();
EXPECT_CALL(fwmp_, Create())
.WillOnce(Return(false));
SetFirmwareManagementParametersRequest request;
request.set_flags(0x1234);
request.set_developer_key_hash(hash.to_string());
service_.DoSetFirmwareManagementParameters(BlobFromProtobuf(request), NULL);
BaseReply reply = GetLastReply();
EXPECT_TRUE(reply.has_error());
EXPECT_EQ(CRYPTOHOME_ERROR_FIRMWARE_MANAGEMENT_PARAMETERS_CANNOT_STORE,
reply.error());
}
TEST_F(ServiceExTest, SetFirmwareManagementParametersStoreError) {
brillo::SecureBlob hash("its_a_hash");
SetupReply();
EXPECT_CALL(fwmp_, Create())
.WillOnce(Return(true));
EXPECT_CALL(fwmp_, Store(_, _))
.WillOnce(Return(false));
SetFirmwareManagementParametersRequest request;
request.set_flags(0x1234);
request.set_developer_key_hash(hash.to_string());
service_.DoSetFirmwareManagementParameters(BlobFromProtobuf(request), NULL);
BaseReply reply = GetLastReply();
EXPECT_TRUE(reply.has_error());
EXPECT_EQ(CRYPTOHOME_ERROR_FIRMWARE_MANAGEMENT_PARAMETERS_CANNOT_STORE,
reply.error());
}
TEST_F(ServiceExTest, RemoveFirmwareManagementParametersSuccess) {
SetupReply();
EXPECT_CALL(fwmp_, Destroy())
.WillOnce(Return(true));
RemoveFirmwareManagementParametersRequest request;
service_.DoRemoveFirmwareManagementParameters(BlobFromProtobuf(request),
NULL);
BaseReply reply = GetLastReply();
EXPECT_FALSE(reply.has_error());
}
TEST_F(ServiceExTest, RemoveFirmwareManagementParametersError) {
SetupReply();
EXPECT_CALL(fwmp_, Destroy())
.WillOnce(Return(false));
RemoveFirmwareManagementParametersRequest request;
service_.DoRemoveFirmwareManagementParameters(BlobFromProtobuf(request),
NULL);
BaseReply reply = GetLastReply();
EXPECT_TRUE(reply.has_error());
EXPECT_EQ(CRYPTOHOME_ERROR_FIRMWARE_MANAGEMENT_PARAMETERS_CANNOT_REMOVE,
reply.error());
}
void ImmediatelySignalOwnership(TpmInit::OwnershipCallback callback) {
callback.Run(true, false);
}
TEST_F(ServiceTestNotInitialized, CheckTpmInitRace) {
NiceMock<MockTpm> tpm;
NiceMock<MockTpmInit> tpm_init;
// Emulate quick tpm initialization by calling the ownership callback from
// TpmInit::Init(). In reality, it is called from the thread created by
// TpmInit::AsyncTakeOwnership(), but since it's guarded by a command line
// switch, call it from Init() instead. It should be safe to call the
// ownership callback from the main thread.
EXPECT_CALL(tpm_init, Init(_))
.WillOnce(Invoke(ImmediatelySignalOwnership));
service_.set_tpm(&tpm);
service_.set_tpm_init(&tpm_init);
service_.set_initialize_tpm(true);
service_.Initialize();
}
TEST_F(ServiceTestNotInitialized, CheckTpmGetPassword) {
NiceMock<MockTpmInit> tpm_init;
service_.set_tpm_init(&tpm_init);
std::string pwd1_ascii_str("abcdefgh", 8);
SecureBlob pwd1_ascii_blob(pwd1_ascii_str);
std::string pwd2_non_ascii_str("ab\xB2\xFF\x00\xA0gh", 8);
SecureBlob pwd2_non_ascii_blob(pwd2_non_ascii_str);
std::wstring pwd2_non_ascii_wstr(
pwd2_non_ascii_blob.data(),
pwd2_non_ascii_blob.data() + pwd2_non_ascii_blob.size());
std::string pwd2_non_ascii_str_utf8(base::SysWideToUTF8(pwd2_non_ascii_wstr));
EXPECT_CALL(tpm_init, GetTpmPassword(_))
.WillOnce(Return(false))
.WillOnce(DoAll(SetArgPointee<0>(pwd1_ascii_blob), Return(true)))
.WillOnce(DoAll(SetArgPointee<0>(pwd2_non_ascii_blob), Return(true)));
gchar* res_pwd;
GError* res_err;
// Return success and NULL if getting tpm password failed.
EXPECT_TRUE(service_.TpmGetPassword(&res_pwd, &res_err));
EXPECT_EQ(NULL, res_pwd);
g_free(res_pwd);
// Check that the ASCII password is returned as is.
EXPECT_TRUE(service_.TpmGetPassword(&res_pwd, &res_err));
EXPECT_EQ(0, memcmp(pwd1_ascii_str.data(),
res_pwd,
pwd1_ascii_str.size()));
g_free(res_pwd);
// Check that non-ASCII password is converted to UTF-8.
EXPECT_TRUE(service_.TpmGetPassword(&res_pwd, &res_err));
EXPECT_EQ(0, memcmp(pwd2_non_ascii_str_utf8.data(),
res_pwd,
pwd2_non_ascii_str_utf8.size()));
g_free(res_pwd);
}
} // namespace cryptohome