From 0722bb3bf4195ac4ffcbc9df66ef620b4d8f7a22 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Wed, 15 Feb 2023 22:58:43 +0100 Subject: [PATCH] Change: try to detect the CA file/path for CURL (#10481) The default is given compile-time, not run-time. So libcurl is of no use to us. Current list is kindly borrowed from https://go.dev/src/crypto/x509/root_linux.go --- src/network/core/http_curl.cpp | 65 ++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/network/core/http_curl.cpp b/src/network/core/http_curl.cpp index 1727cf24b3..188d9b7b63 100644 --- a/src/network/core/http_curl.cpp +++ b/src/network/core/http_curl.cpp @@ -11,6 +11,7 @@ #include "../../stdafx.h" #include "../../debug.h" +#include "../../fileio_func.h" #include "../../rev.h" #include "../../thread.h" #include "../network_internal.h" @@ -26,6 +27,24 @@ #include "../../safeguards.h" +#if defined(UNIX) +/** List of certificate bundles, depending on OS. Taken from: https://go.dev/src/crypto/x509/root_linux.go. */ +static auto _certificate_files = { + "/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc. + "/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL 6 + "/etc/ssl/ca-bundle.pem", // OpenSUSE + "/etc/pki/tls/cacert.pem", // OpenELEC + "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // CentOS/RHEL 7 + "/etc/ssl/cert.pem", // Alpine Linux +}; +/** List of certificate directories, depending on OS. Taken from: https://go.dev/src/crypto/x509/root_linux.go. */ +static auto _certificate_directories = { + "/etc/ssl/certs", // SLES10/SLES11, https://golang.org/issue/12139 + "/etc/pki/tls/certs", // Fedora/RHEL + "/system/etc/security/cacerts", // Android +}; +#endif /* UNIX */ + /** Single HTTP request. */ class NetworkHTTPRequest { public: @@ -61,9 +80,20 @@ static std::atomic _http_thread_exit = false; static std::queue> _http_requests; static std::mutex _http_mutex; static std::condition_variable _http_cv; +#if defined(UNIX) +static std::string _http_ca_file = ""; +static std::string _http_ca_path = ""; +#endif /* UNIX */ /* static */ void NetworkHTTPSocketHandler::Connect(const std::string &uri, HTTPCallback *callback, const char *data) { +#if defined(UNIX) + if (_http_ca_file.empty() && _http_ca_path.empty()) { + callback->OnFailure(); + return; + } +#endif /* UNIX */ + std::lock_guard lock(_http_mutex); _http_requests.push(std::make_unique(uri, callback, data)); _http_cv.notify_one(); @@ -106,6 +136,14 @@ void HttpThread() curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5L); + /* Ensure we validate the certificate and hostname of the server. */ +#if defined(UNIX) + curl_easy_setopt(curl, CURLOPT_CAINFO, _http_ca_file.empty() ? nullptr : _http_ca_file.c_str()); + curl_easy_setopt(curl, CURLOPT_CAPATH, _http_ca_path.empty() ? nullptr : _http_ca_path.c_str()); +#endif /* UNIX */ + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, true); + /* Give the connection about 10 seconds to complete. */ curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L); @@ -159,6 +197,33 @@ void NetworkHTTPInitialize() { curl_global_init(CURL_GLOBAL_DEFAULT); +#if defined(UNIX) + /* Depending on the Linux distro, certificates can either be in + * a bundle or a folder, in a wide range of different locations. + * Try to find what location is used by this OS. */ + for (auto &ca_file : _certificate_files) { + if (FileExists(ca_file)) { + _http_ca_file = ca_file; + break; + } + } + if (_http_ca_file.empty()) { + for (auto &ca_path : _certificate_directories) { + if (FileExists(ca_path)) { + _http_ca_path = ca_path; + break; + } + } + } + Debug(net, 3, "Using certificate file: {}", _http_ca_file.empty() ? "none" : _http_ca_file); + Debug(net, 3, "Using certificate path: {}", _http_ca_path.empty() ? "none" : _http_ca_path); + + /* Tell the user why HTTPS will not be working. */ + if (_http_ca_file.empty() && _http_ca_path.empty()) { + Debug(net, 0, "No certificate files or directories found, HTTPS will not work!"); + } +#endif /* UNIX */ + _http_thread_exit = false; StartNewThread(&_http_thread, "ottd:http", &HttpThread); }