diff --git a/src/libaktualizr/storage/CMakeLists.txt b/src/libaktualizr/storage/CMakeLists.txt index 296b1fca5..eca397ef9 100644 --- a/src/libaktualizr/storage/CMakeLists.txt +++ b/src/libaktualizr/storage/CMakeLists.txt @@ -23,7 +23,10 @@ target_sources(config PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/storage_config.cc) add_aktualizr_test(NAME storage_atomic SOURCES storage_atomic_test.cc PROJECT_WORKING_DIRECTORY) add_aktualizr_test(NAME sql_utils SOURCES sql_utils_test.cc PROJECT_WORKING_DIRECTORY) add_aktualizr_test(NAME sqlstorage SOURCES sqlstorage_test.cc ARGS ${CMAKE_CURRENT_SOURCE_DIR}/test) -add_aktualizr_test(NAME storage_common SOURCES storage_common_test.cc PROJECT_WORKING_DIRECTORY) +add_aktualizr_test(NAME storage_common + SOURCES storage_common_test.cc + LIBRARIES uptane_generator_lib + PROJECT_WORKING_DIRECTORY) add_test(NAME test_schema_migration COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/schema_migration_test.sh ${PROJECT_SOURCE_DIR}/config/sql) diff --git a/src/libaktualizr/storage/invstorage.cc b/src/libaktualizr/storage/invstorage.cc index cbd1c31e7..241ed270e 100644 --- a/src/libaktualizr/storage/invstorage.cc +++ b/src/libaktualizr/storage/invstorage.cc @@ -118,6 +118,33 @@ void INvStorage::importInstalledVersions(const boost::filesystem::path& base_pat } } +void INvStorage::importInitialRoot(const boost::filesystem::path& base_path) { + importInitialRootFile(base_path / "repo/root.json", Uptane::RepositoryType::Image()); + importInitialRootFile(base_path / "director/root.json", Uptane::RepositoryType::Director()); +} + +void INvStorage::importInitialRootFile(const boost::filesystem::path& root_path, Uptane::RepositoryType repo_type) { + std::string root_tmp; // Only needed for loadLatestRoot + if (!loadLatestRoot(&root_tmp, repo_type)) { + if (boost::filesystem::is_regular_file(root_path)) { + try { + std::string root_str = Utils::readFile(root_path); + Uptane::Root orig_root(Uptane::Root::Policy::kAcceptAll); + Uptane::Root new_root(repo_type, Utils::parseJSON(root_str), orig_root); + // No exception. Save it + storeRoot(root_str, repo_type, Uptane::Version(new_root.version())); + LOG_INFO << "Imported initial " << repo_type << " root keys from " << root_path; + } catch (Uptane::Exception& e) { + LOG_WARNING << "Couldn't import initial " << repo_type << " root keys from " << root_path << " " << e.what(); + } + } else { + LOG_DEBUG << "Not importing " << root_path << " because it doesn't exist"; + } + } else { + LOG_TRACE << "Root for " << repo_type << " already present, not importing"; + } +} + void INvStorage::importData(const ImportConfig& import_config) { importPrimaryKeys(import_config.base_path, import_config.uptane_public_key_path, import_config.uptane_private_key_path); @@ -127,6 +154,7 @@ void INvStorage::importData(const ImportConfig& import_config) { importUpdateSimple(import_config.base_path, &INvStorage::storeTlsPkey, &INvStorage::loadTlsPkey, import_config.tls_pkey_path, "client TLS key"); importInstalledVersions(import_config.base_path); + importInitialRoot(import_config.base_path); } std::shared_ptr INvStorage::newStorage(const StorageConfig& config, const bool readonly) { diff --git a/src/libaktualizr/storage/invstorage.h b/src/libaktualizr/storage/invstorage.h index a93efacd0..7666a7cca 100644 --- a/src/libaktualizr/storage/invstorage.h +++ b/src/libaktualizr/storage/invstorage.h @@ -174,6 +174,17 @@ class INvStorage { void importPrimaryKeys(const boost::filesystem::path& base_path, const utils::BasedPath& import_pubkey_path, const utils::BasedPath& import_privkey_path); + /** + * Import initial image and director root.json from the filesystem. + * These would be loaded onto the device during provisioning at a well-known + * location such as /var/sota/import/repo/root.json (image repo) and + * /var/sota/import/director/root.json for the director repo. + * + * @param base_path e.g. '/var/sota/import' + */ + void importInitialRoot(const boost::filesystem::path& base_path); + void importInitialRootFile(const boost::filesystem::path& root_path, Uptane::RepositoryType repo_type); + protected: const StorageConfig config_; }; diff --git a/src/libaktualizr/storage/storage_common_test.cc b/src/libaktualizr/storage/storage_common_test.cc index 9d8c0d52b..26b974c89 100644 --- a/src/libaktualizr/storage/storage_common_test.cc +++ b/src/libaktualizr/storage/storage_common_test.cc @@ -6,17 +6,20 @@ #include #include "libaktualizr/types.h" +#include "repo.h" #include "storage/sqlstorage.h" #include "utilities/utils.h" -std::unique_ptr Storage(const boost::filesystem::path &dir) { +namespace fs = boost::filesystem; + +std::unique_ptr Storage(const fs::path &dir) { StorageConfig storage_config; storage_config.type = StorageType::kSqlite; storage_config.path = dir; return std::unique_ptr(new SQLStorage(storage_config, false)); } -StorageConfig MakeConfig(StorageType type, const boost::filesystem::path &storage_dir) { +StorageConfig MakeConfig(StorageType type, const fs::path &storage_dir) { StorageConfig config; config.type = type; @@ -524,7 +527,7 @@ TEST(StorageCommon, LoadStoreSecondaryInfo) { TEST(StorageImport, ImportData) { TemporaryDirectory temp_dir; std::unique_ptr storage = Storage(temp_dir.Path()); - boost::filesystem::create_directories(temp_dir / "import"); + fs::create_directories(temp_dir / "import"); ImportConfig import_config; import_config.base_path = temp_dir.Path() / "import"; @@ -640,6 +643,43 @@ TEST(StorageImport, ImportData) { EXPECT_EQ(tls_pkey, tls_pkey_in3); } +TEST(StorageImport, ImportInitialRoot) { + TemporaryDirectory temp_dir; + std::unique_ptr storage = Storage(temp_dir.Path()); + fs::create_directories(temp_dir / "import"); + + ImportConfig import_config; + import_config.base_path = temp_dir.Path() / "import"; + + // Generate a set of valid Uptane root keys + auto repo_path = temp_dir.Path() / "repo"; + Repo image_repo{Uptane::RepositoryType::Image(), repo_path, "", ""}; + image_repo.generateRepo(); + Repo director_repo{Uptane::RepositoryType::Director(), repo_path, "", ""}; + director_repo.generateRepo(); + director_repo.rotate(Uptane::Role::Root()); + + EXPECT_FALSE(storage->loadLatestRoot(nullptr, Uptane::RepositoryType::Image())); + EXPECT_FALSE(storage->loadLatestRoot(nullptr, Uptane::RepositoryType::Director())); + + fs::create_directories(import_config.base_path / "repo"); + fs::create_directories(import_config.base_path / "director"); + + fs::copy(repo_path / "repo/repo/root.json", import_config.base_path / "repo/root.json"); + Utils::writeFile(import_config.base_path / "director/root.json", std::string("invalid")); + + storage->importData(import_config); + EXPECT_TRUE(storage->loadLatestRoot(nullptr, Uptane::RepositoryType::Image())); + EXPECT_FALSE(storage->loadLatestRoot(nullptr, Uptane::RepositoryType::Director())) + << "Director root.json was invalid. It shouldn't have been imported"; + + // Copy the real director root.json over + fs::copy_file(repo_path / "repo/director/root.json", import_config.base_path / "director/root.json", + fs::copy_option::overwrite_if_exists); + storage->importData(import_config); + EXPECT_TRUE(storage->loadLatestRoot(nullptr, Uptane::RepositoryType::Director())); +} + #ifndef __NO_MAIN__ int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv);