Mercurial > hg > orthanc
comparison 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 |
comparison
equal
deleted
inserted
replaced
4429:48ff722fad1f | 4430:f5d44e30b429 |
---|---|
24 #include "DicomServer.h" | 24 #include "DicomServer.h" |
25 | 25 |
26 #include "../Logging.h" | 26 #include "../Logging.h" |
27 #include "../MultiThreading/RunnableWorkersPool.h" | 27 #include "../MultiThreading/RunnableWorkersPool.h" |
28 #include "../OrthancException.h" | 28 #include "../OrthancException.h" |
29 #include "../SystemToolbox.h" | |
29 #include "../Toolbox.h" | 30 #include "../Toolbox.h" |
30 #include "Internals/CommandDispatcher.h" | 31 #include "Internals/CommandDispatcher.h" |
31 | 32 |
32 #include <boost/thread.hpp> | 33 #include <boost/thread.hpp> |
33 | 34 |
35 #if ORTHANC_ENABLE_SSL == 1 | |
36 # include <dcmtk/dcmtls/tlslayer.h> | |
37 #endif | |
38 | |
34 #if defined(__linux__) | 39 #if defined(__linux__) |
35 #include <cstdlib> | 40 # include <cstdlib> |
36 #endif | 41 #endif |
37 | 42 |
38 | 43 |
39 namespace Orthanc | 44 namespace Orthanc |
40 { | 45 { |
41 struct DicomServer::PImpl | 46 struct DicomServer::PImpl |
42 { | 47 { |
43 boost::thread thread_; | 48 boost::thread thread_; |
44 T_ASC_Network *network_; | 49 T_ASC_Network *network_; |
45 std::unique_ptr<RunnableWorkersPool> workers_; | 50 std::unique_ptr<RunnableWorkersPool> workers_; |
51 | |
52 #if ORTHANC_ENABLE_SSL == 1 | |
53 std::unique_ptr<DcmTLSTransportLayer> tls_; | |
54 #endif | |
46 }; | 55 }; |
47 | 56 |
48 | 57 |
49 void DicomServer::ServerThread(DicomServer* server) | 58 void DicomServer::ServerThread(DicomServer* server, |
59 bool useDicomTls) | |
50 { | 60 { |
51 CLOG(INFO, DICOM) << "DICOM server started"; | 61 CLOG(INFO, DICOM) << "DICOM server started"; |
52 | 62 |
53 while (server->continue_) | 63 while (server->continue_) |
54 { | 64 { |
55 /* receive an association and acknowledge or reject it. If the association was */ | 65 /* receive an association and acknowledge or reject it. If the association was */ |
56 /* acknowledged, offer corresponding services and invoke one or more if required. */ | 66 /* acknowledged, offer corresponding services and invoke one or more if required. */ |
57 std::unique_ptr<Internals::CommandDispatcher> dispatcher(Internals::AcceptAssociation(*server, server->pimpl_->network_)); | 67 std::unique_ptr<Internals::CommandDispatcher> dispatcher( |
68 Internals::AcceptAssociation(*server, server->pimpl_->network_, useDicomTls)); | |
58 | 69 |
59 try | 70 try |
60 { | 71 { |
61 if (dispatcher.get() != NULL) | 72 if (dispatcher.get() != NULL) |
62 { | 73 { |
347 { | 358 { |
348 throw OrthancException(ErrorCode_NoApplicationEntityFilter); | 359 throw OrthancException(ErrorCode_NoApplicationEntityFilter); |
349 } | 360 } |
350 } | 361 } |
351 | 362 |
363 | |
364 #if ORTHANC_ENABLE_SSL == 1 | |
365 | |
366 #if DCMTK_VERSION_NUMBER < 364 | |
367 # define DCF_Filetype_PEM SSL_FILETYPE_PEM | |
368 #endif | |
369 | |
370 // New in Orthanc 1.9.0 | |
371 void DicomServer::InitializeDicomTls() | |
372 { | |
373 // TODO - Configuration options | |
374 const std::string cf = "/tmp/j/Client.crt"; // This is the "--add-cert-file" ("+cf") option from DCMTK command-line tools | |
375 const std::string key = "/tmp/j/Server.key"; // This is the first argument of "+tls" option | |
376 const std::string cert = "/tmp/j/Server.crt"; // This is the second argument of "+tls" option | |
377 | |
378 if (!SystemToolbox::IsRegularFile(cf)) | |
379 { | |
380 throw OrthancException(ErrorCode_InexistentFile, "Cannot read file with trusted certificates for DICOM TLS: " + cf); | |
381 } | |
382 | |
383 if (!SystemToolbox::IsRegularFile(key)) | |
384 { | |
385 throw OrthancException(ErrorCode_InexistentFile, "Cannot read file with private key for DICOM TLS: " + key); | |
386 } | |
387 | |
388 if (!SystemToolbox::IsRegularFile(cert)) | |
389 { | |
390 throw OrthancException(ErrorCode_InexistentFile, "Cannot read file with server certificate for DICOM TLS: " + cert); | |
391 } | |
392 | |
393 CLOG(INFO, DICOM) << "Initializing DICOM TLS"; | |
394 pimpl_->tls_.reset(new DcmTLSTransportLayer(NET_ACCEPTOR /*opt_networkRole*/, NULL /*opt_readSeedFile*/, | |
395 OFFalse /*initializeOpenSSL, done by Orthanc::Toolbox::InitializeOpenSsl()*/)); | |
396 | |
397 if (pimpl_->tls_->addTrustedCertificateFile(cf.c_str(), DCF_Filetype_PEM /*opt_keyFileFormat*/) != TCS_ok) | |
398 { | |
399 throw OrthancException(ErrorCode_BadFileFormat, "Cannot parse PEM file with trusted certificates for DICOM TLS: " + cf); | |
400 } | |
401 | |
402 if (pimpl_->tls_->setPrivateKeyFile(key.c_str(), DCF_Filetype_PEM /*opt_keyFileFormat*/) != TCS_ok) | |
403 { | |
404 throw OrthancException(ErrorCode_BadFileFormat, "Cannot parse PEM file with private key for DICOM TLS: " + key); | |
405 } | |
406 | |
407 if (pimpl_->tls_->setCertificateFile(cert.c_str(), DCF_Filetype_PEM /*opt_keyFileFormat*/) != TCS_ok) | |
408 { | |
409 throw OrthancException(ErrorCode_BadFileFormat, "Cannot parse PEM file with server certificate for DICOM TLS: " + cert); | |
410 } | |
411 | |
412 if (!pimpl_->tls_->checkPrivateKeyMatchesCertificate()) | |
413 { | |
414 throw OrthancException(ErrorCode_BadFileFormat, "The private key doesn't match the server certificate: " + key + " vs. " + cert); | |
415 } | |
416 | |
417 #if DCMTK_VERSION_NUMBER >= 364 | |
418 if (pimpl_->tls_->setTLSProfile(TSP_Profile_BCP195 /*opt_tlsProfile*/) != TCS_ok) | |
419 { | |
420 throw OrthancException(ErrorCode_InternalError, "Cannot set the DICOM TLS profile"); | |
421 } | |
422 | |
423 if (pimpl_->tls_->activateCipherSuites()) | |
424 { | |
425 throw OrthancException(ErrorCode_InternalError, "Cannot activate the cipher suites for DICOM TLS"); | |
426 } | |
427 #endif | |
428 | |
429 pimpl_->tls_->setCertificateVerification(DCV_requireCertificate /*opt_certVerification*/); | |
430 | |
431 if (ASC_setTransportLayer(pimpl_->network_, pimpl_->tls_.get(), 0).bad()) | |
432 { | |
433 throw OrthancException(ErrorCode_InternalError, "Cannot enable DICOM TLS in the server"); | |
434 } | |
435 } | |
436 #endif | |
437 | |
438 | |
352 void DicomServer::Start() | 439 void DicomServer::Start() |
353 { | 440 { |
354 if (modalities_ == NULL) | 441 if (modalities_ == NULL) |
355 { | 442 { |
356 throw OrthancException(ErrorCode_BadSequenceOfCalls, | 443 throw OrthancException(ErrorCode_BadSequenceOfCalls, |
363 OFCondition cond = ASC_initializeNetwork | 450 OFCondition cond = ASC_initializeNetwork |
364 (NET_ACCEPTOR, OFstatic_cast(int, port_), /*opt_acse_timeout*/ 30, &pimpl_->network_); | 451 (NET_ACCEPTOR, OFstatic_cast(int, port_), /*opt_acse_timeout*/ 30, &pimpl_->network_); |
365 if (cond.bad()) | 452 if (cond.bad()) |
366 { | 453 { |
367 throw OrthancException(ErrorCode_DicomPortInUse, | 454 throw OrthancException(ErrorCode_DicomPortInUse, |
368 " (port = " + boost::lexical_cast<std::string>(port_) + ") cannot create network: " + std::string(cond.text())); | 455 " (port = " + boost::lexical_cast<std::string>(port_) + |
456 ") cannot create network: " + std::string(cond.text())); | |
457 } | |
458 | |
459 bool useDicomTls = false; // TODO - Read from configuration option | |
460 | |
461 #if ORTHANC_ENABLE_SSL == 1 | |
462 if (useDicomTls) | |
463 { | |
464 try | |
465 { | |
466 InitializeDicomTls(); | |
467 } | |
468 catch (OrthancException&) | |
469 { | |
470 pimpl_->tls_.reset(NULL); | |
471 ASC_dropNetwork(&pimpl_->network_); | |
472 throw; | |
473 } | |
474 } | |
475 #endif | |
476 | |
477 if (useDicomTls) | |
478 { | |
479 CLOG(INFO, DICOM) << "Orthanc SCP will use DICOM TLS"; | |
480 } | |
481 else | |
482 { | |
483 CLOG(INFO, DICOM) << "Orthanc SCP will *not* use DICOM TLS"; | |
369 } | 484 } |
370 | 485 |
371 continue_ = true; | 486 continue_ = true; |
372 pimpl_->workers_.reset(new RunnableWorkersPool(4)); // Use 4 workers - TODO as a parameter? | 487 pimpl_->workers_.reset(new RunnableWorkersPool(4)); // Use 4 workers - TODO as a parameter? |
373 pimpl_->thread_ = boost::thread(ServerThread, this); | 488 pimpl_->thread_ = boost::thread(ServerThread, this, useDicomTls); |
374 } | 489 } |
375 | 490 |
376 | 491 |
377 void DicomServer::Stop() | 492 void DicomServer::Stop() |
378 { | 493 { |
384 { | 499 { |
385 pimpl_->thread_.join(); | 500 pimpl_->thread_.join(); |
386 } | 501 } |
387 | 502 |
388 pimpl_->workers_.reset(NULL); | 503 pimpl_->workers_.reset(NULL); |
504 | |
505 #if ORTHANC_ENABLE_SSL == 1 | |
506 pimpl_->tls_.reset(NULL); | |
507 #endif | |
389 | 508 |
390 /* drop the network, i.e. free memory of T_ASC_Network* structure. This call */ | 509 /* drop the network, i.e. free memory of T_ASC_Network* structure. This call */ |
391 /* is the counterpart of ASC_initializeNetwork(...) which was called above. */ | 510 /* is the counterpart of ASC_initializeNetwork(...) which was called above. */ |
392 OFCondition cond = ASC_dropNetwork(&pimpl_->network_); | 511 OFCondition cond = ASC_dropNetwork(&pimpl_->network_); |
393 if (cond.bad()) | 512 if (cond.bad()) |