changeset 4439:5209a9ff6e38

configuration options for DICOM TLS in Orthanc SCP
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 07 Jan 2021 18:18:39 +0100
parents 4a4e33c9082d
children eddb212b2df9
files OrthancFramework/Sources/DicomNetworking/DicomAssociation.cpp OrthancFramework/Sources/DicomNetworking/DicomServer.cpp OrthancFramework/Sources/DicomNetworking/DicomServer.h OrthancServer/Sources/main.cpp
diffstat 4 files changed, 150 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancFramework/Sources/DicomNetworking/DicomAssociation.cpp	Thu Jan 07 16:53:35 2021 +0100
+++ b/OrthancFramework/Sources/DicomNetworking/DicomAssociation.cpp	Thu Jan 07 18:18:39 2021 +0100
@@ -265,8 +265,9 @@
                              "No presentation context was proposed");
     }
 
-    CLOG(INFO, DICOM) << "Opening a DICOM SCU connection from AET \""
-                      << parameters.GetLocalApplicationEntityTitle() 
+    CLOG(INFO, DICOM) << "Opening a DICOM SCU connection "
+                      << (parameters.GetRemoteModality().IsDicomTlsEnabled() ? "using DICOM TLS" : "without DICOM TLS")
+                      << " from AET \"" << parameters.GetLocalApplicationEntityTitle() 
                       << "\" to AET \"" << parameters.GetRemoteModality().GetApplicationEntityTitle()
                       << "\" on host " << parameters.GetRemoteModality().GetHost()
                       << ":" << parameters.GetRemoteModality().GetPortNumber() 
--- a/OrthancFramework/Sources/DicomNetworking/DicomServer.cpp	Thu Jan 07 16:53:35 2021 +0100
+++ b/OrthancFramework/Sources/DicomNetworking/DicomServer.cpp	Thu Jan 07 18:18:39 2021 +0100
@@ -86,7 +86,8 @@
 
   DicomServer::DicomServer() : 
     pimpl_(new PImpl),
-    aet_("ANY-SCP")
+    aet_("ANY-SCP"),
+    useDicomTls_(false)
   {
     port_ = 104;
     modalities_ = NULL;
@@ -368,6 +369,16 @@
       throw OrthancException(ErrorCode_BadSequenceOfCalls,
                              "No list of modalities was provided to the DICOM server");
     }
+
+    if (useDicomTls_)
+    {
+      if (ownCertificatePath_.empty() ||
+          ownPrivateKeyPath_.empty())
+      {
+        throw OrthancException(ErrorCode_ParameterOutOfRange,
+                               "DICOM TLS is enabled in Orthanc SCP, but no certificate was provided");
+      }
+    }
     
     Stop();
 
@@ -381,16 +392,15 @@
                              ") cannot create network: " + std::string(cond.text()));
     }
 
-    bool useDicomTls = false;    // TODO - Read from configuration option
-
 #if ORTHANC_ENABLE_SSL == 1
     assert(pimpl_->tls_.get() == NULL);
 
-    if (useDicomTls)
+    if (useDicomTls_)
     {
+      CLOG(INFO, DICOM) << "Orthanc SCP will use DICOM TLS";
+
       try
       {
-        // TODO - Configuration options
         pimpl_->tls_.reset(Internals::InitializeDicomTls(pimpl_->network_, NET_ACCEPTOR,
                                                          "/tmp/j/Server.key", "/tmp/j/Server.crt", "/tmp/j/Client.crt"));
       }
@@ -400,20 +410,17 @@
         throw;
       }
     }
-#endif
-
-    if (useDicomTls)
-    {
-      CLOG(INFO, DICOM) << "Orthanc SCP will use DICOM TLS";
-    }
     else
     {
       CLOG(INFO, DICOM) << "Orthanc SCP will *not* use DICOM TLS";
     }
+#else
+    CLOG(INFO, DICOM) << "Orthanc SCP will *not* use DICOM TLS";
+#endif
 
     continue_ = true;
     pimpl_->workers_.reset(new RunnableWorkersPool(4));   // Use 4 workers - TODO as a parameter?
