# HG changeset patch # User Sebastien Jodogne # Date 1439394730 -7200 # Node ID 0011cc99443c01008fab2fe49943b095c29f762b # Parent b5eb5210af918e14a69513d6b59eddd9d4ad7fab improving HTTPS support diff -r b5eb5210af91 -r 0011cc99443c Core/HttpClient.cpp --- a/Core/HttpClient.cpp Wed Aug 12 15:04:12 2015 +0200 +++ b/Core/HttpClient.cpp Wed Aug 12 17:52:10 2015 +0200 @@ -35,9 +35,43 @@ #include "Toolbox.h" #include "OrthancException.h" +#include "Logging.h" #include #include +#include + + +static std::string cacert_; +static bool httpsVerifyPeers_ = true; + +extern "C" +{ + static CURLcode GetHttpStatus(CURLcode code, CURL* curl, long* status) + { + if (code == CURLE_OK) + { + code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, status); + return code; + } + else + { + *status = 0; + return code; + } + } + + // This is a dummy wrapper function to suppress any OpenSSL-related + // problem in valgrind. Inlining is prevented. +#if defined(__GNUC__) || defined(__clang__) + __attribute__((noinline)) +#endif + static CURLcode OrthancHttpClientPerformSSL(CURL* curl, long* status) + { + return GetHttpStatus(curl_easy_perform(curl), curl, status); + } +} + namespace Orthanc @@ -53,7 +87,8 @@ { if (code != CURLE_OK) { - throw OrthancException("libCURL error: " + std::string(curl_easy_strerror(code))); + LOG(ERROR) << "libCURL error: " + std::string(curl_easy_strerror(code)); + throw OrthancException(ErrorCode_NetworkProtocol); } return code; @@ -97,7 +132,15 @@ CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_FOLLOWLOCATION, 1)); #if ORTHANC_SSL_ENABLED == 1 - CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYPEER, 0)); + if (httpsVerifyPeers_) + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CAINFO, cacert_.c_str())); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYPEER, 1)); + } + else + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYPEER, 0)); + } #endif // This fixes the "longjmp causes uninitialized stack frame" crash @@ -243,10 +286,19 @@ // Do the actual request - CheckCode(curl_easy_perform(pimpl_->curl_)); + CURLcode code; + long status = 0; - long status; - CheckCode(curl_easy_getinfo(pimpl_->curl_, CURLINFO_RESPONSE_CODE, &status)); + if (boost::starts_with(url_, "https://")) + { + code = OrthancHttpClientPerformSSL(pimpl_->curl_, &status); + } + else + { + code = GetHttpStatus(curl_easy_perform(pimpl_->curl_), pimpl_->curl_, &status); + } + + CheckCode(code); if (status == 0) { @@ -284,10 +336,40 @@ } - void HttpClient::GlobalInitialize() + void HttpClient::GlobalInitialize(bool httpsVerifyPeers, + const std::string& httpsVerifyCertificates) { +#if ORTHANC_SSL_ENABLED == 1 + httpsVerifyPeers_ = httpsVerifyPeers; + cacert_ = httpsVerifyCertificates; + + // TODO + /*if (cacert_.empty()) + { + cacert_ = "/etc/ssl/certs/ca-certificates.crt"; + }*/ + + if (httpsVerifyPeers) + { + if (cacert_.empty()) + { + LOG(WARNING) << "No certificates are provided to validate peers, " + << "set \"HttpsCertificatesFile\" if you need to do HTTPS requests"; + } + else + { + LOG(WARNING) << "HTTPS will use the certificates from this file: " << cacert_; + } + } + else + { + LOG(WARNING) << "The verification of the peers in HTTPS requests is disabled!"; + } +#endif + CheckCode(curl_global_init(CURL_GLOBAL_DEFAULT)); } + void HttpClient::GlobalFinalize() { diff -r b5eb5210af91 -r 0011cc99443c Core/HttpClient.h --- a/Core/HttpClient.h Wed Aug 12 15:04:12 2015 +0200 +++ b/Core/HttpClient.h Wed Aug 12 17:52:10 2015 +0200 @@ -140,7 +140,8 @@ proxy_ = proxy; } - static void GlobalInitialize(); + static void GlobalInitialize(bool httpsVerifyPeers, + const std::string& httpsVerifyCertificates); static void GlobalFinalize(); }; diff -r b5eb5210af91 -r 0011cc99443c NEWS --- a/NEWS Wed Aug 12 15:04:12 2015 +0200 +++ b/NEWS Wed Aug 12 17:52:10 2015 +0200 @@ -7,6 +7,7 @@ Maintenance ----------- +* Options to validate peers in HTTPS requests * Upgrade to curl 7.44.0 for static and Windows builds * Upgrade to libcurl 1.0.2d for static and Windows builds diff -r b5eb5210af91 -r 0011cc99443c OrthancServer/OrthancInitialization.cpp --- a/OrthancServer/OrthancInitialization.cpp Wed Aug 12 15:04:12 2015 +0200 +++ b/OrthancServer/OrthancInitialization.cpp Wed Aug 12 17:52:10 2015 +0200 @@ -77,6 +77,35 @@ static std::string configurationAbsolutePath_; + static std::string GetGlobalStringParameterInternal(const std::string& parameter, + const std::string& defaultValue) + { + if (configuration_.isMember(parameter)) + { + return configuration_[parameter].asString(); + } + else + { + return defaultValue; + } + } + + + static bool GetGlobalBoolParameterInternal(const std::string& parameter, + bool defaultValue) + { + if (configuration_.isMember(parameter)) + { + return configuration_[parameter].asBool(); + } + else + { + return defaultValue; + } + } + + + static void AddFileToConfiguration(const boost::filesystem::path& path) { LOG(WARNING) << "Reading the configuration from: " << path; @@ -286,7 +315,8 @@ // Read the user-provided configuration ReadGlobalConfiguration(configurationFile); - HttpClient::GlobalInitialize(); + HttpClient::GlobalInitialize(GetGlobalBoolParameterInternal("HttpsVerifyPeers", true), + GetGlobalStringParameterInternal("HttpsVerifyCertificates", "")); RegisterUserMetadata(); RegisterUserContentType(); @@ -337,20 +367,11 @@ } - std::string Configuration::GetGlobalStringParameter(const std::string& parameter, const std::string& defaultValue) { boost::mutex::scoped_lock lock(globalMutex_); - - if (configuration_.isMember(parameter)) - { - return configuration_[parameter].asString(); - } - else - { - return defaultValue; - } + return GetGlobalStringParameterInternal(parameter, defaultValue); } @@ -374,15 +395,7 @@ bool defaultValue) { boost::mutex::scoped_lock lock(globalMutex_); - - if (configuration_.isMember(parameter)) - { - return configuration_[parameter].asBool(); - } - else - { - return defaultValue; - } + return GetGlobalBoolParameterInternal(parameter, defaultValue); } diff -r b5eb5210af91 -r 0011cc99443c Resources/CMake/LibCurlConfiguration.cmake --- a/Resources/CMake/LibCurlConfiguration.cmake Wed Aug 12 15:04:12 2015 +0200 +++ b/Resources/CMake/LibCurlConfiguration.cmake Wed Aug 12 17:52:10 2015 +0200 @@ -42,6 +42,8 @@ ) endif() + file(WRITE ${CURL_SOURCES_DIR}/lib/curl_config.h "") + if (MSVC) #add_definitions( # -DHAVE_BOOL_T=1 diff -r b5eb5210af91 -r 0011cc99443c Resources/Configuration.json --- a/Resources/Configuration.json Wed Aug 12 15:04:12 2015 +0200 +++ b/Resources/Configuration.json Wed Aug 12 17:52:10 2015 +0200 @@ -237,5 +237,15 @@ // Enable HTTP compression to improve network bandwidth utilization, // at the expense of more computations on the server. Orthanc // supports the "gzip" and "deflate" encodings. - "HttpCompressionEnabled" : true + "HttpCompressionEnabled" : true, + + // Enable the verification of the peers during HTTPS requests. + "HttpsVerifyPeers" : true, + + // Path to the certificates to validate peers in HTTPS + // requests. From curl documentation: "Tells curl to use the + // specified certificate file to verify the peers. The file may + // contain multiple CA certificates. The certificate(s) must be in + // PEM format." + "HttpsVerifyCertificates" : "" } diff -r b5eb5210af91 -r 0011cc99443c UnitTestsSources/RestApiTests.cpp --- a/UnitTestsSources/RestApiTests.cpp Wed Aug 12 15:04:12 2015 +0200 +++ b/UnitTestsSources/RestApiTests.cpp Wed Aug 12 17:52:10 2015 +0200 @@ -64,20 +64,22 @@ c.SetUrl("http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/Configuration.json"); c.Apply(v); ASSERT_TRUE(v.isMember("StorageDirectory")); - //ASSERT_EQ(GetLastStatusText()); #endif } #if UNIT_TESTS_WITH_HTTP_CONNEXIONS == 1 -TEST(HttpClient, DISABLED_Ssl) +TEST(HttpClient, Ssl) { HttpClient c; - c.SetUrl("https://www.montefiore.ulg.ac.be/~jodogne/Orthanc/Configuration.json"); + c.SetUrl("https://bitbucket.org/sjodogne/orthanc/raw/Orthanc-0.9.3/Resources/Configuration.json"); - Json::Value v; + std::string s; + c.Apply(s); + + /*Json::Value v; c.Apply(v); - ASSERT_TRUE(v.isMember("LuaScripts")); + ASSERT_TRUE(v.isMember("LuaScripts"));*/ } #endif