From 74337f040d1ddf1bc4a5ecfcd2a3508683447bc6 Mon Sep 17 00:00:00 2001 From: Adrien DELSALLE Date: Thu, 27 Jan 2022 17:27:48 +0100 Subject: [PATCH] generate PkgMgr role from its file definition update tests remove banners in configuration tests fix set_default value of configurable --- libmamba/include/mamba/api/configuration.hpp | 3 +- libmamba/include/mamba/core/validate.hpp | 3 +- libmamba/src/core/validate.cpp | 66 ++++++++++++++++-- libmamba/tests/test_configuration.cpp | 8 +++ libmamba/tests/test_validate.cpp | 72 ++++++++++++++++++-- 5 files changed, 139 insertions(+), 13 deletions(-) diff --git a/libmamba/include/mamba/api/configuration.hpp b/libmamba/include/mamba/api/configuration.hpp index 17be262674..dda2ca7907 100644 --- a/libmamba/include/mamba/api/configuration.hpp +++ b/libmamba/include/mamba/api/configuration.hpp @@ -958,7 +958,8 @@ namespace mamba template auto Configurable::set_default_value(const T& value) -> self_type& { - m_value = m_default_value; + m_default_value = value; + m_value = value; return *this; }; diff --git a/libmamba/include/mamba/core/validate.hpp b/libmamba/include/mamba/core/validate.hpp index 245fddc273..b4a03f295e 100644 --- a/libmamba/include/mamba/core/validate.hpp +++ b/libmamba/include/mamba/core/validate.hpp @@ -703,7 +703,8 @@ namespace validate // std::set roles() const override; RoleFullKeys self_keys() const override; - PkgMgrRole create_pkg_mgr() const; + PkgMgrRole create_pkg_mgr(const fs::path& p) const; + PkgMgrRole create_pkg_mgr(const json& j) const; /** * Return a ``RepoIndexChecker`` implementation (derived class) diff --git a/libmamba/src/core/validate.cpp b/libmamba/src/core/validate.cpp index 752a097a5f..97aec5bc37 100644 --- a/libmamba/src/core/validate.cpp +++ b/libmamba/src/core/validate.cpp @@ -1380,7 +1380,7 @@ namespace validate if ((result == CURLE_OK) && dl_target->finalize()) { - KeyMgrRole key_mgr(tmp_metadata_path, all_keys()["key_mgr"], spec_impl()); + KeyMgrRole key_mgr = create_key_mgr(tmp_metadata_path); // TUF spec 5.6.5 - Check for a freeze attack // 'key_mgr' (equivalent of 'targets') role should not be expired @@ -1407,7 +1407,7 @@ namespace validate // Fallback to local cached-copy if existing if (fs::exists(metadata_path)) { - KeyMgrRole key_mgr(metadata_path, all_keys()["key_mgr"], spec_impl()); + KeyMgrRole key_mgr = create_key_mgr(metadata_path); return key_mgr.build_index_checker(base_url, cache_path); } @@ -1504,16 +1504,68 @@ namespace validate return m_keys; } - PkgMgrRole KeyMgrRole::create_pkg_mgr() const + PkgMgrRole KeyMgrRole::create_pkg_mgr(const fs::path& p) const { - return PkgMgrRole(all_keys().at("pkg_mgr"), spec_impl()); + return PkgMgrRole(p, all_keys()["pkg_mgr"], spec_impl()); + } + + PkgMgrRole KeyMgrRole::create_pkg_mgr(const json& j) const + { + return PkgMgrRole(j, all_keys()["pkg_mgr"], spec_impl()); } std::unique_ptr KeyMgrRole::build_index_checker( - const std::string& url, const fs::path& cache_path) const + const std::string& base_url, const fs::path& cache_path) const { - auto pkg_mgr = std::make_unique(create_pkg_mgr()); - return pkg_mgr; + fs::path metadata_path = cache_path / "pkg_mgr.json"; + + auto tmp_dir = std::make_unique(); + auto tmp_metadata_path = tmp_dir->path() / "pkg_mgr.json"; + + mamba::URLHandler url(base_url + "/pkg_mgr.json"); + + auto dl_target = std::make_unique( + "pkg_mgr.json", url.url(), tmp_metadata_path); + + if (dl_target->resource_exists()) + { + auto result = curl_easy_perform(dl_target->handle()); + dl_target->set_result(result); + + if ((result == CURLE_OK) && dl_target->finalize()) + { + PkgMgrRole pkg_mgr = create_pkg_mgr(tmp_metadata_path); + + // TUF spec 5.6.5 - Check for a freeze attack + // 'pkg_mgr' (equivalent of delegated 'targets') role should not be expired + // https://theupdateframework.github.io/specification/latest/#update-targets + if (pkg_mgr.expired()) + { + LOG_ERROR << "Possible freeze attack of 'pkg_mgr' metadata.\nExpired: " + << pkg_mgr.expires(); + throw freeze_error(); + } + + // TUF spec 5.6.6 - Persist targets metadata + if (!cache_path.empty()) + { + if (fs::exists(metadata_path)) + fs::remove(metadata_path); + fs::copy(tmp_metadata_path, metadata_path); + } + + return std::make_unique(pkg_mgr); + } + } + + // Fallback to local cached-copy if existing + if (fs::exists(metadata_path)) + { + return std::make_unique(create_pkg_mgr(metadata_path)); + } + + LOG_ERROR << "Error while fetching 'pkg_mgr' metadata"; + throw fetching_error(); } std::set KeyMgrRole::mandatory_defined_roles() const diff --git a/libmamba/tests/test_configuration.cpp b/libmamba/tests/test_configuration.cpp index d8b65bd387..37e46d9a6c 100644 --- a/libmamba/tests/test_configuration.cpp +++ b/libmamba/tests/test_configuration.cpp @@ -34,6 +34,10 @@ namespace mamba .at("rc_files") .get_wrapped>() .set_value({ fs::path(unique_location) }); + mamba::Configuration::instance() + .at("show_banner") + .get_wrapped() + .set_default_value(false); mamba::Configuration::instance().load(); } @@ -59,6 +63,10 @@ namespace mamba .at("rc_files") .get_wrapped>() .set_value(sources); + mamba::Configuration::instance() + .at("show_banner") + .get_wrapped() + .set_default_value(false); mamba::Configuration::instance().load(); } diff --git a/libmamba/tests/test_validate.cpp b/libmamba/tests/test_validate.cpp index 7cc3bd0020..75bfb21a6a 100644 --- a/libmamba/tests/test_validate.cpp +++ b/libmamba/tests/test_validate.cpp @@ -1101,6 +1101,7 @@ namespace validate PkgMgrT_v06() : KeyMgrT_v06() { + sign_pkg_mgr(); generate_index_checkerdata(); root = std::make_unique(root1_json); }; @@ -1125,11 +1126,73 @@ namespace validate return updated_repodata; } + void sign_pkg_mgr() + { + std::vector pkg_mgr_pks; + for (auto& secret : secrets.at("pkg_mgr")) + { + pkg_mgr_pks.push_back(secret.first); + } + pkg_mgr_json["signed"]["delegations"] = json::object(); + + pkg_mgr_json["signed"]["version"] = 1; + pkg_mgr_json["signed"]["metadata_spec_version"] = "0.6.0"; + pkg_mgr_json["signed"]["type"] = "pkg_mgr"; + + pkg_mgr_json["signed"]["timestamp"] = timestamp(utc_time_now()); + pkg_mgr_json["signed"]["expiration"] = timestamp(utc_time_now() + 3600); + pkg_mgr_json["signatures"] = sign_pkg_mgr_meta(pkg_mgr_json["signed"]); + } + + json patched_pkg_mgr_json(const json& patch = json()) + { + json update_pkg_mgr = pkg_mgr_json; + + if (!patch.empty()) + update_pkg_mgr = update_pkg_mgr.patch(patch); + + json sig_patch = json::parse( + R"([ + { "op": "replace", "path": "/signatures", "value": )" + + sign_pkg_mgr_meta(update_pkg_mgr.at("signed")).dump() + R"( } + ])"); + return update_pkg_mgr.patch(sig_patch); + } + + fs::path write_pkg_mgr_file(const json& j, + const std::string& filename = "pkg_mgr.json") + { + fs::path p = channel_dir->path() / filename; + + std::ofstream out_file(p, std::ofstream::out | std::ofstream::trunc); + out_file << j; + out_file.close(); + + return p; + } + protected: - json repodata_json, signed_repodata_json; + json pkg_mgr_json, repodata_json, signed_repodata_json; std::unique_ptr root; + json sign_pkg_mgr_meta(const json& meta) + { + std::map> signatures; + + unsigned char sig_bin[MAMBA_ED25519_SIGSIZE_BYTES]; + + for (auto& secret : secrets.at("pkg_mgr")) + { + sign(meta.dump(2), secret.second.data(), sig_bin); + + auto sig_hex = ::mamba::hex_string(sig_bin, MAMBA_ED25519_SIGSIZE_BYTES); + signatures[secret.first].insert({ "signature", sig_hex }); + } + + return signatures; + } + void generate_index_checkerdata() { repodata_json = R"({ @@ -1182,7 +1245,7 @@ namespace validate TEST_F(PkgMgrT_v06, verify_index) { auto key_mgr = root->create_key_mgr(key_mgr_json); - auto pkg_mgr = key_mgr.create_pkg_mgr(); + auto pkg_mgr = key_mgr.create_pkg_mgr(pkg_mgr_json); pkg_mgr.verify_index(signed_repodata_json); } @@ -1190,7 +1253,7 @@ namespace validate TEST_F(PkgMgrT_v06, corrupted_repodata) { auto key_mgr = root->create_key_mgr(key_mgr_json); - auto pkg_mgr = key_mgr.create_pkg_mgr(); + auto pkg_mgr = key_mgr.create_pkg_mgr(pkg_mgr_json); json wrong_pkg_patch = R"([ { "op": "replace", "path": "/packages/test-package1-0.1-0.tar.bz2/version", "value": "0.1.1" } @@ -1202,7 +1265,7 @@ namespace validate TEST_F(PkgMgrT_v06, illformed_repodata) { auto key_mgr = root->create_key_mgr(key_mgr_json); - auto pkg_mgr = key_mgr.create_pkg_mgr(); + auto pkg_mgr = key_mgr.create_pkg_mgr(pkg_mgr_json); json illformed_pkg_patch = R"([ { "op": "remove", "path": "/signatures"} @@ -1229,6 +1292,7 @@ namespace validate write_role(create_root_update_json(patch), channel_dir->path() / "2.root.json"); write_role(key_mgr_json, channel_dir->path() / "key_mgr.json"); + write_role(pkg_mgr_json, channel_dir->path() / "pkg_mgr.json"); spdlog::set_level(spdlog::level::debug); }