-    pimpl_->thread_ = boost::thread(ServerThread, this, useDicomTls);
+    pimpl_->thread_ = boost::thread(ServerThread, this, useDicomTls_);
   }
 
 
@@ -462,4 +469,94 @@
       return modalities_->IsSameAETitle(aet, GetApplicationEntityTitle());
     }
   }
+
+
+  void DicomServer::SetDicomTlsEnabled(bool enabled)
+  {
+    Stop();
+    useDicomTls_ = enabled;
+  }
+  
+  bool DicomServer::IsDicomTlsEnabled() const
+  {
+    return useDicomTls_;
+  }
+
+  void DicomServer::SetOwnCertificatePath(const std::string& privateKeyPath,
+                                          const std::string& certificatePath)
+  {
+    Stop();
+
+    if (!privateKeyPath.empty() &&
+        !certificatePath.empty())
+    {
+      CLOG(INFO, DICOM) << "Setting the TLS certificate for DICOM SCP connections: " 
+                        << privateKeyPath << " (key), " << certificatePath << " (certificate)";
+
+      if (certificatePath.empty())
+      {
+        throw OrthancException(ErrorCode_ParameterOutOfRange, "No path to the default DICOM TLS certificate was provided");
+      }
+      
+      if (privateKeyPath.empty())
+      {
+        throw OrthancException(ErrorCode_ParameterOutOfRange,
+                               "No path to the private key for the default DICOM TLS certificate was provided");
+      }
+      
+      if (!SystemToolbox::IsRegularFile(privateKeyPath))
+      {
+        throw OrthancException(ErrorCode_InexistentFile, "Inexistent file: " + privateKeyPath);
+      }
+
+      if (!SystemToolbox::IsRegularFile(certificatePath))
+      {
+        throw OrthancException(ErrorCode_InexistentFile, "Inexistent file: " + certificatePath);
+      }
+      
+      ownPrivateKeyPath_ = privateKeyPath;
+      ownCertificatePath_ = certificatePath;
+    }
+    else
+    {
+      ownPrivateKeyPath_.clear();
+      ownCertificatePath_.clear();
+    }
+  }
+  
+  const std::string& DicomServer::GetOwnPrivateKeyPath() const
+  {
+    return ownPrivateKeyPath_;
+  }
+  
+  const std::string& DicomServer::GetOwnCertificatePath() const
+  {
+    return ownCertificatePath_;
+  }
+    
+  void DicomServer::SetTrustedCertificatesPath(const std::string& path)
+  {
+    Stop();
+
+    if (!path.empty())
+    {
+      CLOG(INFO, DICOM) << "Setting the trusted certificates for DICOM SCP connections: " << path;
+
+      if (!SystemToolbox::IsRegularFile(path))
+      {
+        throw OrthancException(ErrorCode_InexistentFile, "Inexistent file: " + path);
+      }
+      
+      trustedCertificatesPath_ = path;
+    }
+    else
+    {
+      trustedCertificatesPath_.clear();
+    }
+  }
+  
+  const std::string& DicomServer::GetTrustedCertificatesPath() const
+  {
+    return trustedCertificatesPath_;
+  }
 }
--- a/OrthancFramework/Sources/DicomNetworking/DicomServer.h	Thu Jan 07 16:53:35 2021 +0100
+++ b/OrthancFramework/Sources/DicomNetworking/DicomServer.h	Thu Jan 07 18:18:39 2021 +0100
@@ -81,6 +81,12 @@
     IStorageCommitmentRequestHandlerFactory* storageCommitmentFactory_;
     IApplicationEntityFilter* applicationEntityFilter_;
 
+    // New in Orthanc 1.9.0 for DICOM TLS
+    bool         useDicomTls_;
+    std::string  ownPrivateKeyPath_;
+    std::string  ownCertificatePath_;
+    std::string  trustedCertificatesPath_;
+
     static void ServerThread(DicomServer* server,
                              bool useDicomTls);
 
