diff --git a/android/res/mipmap-mdpi/meson.build b/android/res/mipmap-mdpi/meson.build index 1e0a807bf..4cb9810c3 100644 --- a/android/res/mipmap-mdpi/meson.build +++ b/android/res/mipmap-mdpi/meson.build @@ -1,5 +1 @@ -android_resources += configure_file( - input: rendered_icons['icon_exe'], - output: 'ic_launcher.png', - copy: true, -) +android_resources += fs.copyfile(rendered_icons['icon_exe'], 'ic_launcher.png') diff --git a/meson.build b/meson.build index 2d56592d9..cf9776775 100644 --- a/meson.build +++ b/meson.build @@ -18,6 +18,7 @@ if get_option('prepare') subdir_done() endif +fs = import('fs') to_array = generator( executable('toarray', sources: 'resources/ToArray.cpp', native: true), output: [ '@PLAINNAME@.cpp', '@PLAINNAME@.h' ], @@ -342,7 +343,7 @@ if host_platform == 'windows' do_copy = true endif if do_copy - configure_file(input: dll_input, output: dll_output, copy: true) + fs.copyfile(dll_input, dll_output) endif endforeach endif diff --git a/src/Config.template.h b/src/Config.template.h index 1676bb841..279583a2c 100644 --- a/src/Config.template.h +++ b/src/Config.template.h @@ -17,6 +17,7 @@ constexpr bool INSTALL_CHECK = @INSTALL_CHECK@; constexpr bool IGNORE_UPDATES = @IGNORE_UPDATES@; constexpr bool ENFORCE_HTTPS = @ENFORCE_HTTPS@; constexpr bool SECURE_CIPHERS_ONLY = @SECURE_CIPHERS_ONLY@; +constexpr bool USE_SYSTEM_CERT_PROVIDER = @USE_SYSTEM_CERT_PROVIDER@; constexpr bool FFTW_PLAN_MEASURE = @FFTW_PLAN_MEASURE@; constexpr bool ALLOW_QUIT = @ALLOW_QUIT@; constexpr bool ALLOW_WINDOW_FRAME_OPS = @ALLOW_WINDOW_FRAME_OPS@; diff --git a/src/client/http/requestmanager/CurlError.h b/src/client/http/requestmanager/CurlError.h index 959b54e8d..f859c5480 100644 --- a/src/client/http/requestmanager/CurlError.h +++ b/src/client/http/requestmanager/CurlError.h @@ -9,6 +9,7 @@ namespace http using runtime_error::runtime_error; }; + void UseSystemCertProvider(CURL *easy); void SetupCurlEasyCiphers(CURL *easy); void HandleCURLcode(CURLcode code); void HandleCURLMcode(CURLMcode code); diff --git a/src/client/http/requestmanager/Libcurl.cpp b/src/client/http/requestmanager/Libcurl.cpp index 64dc244e5..828daf23c 100644 --- a/src/client/http/requestmanager/Libcurl.cpp +++ b/src/client/http/requestmanager/Libcurl.cpp @@ -462,14 +462,6 @@ namespace http { HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_PROXY, proxy.c_str())); } - if (cafile.size()) - { - HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_CAINFO, cafile.c_str())); - } - if (capath.size()) - { - HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_CAPATH, capath.c_str())); - } HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_PRIVATE, (void *)handle)); HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_USERAGENT, userAgent.c_str())); HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_HEADERDATA, (void *)handle)); @@ -562,5 +554,20 @@ namespace http #elif defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 44, 0) HandleCURLcode(curl_easy_setopt(easy, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE)); #endif + + auto &capath = http::RequestManager::Ref().Capath(); + auto &cafile = http::RequestManager::Ref().Cafile(); + if (capath.size()) + { + HandleCURLcode(curl_easy_setopt(easy, CURLOPT_CAPATH, capath.c_str())); + } + else if (cafile.size()) + { + HandleCURLcode(curl_easy_setopt(easy, CURLOPT_CAINFO, cafile.c_str())); + } + else if constexpr (USE_SYSTEM_CERT_PROVIDER) + { + UseSystemCertProvider(easy); + } } } diff --git a/src/client/http/requestmanager/RequestManager.h b/src/client/http/requestmanager/RequestManager.h index addd05805..c55e8f3a0 100644 --- a/src/client/http/requestmanager/RequestManager.h +++ b/src/client/http/requestmanager/RequestManager.h @@ -89,6 +89,16 @@ namespace http return disableNetwork; } + const ByteString &Cafile() const + { + return cafile; + } + + const ByteString &Capath() const + { + return capath; + } + static RequestManagerPtr Create(ByteString newProxy, ByteString newCafile, ByteString newCapath, bool newDisableNetwork); }; } diff --git a/src/client/http/requestmanager/WindowsCertProvider.cpp b/src/client/http/requestmanager/WindowsCertProvider.cpp new file mode 100644 index 000000000..33d8f7423 --- /dev/null +++ b/src/client/http/requestmanager/WindowsCertProvider.cpp @@ -0,0 +1,95 @@ +#include "CurlError.h" +#include "common/String.h" +#include // crypt32.lib is pulled in by tpt-libs +#include +#include + +namespace http +{ + // see https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store + void UseSystemCertProvider(CURL *easy) + { + struct DoOnce + { + ByteString allPems; + + void InitPem() + { + struct StoreDeleter + { + typedef HCERTSTORE pointer; + + void operator ()(HCERTSTORE p) const + { + ::CertCloseStore(p, 0); + } + }; + using StorePtr = std::unique_ptr; + struct ContextDeleter + { + typedef PCCERT_CONTEXT pointer; + + void operator ()(PCCERT_CONTEXT p) const + { + ::CertFreeCertificateContext(p); + } + }; + using ContextPtr = std::unique_ptr; + + auto die = [](ByteString message) { + std::cerr << "failed to enumerate system certificates: " << message << ": " << GetLastError() << std::endl; + }; + auto store = StorePtr(::CertOpenSystemStore(0, L"ROOT"), StoreDeleter{}); + if (!store) + { + return die("CertOpenSystemStore failed"); + } + ContextPtr context; + while (true) + { + context = ContextPtr(::CertEnumCertificatesInStore(store.get(), context.release()), ContextDeleter{}); + if (!context) + { + if (::GetLastError() != DWORD(CRYPT_E_NOT_FOUND)) + { + return die("CertEnumCertificatesInStore failed"); + } + break; + } + DWORD pemLength; + // get required buffer size first + if (!CryptBinaryToStringA(context->pbCertEncoded, context->cbCertEncoded, CRYPT_STRING_BASE64HEADER, NULL, &pemLength)) + { + return die("CryptBinaryToStringA failed"); + } + std::vector pem(pemLength); + // actually get the data + if (!CryptBinaryToStringA(context->pbCertEncoded, context->cbCertEncoded, CRYPT_STRING_BASE64HEADER, &pem[0], &pemLength)) + { + return die("CryptBinaryToStringA failed"); + } + allPems += ByteString(&pem[0], &pem[0] + pem.size() - 1); // buffer includes the zero terminator, omit that + } + if (!allPems.size()) + { + die("no system certificates"); + } + } + + DoOnce() + { + InitPem(); + } + }; + + static DoOnce doOnce; + if (doOnce.allPems.size()) + { + curl_blob blob; + blob.data = &doOnce.allPems[0]; + blob.len = doOnce.allPems.size(); + blob.flags = CURL_BLOB_COPY; + HandleCURLcode(curl_easy_setopt(easy, CURLOPT_CAINFO_BLOB, &blob)); + } + } +} diff --git a/src/client/http/requestmanager/meson.build b/src/client/http/requestmanager/meson.build index 4dc0dfbad..c2de3e7b1 100644 --- a/src/client/http/requestmanager/meson.build +++ b/src/client/http/requestmanager/meson.build @@ -2,11 +2,17 @@ client_files += files( 'Common.cpp', ) +use_system_cert_provider = false if not enable_http client_files += files('Null.cpp') elif host_platform == 'emscripten' client_files += files('Emscripten.cpp') else client_files += files('Libcurl.cpp') + if host_platform == 'windows' + use_system_cert_provider = true + client_files += files('WindowsCertProvider.cpp') + endif endif conf_data.set('NOHTTP', (not enable_http).to_string()) +conf_data.set('USE_SYSTEM_CERT_PROVIDER', use_system_cert_provider.to_string())