diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/other_tools/git_operations/TARGETS | 12 | ||||
-rw-r--r-- | src/other_tools/git_operations/git_config_settings.cpp | 171 | ||||
-rw-r--r-- | src/other_tools/git_operations/git_config_settings.hpp | 49 |
3 files changed, 232 insertions, 0 deletions
diff --git a/src/other_tools/git_operations/TARGETS b/src/other_tools/git_operations/TARGETS index 7f5dede1..27be26ed 100644 --- a/src/other_tools/git_operations/TARGETS +++ b/src/other_tools/git_operations/TARGETS @@ -34,4 +34,16 @@ , ["", "libgit2"] ] } +, "git_config_settings": + { "type": ["@", "rules", "CC", "library"] + , "name": ["git_config_settings"] + , "hdrs": ["git_config_settings.hpp"] + , "srcs": ["git_config_settings.cpp"] + , "stage": ["src", "other_tools", "git_operations"] + , "private-deps": + [ ["src/other_tools/utils", "curl_url_handle"] + , ["", "libgit2"] + , ["@", "fmt", "", "fmt"] + ] + } } diff --git a/src/other_tools/git_operations/git_config_settings.cpp b/src/other_tools/git_operations/git_config_settings.cpp new file mode 100644 index 00000000..dd930883 --- /dev/null +++ b/src/other_tools/git_operations/git_config_settings.cpp @@ -0,0 +1,171 @@ +// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/other_tools/git_operations/git_config_settings.hpp" + +#include <map> + +#include "fmt/core.h" +#include "src/other_tools/utils/curl_url_handle.hpp" + +extern "C" { +#include <git2.h> +} + +namespace { + +void config_iter_closer(gsl::owner<git_config_iterator*> iter) { + git_config_iterator_free(iter); +} + +// 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 Custom comparison of matching degrees. Return true if left argument's +/// degree of matching is better that the right argument's. When both are +/// equally good matches, return true to make the latest entry win. +struct ConfigKeyMatchCompare { + [[nodiscard]] auto operator()(ConfigKeyMatchDegree const& left, + ConfigKeyMatchDegree const& right) const + -> bool { + if (left.host_len != right.host_len) { + return left.host_len > right.host_len; + } + if (left.path_len != right.path_len) { + return left.path_len > right.path_len; + } + if (left.user_matched != right.user_matched) { + return left.user_matched; + } + return true; + } +}; + +} // namespace + +auto GitConfigSettings::GetSSLCallback(std::shared_ptr<git_config> const& cfg, + std::string const& url, + anon_logger_ptr const& logger) + -> std::optional<git_transport_certificate_check_cb> { + try { + // check SSL verification settings, from most to least specific + std::optional<bool> check_cert{std::nullopt}; + int tmp{}; + // check if GIT_SSL_NO_VERIFY envariable is set (value is + // irrelevant) + const char* ssl_no_verify_var{std::getenv("GIT_SSL_NO_VERIFY")}; + if (ssl_no_verify_var != nullptr) { + check_cert = false; + } + else { + if (cfg != nullptr) { + // check all the url-specific gitconfig entries; if any key + // url matches, use the respective gitconfig entry value + auto parsed_url = CurlURLHandle::Create(url); + if (not parsed_url) { + // unexpected error occurred + (*logger)( + "While getting SSL callback:\nfailed to parse remote " + "URL", + true /*fatal*/); + return std::nullopt; + } + if (*parsed_url) { + // iterate over config entries of type + // "http.<url>.sslVerify" + git_config_iterator* iter_ptr{nullptr}; + if (git_config_iterator_glob_new( + &iter_ptr, cfg.get(), R"(http\..*\.sslverify)") == + 0) { + // wrap iterator + auto iter = + std::unique_ptr<git_config_iterator, + decltype(&config_iter_closer)>( + iter_ptr, config_iter_closer); + // set config key parsing offsets + const std::string::difference_type start_offset{ + 5}; // len("http.") + const std::string::difference_type end_offset{ + 10}; // len(".sslverify") + // define ordered container storing matches + std::map<ConfigKeyMatchDegree, + std::string, + ConfigKeyMatchCompare> + matches{}; + // iterate through config keys + git_config_entry* entry{nullptr}; + while (git_config_next(&entry, iter.get()) == 0) { + // get the url part of the config key + std::string entry_name{entry->name}; + auto entry_url = + std::string(entry_name.begin() + start_offset, + entry_name.end() - end_offset); + // get match degree + auto match = + parsed_url.value()->MatchConfigKey(entry_url); + if (not match) { + // unexpected behavior + (*logger)( + "While getting SSL callback:\nmatching " + "config key failed", + true /*fatal*/); + return std::nullopt; + } + // store in ordered list only if a match + // occurred + if (match->matched) { + matches.emplace(*match, + std::string(entry->value)); + } + } + // if at least one match occurred, use the best one + if (not matches.empty()) { + if (git_config_parse_bool( + &tmp, matches.begin()->second.c_str()) == + 0) { + check_cert = tmp == 1; + } + } + } + } + if (not check_cert) { + // check the generic gitconfig entry; ignore errors + if (git_config_get_bool( + &tmp, cfg.get(), R"(http.sslverify)") == 0) { + check_cert = tmp == 1; + } + } + } + } + // set callback: passthrough only if check_cert is false + return (check_cert and not *check_cert) ? certificate_passthrough_cb + : certificate_check_cb; + } catch (std::exception const& ex) { + (*logger)( + fmt::format("Getting SSL callback failed with:\n{}", ex.what()), + true /*fatal*/); + return std::nullopt; + } +} diff --git a/src/other_tools/git_operations/git_config_settings.hpp b/src/other_tools/git_operations/git_config_settings.hpp new file mode 100644 index 00000000..8aa412e8 --- /dev/null +++ b/src/other_tools/git_operations/git_config_settings.hpp @@ -0,0 +1,49 @@ +// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef INCLUDED_SRC_OTHER_TOOLS_GIT_OPERATIONS_GIT_CONFIG_SETTINGS_HPP +#define INCLUDED_SRC_OTHER_TOOLS_GIT_OPERATIONS_GIT_CONFIG_SETTINGS_HPP + +#include <functional> +#include <memory> +#include <optional> +#include <string> + +extern "C" { +struct git_cert; +struct git_config; +using git_transport_certificate_check_cb = auto (*)(git_cert*, + int, + const char*, + void*) -> int; +} + +// Internal type used for logging with AsyncMaps +using anon_logger_t = std::function<void(std::string const&, bool)>; +using anon_logger_ptr = std::shared_ptr<anon_logger_t>; + +namespace GitConfigSettings { + +/// \brief Get a custom SSL certificate check callback to honor the existing +/// Git configuration of a repository trying to connect to a remote. +/// A null config snapshot reference will simply be ignored. +/// Returns nullopt if errors. +[[nodiscard]] auto GetSSLCallback(std::shared_ptr<git_config> const& cfg, + std::string const& url, + anon_logger_ptr const& logger) + -> std::optional<git_transport_certificate_check_cb>; + +} // namespace GitConfigSettings + +#endif // INCLUDED_SRC_OTHER_TOOLS_GIT_OPERATIONS_GIT_CONFIG_SETTINGS_HPP |