@@ -137,6 +143,17 @@
     void Stop();
 
     bool IsMyAETitle(const std::string& aet) const;
+
+    void SetDicomTlsEnabled(bool enabled);
+    bool IsDicomTlsEnabled() const;
+
+    void SetOwnCertificatePath(const std::string& privateKeyPath,
+                               const std::string& certificatePath);
+    const std::string& GetOwnPrivateKeyPath() const;    
+    const std::string& GetOwnCertificatePath() const;
+    
+    void SetTrustedCertificatesPath(const std::string& path);
+    const std::string& GetTrustedCertificatesPath() const;
   };
 
 }
--- a/OrthancServer/Sources/main.cpp	Thu Jan 07 16:53:35 2021 +0100
+++ b/OrthancServer/Sources/main.cpp	Thu Jan 07 18:18:39 2021 +0100
@@ -64,6 +64,12 @@
 using namespace Orthanc;
 
 
+static const char* const KEY_DICOM_TLS_PRIVATE_KEY = "DicomTlsPrivateKey";
+static const char* const KEY_DICOM_TLS_ENABLED = "DicomTlsEnabled";
+static const char* const KEY_DICOM_TLS_CERTIFICATE = "DicomTlsCertificate";
+static const char* const KEY_DICOM_TLS_TRUSTED_CERTIFICATES = "DicomTlsTrustedCertificates";
+
+
 class OrthancStoreRequestHandler : public IStoreRequestHandler
 {
 private:
@@ -1189,6 +1195,17 @@
       dicomServer.SetAssociationTimeout(lock.GetConfiguration().GetUnsignedIntegerParameter("DicomScpTimeout", 30));
       dicomServer.SetPortNumber(lock.GetConfiguration().GetUnsignedIntegerParameter("DicomPort", 4242));
       dicomServer.SetApplicationEntityTitle(lock.GetConfiguration().GetOrthancAET());
+
+      // Configuration of DICOM TLS for Orthanc SCP (since Orthanc 1.9.0)
+      dicomServer.SetDicomTlsEnabled(lock.GetConfiguration().GetBooleanParameter(KEY_DICOM_TLS_ENABLED, false));
+      if (dicomServer.IsDicomTlsEnabled())
+      {
+        dicomServer.SetOwnCertificatePath(
+          lock.GetConfiguration().GetStringParameter(KEY_DICOM_TLS_PRIVATE_KEY, ""),
+          lock.GetConfiguration().GetStringParameter(KEY_DICOM_TLS_CERTIFICATE, ""));
+        dicomServer.SetTrustedCertificatesPath(
+          lock.GetConfiguration().GetStringParameter(KEY_DICOM_TLS_TRUSTED_CERTIFICATES, ""));
+      }
     }
 
 #if ORTHANC_ENABLE_PLUGINS == 1
@@ -1438,12 +1455,12 @@
       LOG(WARNING) << "Setting option \"JobsHistorySize\" to zero is not recommended";
     }
 
-    // Configuration of DICOM TLS (since Orthanc 1.9.0)
+    // Configuration of DICOM TLS for Orthanc SCU (since Orthanc 1.9.0)
     DicomAssociationParameters::SetDefaultOwnCertificatePath(
-      lock.GetConfiguration().GetStringParameter("DicomTlsPrivateKey", ""),
-      lock.GetConfiguration().GetStringParameter("DicomTlsCertificate", ""));
+      lock.GetConfiguration().GetStringParameter(KEY_DICOM_TLS_PRIVATE_KEY, ""),
+      lock.GetConfiguration().GetStringParameter(KEY_DICOM_TLS_CERTIFICATE, ""));
     DicomAssociationParameters::SetDefaultTrustedCertificatesPath(
-      lock.GetConfiguration().GetStringParameter("DicomTlsTrustedCertificates", ""));
+      lock.GetConfiguration().GetStringParameter(KEY_DICOM_TLS_TRUSTED_CERTIFICATES, ""));
   }
   
   ServerContext context(database, storageArea, false /* not running unit tests */, maxCompletedJobs);