Mercurial > hg > orthanc
diff OrthancFramework/Sources/DicomNetworking/DicomServer.cpp @ 4430:f5d44e30b429
testing DICOM TLS in Orthanc SCP
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 04 Jan 2021 12:42:45 +0100 |
parents | 756126cd2219 |
children | fcbac3e8ac1c |
line wrap: on
line diff
--- a/OrthancFramework/Sources/DicomNetworking/DicomServer.cpp Sat Jan 02 14:53:24 2021 +0100 +++ b/OrthancFramework/Sources/DicomNetworking/DicomServer.cpp Mon Jan 04 12:42:45 2021 +0100 @@ -26,13 +26,18 @@ #include "../Logging.h" #include "../MultiThreading/RunnableWorkersPool.h" #include "../OrthancException.h" +#include "../SystemToolbox.h" #include "../Toolbox.h" #include "Internals/CommandDispatcher.h" #include <boost/thread.hpp> +#if ORTHANC_ENABLE_SSL == 1 +# include <dcmtk/dcmtls/tlslayer.h> +#endif + #if defined(__linux__) -#include <cstdlib> +# include <cstdlib> #endif @@ -43,10 +48,15 @@ boost::thread thread_; T_ASC_Network *network_; std::unique_ptr<RunnableWorkersPool> workers_; + +#if ORTHANC_ENABLE_SSL == 1 + std::unique_ptr<DcmTLSTransportLayer> tls_; +#endif }; - void DicomServer::ServerThread(DicomServer* server) + void DicomServer::ServerThread(DicomServer* server, + bool useDicomTls) { CLOG(INFO, DICOM) << "DICOM server started"; @@ -54,7 +64,8 @@ { /* receive an association and acknowledge or reject it. If the association was */ /* acknowledged, offer corresponding services and invoke one or more if required. */ - std::unique_ptr<Internals::CommandDispatcher> dispatcher(Internals::AcceptAssociation(*server, server->pimpl_->network_)); + std::unique_ptr<Internals::CommandDispatcher> dispatcher( + Internals::AcceptAssociation(*server, server->pimpl_->network_, useDicomTls)); try { @@ -349,6 +360,82 @@ } } + +#if ORTHANC_ENABLE_SSL == 1 + +#if DCMTK_VERSION_NUMBER < 364 +# define DCF_Filetype_PEM SSL_FILETYPE_PEM +#endif + + // New in Orthanc 1.9.0 + void DicomServer::InitializeDicomTls() + { + // TODO - Configuration options + const std::string cf = "/tmp/j/Client.crt"; // This is the "--add-cert-file" ("+cf") option from DCMTK command-line tools + const std::string key = "/tmp/j/Server.key"; // This is the first argument of "+tls" option + const std::string cert = "/tmp/j/Server.crt"; // This is the second argument of "+tls" option + + if (!SystemToolbox::IsRegularFile(cf)) + { + throw OrthancException(ErrorCode_InexistentFile, "Cannot read file with trusted certificates for DICOM TLS: " + cf); + } + + if (!SystemToolbox::IsRegularFile(key)) + { + throw OrthancException(ErrorCode_InexistentFile, "Cannot read file with private key for DICOM TLS: " + key); + } + + if (!SystemToolbox::IsRegularFile(cert)) + { + throw OrthancException(ErrorCode_InexistentFile, "Cannot read file with server certificate for DICOM TLS: " + cert); + } + + CLOG(INFO, DICOM) << "Initializing DICOM TLS"; + pimpl_->tls_.reset(new DcmTLSTransportLayer(NET_ACCEPTOR /*opt_networkRole*/, NULL /*opt_readSeedFile*/, + OFFalse /*initializeOpenSSL, done by Orthanc::Toolbox::InitializeOpenSsl()*/)); + + if (pimpl_->tls_->addTrustedCertificateFile(cf.c_str(), DCF_Filetype_PEM /*opt_keyFileFormat*/) != TCS_ok) + { + throw OrthancException(ErrorCode_BadFileFormat, "Cannot parse PEM file with trusted certificates for DICOM TLS: " + cf); + } + + if (pimpl_->tls_->setPrivateKeyFile(key.c_str(), DCF_Filetype_PEM /*opt_keyFileFormat*/) != TCS_ok) + { + throw OrthancException(ErrorCode_BadFileFormat, "Cannot parse PEM file with private key for DICOM TLS: " + key); + } + + if (pimpl_->tls_->setCertificateFile(cert.c_str(), DCF_Filetype_PEM /*opt_keyFileFormat*/) != TCS_ok) + { + throw OrthancException(ErrorCode_BadFileFormat, "Cannot parse PEM file with server certificate for DICOM TLS: " + cert); + } + + if (!pimpl_->tls_->checkPrivateKeyMatchesCertificate()) + { + throw OrthancException(ErrorCode_BadFileFormat, "The private key doesn't match the server certificate: " + key + " vs. " + cert); + } + +#if DCMTK_VERSION_NUMBER >= 364 + if (pimpl_->tls_->setTLSProfile(TSP_Profile_BCP195 /*opt_tlsProfile*/) != TCS_ok) + { + throw OrthancException(ErrorCode_InternalError, "Cannot set the DICOM TLS profile"); + } + + if (pimpl_->tls_->activateCipherSuites()) + { + throw OrthancException(ErrorCode_InternalError, "Cannot activate the cipher suites for DICOM TLS"); + } +#endif + + pimpl_->tls_->setCertificateVerification(DCV_requireCertificate /*opt_certVerification*/); + + if (ASC_setTransportLayer(pimpl_->network_, pimpl_->tls_.get(), 0).bad()) + { + throw OrthancException(ErrorCode_InternalError, "Cannot enable DICOM TLS in the server"); + } + } +#endif + + void DicomServer::Start() { if (modalities_ == NULL) @@ -365,12 +452,40 @@ if (cond.bad()) { throw OrthancException(ErrorCode_DicomPortInUse, - " (port = " + boost::lexical_cast<std::string>(port_) + ") cannot create network: " + std::string(cond.text())); + " (port = " + boost::lexical_cast<std::string>(port_) + + ") cannot create network: " + std::string(cond.text())); + } + + bool useDicomTls = false; // TODO - Read from configuration option + +#if ORTHANC_ENABLE_SSL == 1 + if (useDicomTls) + { + try + { + InitializeDicomTls(); + } + catch (OrthancException&) + { + pimpl_->tls_.reset(NULL); + ASC_dropNetwork(&pimpl_->network_); + throw; + } + } +#endif + + if (useDicomTls) + { + CLOG(INFO, DICOM) << "Orthanc SCP will use DICOM TLS"; + } + else + { + CLOG(INFO, DICOM) << "Orthanc SCP will *not* use DICOM TLS"; } continue_ = true; pimpl_->workers_.reset(new RunnableWorkersPool(4)); // Use 4 workers - TODO as a parameter? - pimpl_->thread_ = boost::thread(ServerThread, this); + pimpl_->thread_ = boost::thread(ServerThread, this, useDicomTls); } @@ -387,6 +502,10 @@ pimpl_->workers_.reset(NULL); +#if ORTHANC_ENABLE_SSL == 1 + pimpl_->tls_.reset(NULL); +#endif + /* drop the network, i.e. free memory of T_ASC_Network* structure. This call */ /* is the counterpart of ASC_initializeNetwork(...) which was called above. */ OFCondition cond = ASC_dropNetwork(&pimpl_->network_);