changeset 1533:0011cc99443c

improving HTTPS support
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 12 Aug 2015 17:52:10 +0200
parents b5eb5210af91
children 95b3b0260240
files Core/HttpClient.cpp Core/HttpClient.h NEWS OrthancServer/OrthancInitialization.cpp Resources/CMake/LibCurlConfiguration.cmake Resources/Configuration.json UnitTestsSources/RestApiTests.cpp
diffstat 7 files changed, 144 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- 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 <string.h>
 #include <curl/curl.h>
+#include <boost/algorithm/string/predicate.hpp>
+
+
+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()
   {
--- 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();
   };
--- 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
 
--- 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);
   }
 
 
--- 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
--- 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" : ""
 }
--- 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