blob: bf02cd4962e6b81590f06dc9ea5c71503be566f7 [file] [log] [blame]
// Copyright 2019 The Fuchsia 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 <dirent.h>
#include <fcntl.h>
#include <fidl/fuchsia.boot/cpp/test_base.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/zx/object.h>
#include <unistd.h>
#include <zircon/status.h>
#include <array>
#include <memory>
#include <string>
#include <thread>
#include <fbl/unique_fd.h>
#include <gtest/gtest.h>
#include "src/developer/sshd-host/constants.h"
#include "src/developer/sshd-host/service.h"
#include "src/lib/testing/loop_fixture/real_loop_fixture.h"
namespace sshd_host {
namespace {
using fuchsia_boot::Items;
// Mock fuchsia.boot.Items server
class FakeItems final : public fidl::testing::TestBase<Items> {
public:
void NotImplemented_(const std::string &name, fidl::CompleterBase &completer) override {
ASSERT_TRUE(false) << name << " not implemented.";
}
void GetBootloaderFile(GetBootloaderFileRequest &request,
GetBootloaderFileCompleter::Sync &completer) override {
if (!bootloader_file_set_) {
completer.Reply({{.payload = zx::vmo(ZX_HANDLE_INVALID)}});
return;
}
bootloader_file_set_ = false;
if (request.filename() == filename_) {
completer.Reply({{.payload = zx::vmo(std::move(vmo_))}});
} else {
completer.Reply({{.payload = zx::vmo(ZX_HANDLE_INVALID)}});
}
}
void SetFile(const char *filename, const char *payload, uint64_t payload_len) {
filename_ = std::string(filename);
ASSERT_EQ(zx::vmo::create(payload_len, 0, &vmo_), ZX_OK);
ASSERT_EQ(vmo_.write((payload), 0, payload_len), ZX_OK);
ASSERT_EQ(vmo_.set_property(ZX_PROP_VMO_CONTENT_SIZE, &payload_len, sizeof(payload_len)),
ZX_OK);
bootloader_file_set_ = true;
}
private:
bool bootloader_file_set_ = false;
std::string filename_;
::zx::vmo vmo_;
};
class SshdHostBootItemTest : public gtest::RealLoopFixture {
public:
void SetUp() override {
EXPECT_EQ(loop_.StartThread(), ZX_OK);
auto items_endpoints = fidl::Endpoints<Items>::Create();
binding_ref_ =
fidl::BindServer(loop_.dispatcher(), std::move(items_endpoints.server), &fake_items_);
items_client_ = fidl::SyncClient(std::move(items_endpoints.client));
}
void TearDown() override {
loop_.Quit();
loop_.JoinThreads();
}
async::Loop loop_{&kAsyncLoopConfigNoAttachToCurrentThread};
FakeItems fake_items_;
std::optional<fidl::ServerBindingRef<Items>> binding_ref_;
fidl::SyncClient<Items> items_client_;
thrd_t thread_;
};
void remove_authorized_keys() {
if (unlink(kAuthorizedKeysPath)) {
ASSERT_EQ(errno, ENOENT);
}
if (rmdir(kSshDirectory)) {
ASSERT_EQ(errno, ENOENT);
}
}
void write_authorized_keys(const char *payload, size_t len) {
if (mkdir(kSshDirectory, 0700), 0) {
ASSERT_EQ(errno, EEXIST);
}
fbl::unique_fd kfd(open(kAuthorizedKeysPath, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR));
ASSERT_TRUE(kfd.is_valid());
ASSERT_EQ(write(kfd.get(), payload, len), ssize_t(len));
fsync(kfd.get());
ASSERT_EQ(close(kfd.release()), 0);
}
void verify_authorized_keys(const char *payload, size_t len) {
auto file_buf = std::make_unique<uint8_t[]>(len);
fbl::unique_fd kfd(open(kAuthorizedKeysPath, O_RDONLY));
ASSERT_TRUE(kfd.is_valid());
ASSERT_EQ(read(kfd.get(), file_buf.get(), len), ssize_t(len));
// verify entire file was read.
uint8_t tmp;
ASSERT_EQ(read(kfd.get(), &tmp, sizeof(uint8_t)), 0);
ASSERT_EQ(close(kfd.release()), 0);
ASSERT_EQ(memcmp(file_buf.get(), payload, len), 0);
}
// If key file does not exist and the bootloader file is not found, nothing should happen.
TEST_F(SshdHostBootItemTest, TestNoKeyFileNoBootloaderFile) {
remove_authorized_keys();
zx_status_t status = provision_authorized_keys_from_bootloader_file(items_client_);
ASSERT_EQ(status, ZX_ERR_NOT_FOUND);
ASSERT_EQ(opendir(kSshDirectory), nullptr);
ASSERT_EQ(errno, ENOENT);
}
// If key file exists and no bootloader file is found, file should be untouched.
TEST_F(SshdHostBootItemTest, TestKeyFileExistsNoBootloaderFile) {
constexpr char kAuthorizedKeysPayload[] = "authorized_keys_file_data";
write_authorized_keys(kAuthorizedKeysPayload, strlen(kAuthorizedKeysPayload));
zx_status_t status = provision_authorized_keys_from_bootloader_file(items_client_);
ASSERT_EQ(status, ZX_ERR_NOT_FOUND);
verify_authorized_keys(kAuthorizedKeysPayload, strlen(kAuthorizedKeysPayload));
}
// If key file does not exist and a bootloader file is found, file should be written.
TEST_F(SshdHostBootItemTest, TestBootloaderFileProvisioningNoKeyFile) {
constexpr char kAuthorizedKeysPayload[] = "authorized_keys_file_data_new";
fake_items_.SetFile(kAuthorizedKeysBootloaderFileName.data(), kAuthorizedKeysPayload,
strlen(kAuthorizedKeysPayload));
remove_authorized_keys();
zx_status_t status = provision_authorized_keys_from_bootloader_file(items_client_);
ASSERT_EQ(status, ZX_OK);
verify_authorized_keys(kAuthorizedKeysPayload, strlen(kAuthorizedKeysPayload));
}
// If key file does not exist, the ssh directory does exist and a bootloader file is found,
// key file should be written.
TEST_F(SshdHostBootItemTest, TestBootloaderFileProvisioningSshDirNoKeyFile) {
constexpr char kAuthorizedKeysPayload[] = "authorized_keys_file_data_new";
fake_items_.SetFile(kAuthorizedKeysBootloaderFileName.data(), kAuthorizedKeysPayload,
strlen(kAuthorizedKeysPayload));
remove_authorized_keys();
ASSERT_EQ(mkdir(kSshDirectory, 0700), 0);
zx_status_t status = provision_authorized_keys_from_bootloader_file(items_client_);
ASSERT_EQ(status, ZX_OK);
verify_authorized_keys(kAuthorizedKeysPayload, strlen(kAuthorizedKeysPayload));
}
// If key file already exists and a bootloader file is found, key file should not be changed.
TEST_F(SshdHostBootItemTest, TestBootloaderFileNotProvisionedWithExistingKeyFile) {
constexpr char kAuthorizedKeysPayload[] = "existing authorized_keys_file_data";
write_authorized_keys(kAuthorizedKeysPayload, strlen(kAuthorizedKeysPayload));
constexpr char kAuthorizedKeysBootItemPayload[] = "new authorized_keys_file_data";
fake_items_.SetFile(kAuthorizedKeysBootloaderFileName.data(), kAuthorizedKeysBootItemPayload,
strlen(kAuthorizedKeysBootItemPayload));
zx_status_t status = provision_authorized_keys_from_bootloader_file(items_client_);
ASSERT_EQ(status, ZX_ERR_ALREADY_EXISTS);
verify_authorized_keys(kAuthorizedKeysPayload, strlen(kAuthorizedKeysPayload));
}
} // namespace
} // namespace sshd_host