From 983aeea57d75494fd4ea2ff2903f966136278c15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= Date: Wed, 9 Feb 2022 13:17:00 +0100 Subject: [PATCH 28/34] Add private API for filling, reading and verifying new dnf solv userdata --- libdnf/hy-iutil-private.hpp | 24 +++++++++ libdnf/hy-iutil.cpp | 102 ++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) diff --git a/libdnf/hy-iutil-private.hpp b/libdnf/hy-iutil-private.hpp index e07b1b51..d498c032 100644 --- a/libdnf/hy-iutil-private.hpp +++ b/libdnf/hy-iutil-private.hpp @@ -24,6 +24,30 @@ #include "hy-iutil.h" #include "hy-types.h" #include "sack/packageset.hpp" +#include +#include + +// Use 8 bytes for libsolv version (API: solv_toolversion) +// to be future proof even though it currently is "1.2" +static constexpr const size_t solv_userdata_solv_toolversion_size{8}; +static constexpr const std::array solv_userdata_magic{'\0', 'd', 'n', 'f'}; +static constexpr const std::array solv_userdata_dnf_version{'\0', '1', '.', '0'}; + +static constexpr const int solv_userdata_size = solv_userdata_solv_toolversion_size + \ + solv_userdata_magic.size() + \ + solv_userdata_dnf_version.size() + \ + CHKSUM_BYTES; + +struct SolvUserdata { + char dnf_magic[solv_userdata_magic.size()]; + char dnf_version[solv_userdata_dnf_version.size()]; + char libsolv_version[solv_userdata_solv_toolversion_size]; + unsigned char checksum[CHKSUM_BYTES]; +}__attribute__((packed)); ; + +int solv_userdata_fill(SolvUserdata *solv_userdata, const unsigned char *checksum, GError** error); +std::unique_ptr solv_userdata_read(FILE *fp); +int solv_userdata_verify(const SolvUserdata *solv_userdata, const unsigned char *checksum); /* crypto utils */ int checksum_cmp(const unsigned char *cs1, const unsigned char *cs2); diff --git a/libdnf/hy-iutil.cpp b/libdnf/hy-iutil.cpp index 2af13197..f81ca52f 100644 --- a/libdnf/hy-iutil.cpp +++ b/libdnf/hy-iutil.cpp @@ -43,6 +43,7 @@ extern "C" { #include #include #include +#include #include #include } @@ -182,6 +183,107 @@ int checksum_write(const unsigned char *cs, FILE *fp) return 0; } +static std::array +get_padded_solv_toolversion() +{ + std::array padded_solv_toolversion{}; + std::string solv_ver_str{solv_toolversion}; + std::copy(solv_ver_str.rbegin(), solv_ver_str.rend(), padded_solv_toolversion.rbegin()); + + return padded_solv_toolversion; +} + +int +solv_userdata_fill(SolvUserdata *solv_userdata, const unsigned char *checksum, GError** error) +{ + if (strlen(solv_toolversion) > solv_userdata_solv_toolversion_size) { + g_set_error(error, DNF_ERROR, DNF_ERROR_INTERNAL_ERROR, + _("Libsolv's solv_toolversion is: %zu long but we expect max of: %zu"), + strlen(solv_toolversion), solv_userdata_solv_toolversion_size); + return 1; + } + + // copy dnf solv file magic + memcpy(solv_userdata->dnf_magic, solv_userdata_magic.data(), solv_userdata_magic.size()); + + // copy dnf solv file version + memcpy(solv_userdata->dnf_version, solv_userdata_dnf_version.data(), solv_userdata_dnf_version.size()); + + // copy libsolv solv file version + memcpy(solv_userdata->libsolv_version, get_padded_solv_toolversion().data(), solv_userdata_solv_toolversion_size); + + // copy checksum + memcpy(solv_userdata->checksum, checksum, CHKSUM_BYTES); + + return 0; +} + + +std::unique_ptr +solv_userdata_read(FILE *fp) +{ + unsigned char *dnf_solvfile_userdata_read = NULL; + int dnf_solvfile_userdata_len_read; + if (!fp) { + return nullptr; + } + + int ret_code = solv_read_userdata(fp, &dnf_solvfile_userdata_read, &dnf_solvfile_userdata_len_read); + // The userdata layout has to match our struct exactly so we can just cast the memory + // allocated by libsolv + std::unique_ptr uniq_userdata(reinterpret_cast(dnf_solvfile_userdata_read)); + if(ret_code) { + g_warning("Failed to read solv userdata: solv_read_userdata returned: %i", ret_code); + return nullptr; + } + + if (dnf_solvfile_userdata_len_read != solv_userdata_size) { + g_warning("Solv userdata length mismatch, read: %i vs expected: %i", + dnf_solvfile_userdata_len_read, solv_userdata_size); + return nullptr; + } + + return uniq_userdata; +} + +gboolean +solv_userdata_verify(const SolvUserdata *solv_userdata, const unsigned char *checksum) +{ + // check dnf solvfile magic bytes + if (memcmp(solv_userdata->dnf_magic, solv_userdata_magic.data(), solv_userdata_magic.size()) != 0) { + // This is not dnf header do not read after it + g_warning("magic bytes don't match, read: %s vs. dnf solvfile magic: %s", + solv_userdata->dnf_magic, solv_userdata_magic.data()); + return FALSE; + } + + // check dnf solvfile version + if (memcmp(solv_userdata->dnf_version, solv_userdata_dnf_version.data(), solv_userdata_dnf_version.size()) != 0) { + // Mismatching dnf solvfile version -> we need to regenerate + g_warning("dnf solvfile version doesn't match, read: %s vs. dnf solvfile version: %s", + solv_userdata->dnf_version, solv_userdata_dnf_version.data()); + return FALSE; + } + + // check libsolv solvfile version + if (memcmp(solv_userdata->libsolv_version, get_padded_solv_toolversion().data(), solv_userdata_solv_toolversion_size) != 0) { + // Mismatching libsolv solvfile version -> we need to regenerate + g_warning("libsolv solvfile version doesn't match, read: %s vs. libsolv version: %s", + solv_userdata->libsolv_version, solv_toolversion); + return FALSE; + } + + // check solvfile checksum + if (checksum_cmp(solv_userdata->checksum, checksum)) { + // Mismatching solvfile checksum -> we need to regenerate + g_debug("solvfile checksum doesn't match, read: %s vs. repomd checksum: %s", + solv_userdata->checksum, checksum); + return FALSE; + } + + return TRUE; +} + int checksum_type2length(int type) { -- 2.31.1