diff options
author | Paul Cristian Sarbu <paul.cristian.sarbu@huawei.com> | 2023-02-13 15:31:01 +0100 |
---|---|---|
committer | Paul Cristian Sarbu <paul.cristian.sarbu@huawei.com> | 2023-03-03 16:21:14 +0100 |
commit | 91f33def73223721ad91f99040158f3ff54dffab (patch) | |
tree | 1781e6e22ff169fafc81697d6502bc9dc9f396cb /src/other_tools/git_operations | |
parent | cab6734ee8d6ffc5a001144aead0ce6c79c90048 (diff) | |
download | justbuild-91f33def73223721ad91f99040158f3ff54dffab.tar.gz |
GitRepoRemote: Correctly honor SSL certification settings in fetch and commit update
Uses the SSL certification utility method to correctly set the
certification check options for the remote URL libgit2 calls.
Due to the fact that remote operations are done via a temporary
repository to allow concurrent work, the correct repository
configuration needs to be interrogated. Thankfully, libgit2
provides a thread safe config snapshot object to be used in
such scenarios.
Also updates the existing GitRepoRemote tests accordingly.
Diffstat (limited to 'src/other_tools/git_operations')
-rw-r--r-- | src/other_tools/git_operations/TARGETS | 1 | ||||
-rw-r--r-- | src/other_tools/git_operations/git_repo_remote.cpp | 139 | ||||
-rw-r--r-- | src/other_tools/git_operations/git_repo_remote.hpp | 12 |
3 files changed, 97 insertions, 55 deletions
diff --git a/src/other_tools/git_operations/TARGETS b/src/other_tools/git_operations/TARGETS index 27be26ed..19dabe4f 100644 --- a/src/other_tools/git_operations/TARGETS +++ b/src/other_tools/git_operations/TARGETS @@ -32,6 +32,7 @@ , ["src/buildtool/file_system", "git_utils"] , ["@", "fmt", "", "fmt"] , ["", "libgit2"] + , "git_config_settings" ] } , "git_config_settings": diff --git a/src/other_tools/git_operations/git_repo_remote.cpp b/src/other_tools/git_operations/git_repo_remote.cpp index 7d2ee7f4..f599c108 100644 --- a/src/other_tools/git_operations/git_repo_remote.cpp +++ b/src/other_tools/git_operations/git_repo_remote.cpp @@ -17,6 +17,7 @@ #include "fmt/core.h" #include "src/buildtool/file_system/git_utils.hpp" #include "src/buildtool/logging/logger.hpp" +#include "src/other_tools/git_operations/git_config_settings.hpp" extern "C" { #include <git2.h> @@ -67,48 +68,6 @@ void fetch_backend_free(git_odb_backend* /*_backend*/) {} // A backend that can be used to fetch from the remote of another repository. auto const kFetchIntoODBParent = CreateFetchIntoODBParent(); -// callback to enable SSL certificate check for remote fetch -const auto certificate_check_cb = [](git_cert* /*cert*/, - int /*valid*/, - const char* /*host*/, - void* /*payload*/) -> int { return 1; }; - -// callback to remote fetch without an SSL certificate check -const auto certificate_passthrough_cb = [](git_cert* /*cert*/, - int /*valid*/, - const char* /*host*/, - void* /*payload*/) -> int { - return 0; -}; - -/// \brief Set a custom SSL certificate check callback to honor the existing Git -/// configuration of a repository trying to connect to a remote. -[[nodiscard]] auto SetCustomSSLCertificateCheckCallback(git_repository* repo) - -> git_transport_certificate_check_cb { - // check SSL verification settings, from most to least specific - std::optional<int> check_cert{std::nullopt}; - // check gitconfig; ignore errors - git_config* cfg{nullptr}; - int tmp{}; - if (git_repository_config(&cfg, repo) == 0 and - git_config_get_bool(&tmp, cfg, "http.sslVerify") == 0) { - check_cert = tmp; - } - if (not check_cert) { - // check for GIT_SSL_NO_VERIFY environment variable - const char* ssl_no_verify_var{std::getenv("GIT_SSL_NO_VERIFY")}; - if (ssl_no_verify_var != nullptr and - git_config_parse_bool(&tmp, ssl_no_verify_var) == 0) { - check_cert = tmp; - } - } - // cleanup memory - git_config_free(cfg); - // set callback - return (check_cert and check_cert.value() == 0) ? certificate_passthrough_cb - : certificate_check_cb; -} - } // namespace auto GitRepoRemote::Open(GitCASPtr git_cas) noexcept @@ -157,7 +116,8 @@ auto GitRepoRemote::InitAndOpen(std::filesystem::path const& repo_path, return std::nullopt; } -auto GitRepoRemote::GetCommitFromRemote(std::string const& repo_url, +auto GitRepoRemote::GetCommitFromRemote(std::shared_ptr<git_config> cfg, + std::string const& repo_url, std::string const& branch, anon_logger_ptr const& logger) noexcept -> std::optional<std::string> { @@ -183,14 +143,34 @@ auto GitRepoRemote::GetCommitFromRemote(std::string const& repo_url, } auto remote = std::unique_ptr<git_remote, decltype(&remote_closer)>( remote_ptr, remote_closer); + // get the canonical url + auto canonical_url = std::string(git_remote_url(remote.get())); + + // get a well-defined config file + if (not cfg) { + // get config snapshot of current repo (shared with caller) + cfg = GetConfigSnapshot(); + if (cfg == nullptr) { + (*logger)(fmt::format("retrieving config object in get commit " + "from remote failed with:\n{}", + GitLastError()), + true /*fatal*/); + return std::nullopt; + } + } // connect to remote git_remote_callbacks callbacks{}; git_remote_init_callbacks(&callbacks, GIT_REMOTE_CALLBACKS_VERSION); - // set custom SSL verification callback - callbacks.certificate_check = - SetCustomSSLCertificateCheckCallback(GetRepoRef().get()); + // set custom SSL verification callback; use canonicalized url + auto cert_check = + GitConfigSettings::GetSSLCallback(cfg, canonical_url, logger); + if (not cert_check) { + // error occurred while handling the url + return std::nullopt; + } + callbacks.certificate_check = *cert_check; git_proxy_options proxy_opts{}; git_proxy_options_init(&proxy_opts, GIT_PROXY_OPTIONS_VERSION); @@ -251,7 +231,8 @@ auto GitRepoRemote::GetCommitFromRemote(std::string const& repo_url, } } -auto GitRepoRemote::FetchFromRemote(std::string const& repo_url, +auto GitRepoRemote::FetchFromRemote(std::shared_ptr<git_config> cfg, + std::string const& repo_url, std::optional<std::string> const& branch, anon_logger_ptr const& logger) noexcept -> bool { @@ -279,6 +260,24 @@ auto GitRepoRemote::FetchFromRemote(std::string const& repo_url, // wrap remote object auto remote = std::unique_ptr<git_remote, decltype(&remote_closer)>( remote_ptr, remote_closer); + // get the canonical url + auto canonical_url = std::string(git_remote_url(remote.get())); + + // get a well-defined config file + if (not cfg) { + // get config snapshot of current repo + git_config* cfg_ptr{nullptr}; + if (git_repository_config_snapshot(&cfg_ptr, GetRepoRef().get()) != + 0) { + (*logger)(fmt::format("retrieving config object in fetch from " + "remote failed with:\n{}", + GitLastError()), + true /*fatal*/); + return false; + } + // share pointer with caller + cfg.reset(cfg_ptr, config_closer); + } // define default fetch options git_fetch_options fetch_opts{}; @@ -287,9 +286,25 @@ auto GitRepoRemote::FetchFromRemote(std::string const& repo_url, // set the option to auto-detect proxy settings fetch_opts.proxy_opts.type = GIT_PROXY_AUTO; - // set custom SSL verification callback - fetch_opts.callbacks.certificate_check = - SetCustomSSLCertificateCheckCallback(GetRepoRef().get()); + // set custom SSL verification callback; use canonicalized url + if (not cfg) { + // get config snapshot of current repo (shared with caller) + cfg = GetConfigSnapshot(); + if (cfg == nullptr) { + (*logger)(fmt::format("retrieving config object in fetch from " + "remote failed with:\n{}", + GitLastError()), + true /*fatal*/); + return false; + } + } + auto cert_check = + GitConfigSettings::GetSSLCallback(cfg, canonical_url, logger); + if (not cert_check) { + // error occurred while handling the url + return false; + } + fetch_opts.callbacks.certificate_check = *cert_check; // disable update of the FETCH_HEAD pointer fetch_opts.update_fetchhead = 0; @@ -352,7 +367,17 @@ auto GitRepoRemote::UpdateCommitViaTmpRepo( msg), fatal); }); - return tmp_repo->GetCommitFromRemote(repo_url, branch, wrapped_logger); + // get the config of the correct target repo + auto cfg = GetConfigSnapshot(); + if (cfg == nullptr) { + (*logger)(fmt::format("retrieving config object in update commit " + "via tmp repo failed with:\n{}", + GitLastError()), + true /*fatal*/); + return std::nullopt; + } + return tmp_repo->GetCommitFromRemote( + cfg, repo_url, branch, wrapped_logger); } catch (std::exception const& ex) { Logger::Log(LogLevel::Error, "update commit via tmp repo failed with:\n{}", @@ -395,7 +420,17 @@ auto GitRepoRemote::FetchViaTmpRepo(std::filesystem::path const& tmp_repo_path, "While doing branch fetch via tmp repo:\n{}", msg), fatal); }); - return tmp_repo->FetchFromRemote(repo_url, branch, wrapped_logger); + // get the config of the correct target repo + auto cfg = GetConfigSnapshot(); + if (cfg == nullptr) { + (*logger)(fmt::format("retrieving config object in fetch via " + "tmp repo failed with:\n{}", + GitLastError()), + true /*fatal*/); + return false; + } + return tmp_repo->FetchFromRemote( + cfg, repo_url, branch, wrapped_logger); } return false; } catch (std::exception const& ex) { diff --git a/src/other_tools/git_operations/git_repo_remote.hpp b/src/other_tools/git_operations/git_repo_remote.hpp index 334f7fca..8b0021b8 100644 --- a/src/other_tools/git_operations/git_repo_remote.hpp +++ b/src/other_tools/git_operations/git_repo_remote.hpp @@ -50,9 +50,12 @@ class GitRepoRemote : public GitRepo { /// \brief Retrieve commit hash from remote branch given its name. /// Only possible with real repository and thus non-thread-safe. + /// If non-null, use given config snapshot to interact with config entries; + /// otherwise, use a snapshot from the current repo and share pointer to it. /// Returns the retrieved commit hash, or nullopt if failure. /// It guarantees the logger is called exactly once with fatal if failure. [[nodiscard]] auto GetCommitFromRemote( + std::shared_ptr<git_config> cfg, std::string const& repo_url, std::string const& branch, anon_logger_ptr const& logger) noexcept -> std::optional<std::string>; @@ -60,9 +63,12 @@ class GitRepoRemote : public GitRepo { /// \brief Fetch from given remote. It can either fetch a given named /// branch, or it can fetch with base refspecs. /// Only possible with real repository and thus non-thread-safe. - /// Returns a success flag. - /// It guarantees the logger is called exactly once with fatal if failure. - [[nodiscard]] auto FetchFromRemote(std::string const& repo_url, + /// If non-null, use given config snapshot to interact with config entries; + /// otherwise, use a snapshot from the current repo and share pointer to it. + /// Returns a success flag. It guarantees the logger is called + /// exactly once with fatal if failure. + [[nodiscard]] auto FetchFromRemote(std::shared_ptr<git_config> cfg, + std::string const& repo_url, std::optional<std::string> const& branch, anon_logger_ptr const& logger) noexcept -> bool; |