Mercurial > hg > orthanc
changeset 2352:3ab96768d144
Fix issue #52 (DICOM level security association problems)
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 18 Jul 2017 17:33:26 +0200 |
parents | 56504f89d4ac |
children | 2421c137c304 415450f11cc7 |
files | NEWS OrthancServer/DicomProtocol/DicomUserConnection.cpp OrthancServer/Internals/CommandDispatcher.cpp OrthancServer/OrthancInitialization.cpp OrthancServer/OrthancInitialization.h OrthancServer/ServerEnumerations.cpp OrthancServer/ServerEnumerations.h OrthancServer/main.cpp Resources/Configuration.json UnitTestsSources/UnitTestsMain.cpp |
diffstat | 10 files changed, 79 insertions(+), 37 deletions(-) [+] |
line wrap: on
line diff
--- a/NEWS Tue Jul 18 08:41:09 2017 +0200 +++ b/NEWS Tue Jul 18 17:33:26 2017 +0200 @@ -7,12 +7,12 @@ * Orthanc now anonymizes according to Basic Profile of PS 3.15-2017c Table E.1-1 * In the "DicomModalities" configuration: - Manufacturer type MedInria is now obsolete - - Manufacturer types AgfaImpax and SyngoVia are obsolete too, - use GenericNoWildcardInDates instead + - Manufacturer types AgfaImpax and SyngoVia are obsolete too + (use GenericNoWildcardInDates instead) - Obsolete manufacturers are still accepted but might disappear in the future - - Added new manufacturer: GenericNoWidlcards to replace all '*' by '' in + - Added new manufacturer: GenericNoUniversalWildcard to replace all '*' by '' in outgoing C-Find requests -* "Locale" configuration option +* New security-related options: "DicomAlwaysAllowStore" and "DicomCheckModalityHost" REST API -------- @@ -46,6 +46,7 @@ * Fix issue 45 (crash when providing a folder to "--config" command-line option) * Fix issue 46 (PHI remaining after anonymization) * Fix issue 49 (worklists: accentuated characters are removed from C-Find responses) +* Fix issue 52 (DICOM level security association problems) * Fix issue 55 (modification/anonymization of tags that might break the database model now requires the "Force" parameter to be set to "true" in the query) * Fix issue 56 (case-insensitive matching over accents) @@ -53,7 +54,8 @@ * Fix XSS inside DICOM in Orthanc Explorer (as reported by Victor Pasnkel, Morphus Labs) * Upgrade to DCMTK 3.6.2 in static builds (released on 2017-07-17) * Upgrade to Boost 1.64.0 in static builds -* Removed configuration option USE_DCMTK_361_PRIVATE_DIC +* New advanced "Locale" configuration option +* Removed configuration option "USE_DCMTK_361_PRIVATE_DIC" Version 1.2.0 (2016/12/13)
--- a/OrthancServer/DicomProtocol/DicomUserConnection.cpp Tue Jul 18 08:41:09 2017 +0200 +++ b/OrthancServer/DicomProtocol/DicomUserConnection.cpp Tue Jul 18 17:33:26 2017 +0200 @@ -496,7 +496,7 @@ switch (manufacturer) { case ModalityManufacturer_GenericNoWildcardInDates: - case ModalityManufacturer_GenericNoWildcards: + case ModalityManufacturer_GenericNoUniversalWildcard: { std::auto_ptr<DicomMap> fix(fields.Clone()); @@ -508,7 +508,7 @@ // Replace a "*" wildcard query by an empty query ("") for // "date" or "all" value representations depending on the // type of manufacturer. - if (manufacturer == ModalityManufacturer_GenericNoWildcards || + if (manufacturer == ModalityManufacturer_GenericNoUniversalWildcard || (manufacturer == ModalityManufacturer_GenericNoWildcardInDates && FromDcmtkBridge::LookupValueRepresentation(*it) == ValueRepresentation_Date)) {
--- a/OrthancServer/Internals/CommandDispatcher.cpp Tue Jul 18 08:41:09 2017 +0200 +++ b/OrthancServer/Internals/CommandDispatcher.cpp Tue Jul 18 17:33:26 2017 +0200 @@ -806,13 +806,12 @@ // Check whether this request is allowed by the security filter if (supported && - request != DicomRequestType_Echo && // Always allow incoming ECHO requests filter_ != NULL && !filter_->IsAllowedRequest(remoteIp_, remoteAet_, calledAet_, request)) { - LOG(ERROR) << EnumerationToString(request) - << " requests are disallowed for the AET \"" - << remoteAet_ << "\""; + LOG(WARNING) << "Rejected " << EnumerationToString(request) + << " request from remote DICOM modality with AET \"" + << remoteAet_ << "\" and hostname \"" << remoteIp_ << "\""; cond = DIMSE_ILLEGALASSOCIATION; supported = false; finished = true; @@ -906,7 +905,7 @@ else { OFString temp_str; - LOG(ERROR) << "DIMSE failure (aborting association): " << cond.text(); + LOG(INFO) << "DIMSE failure (aborting association): " << cond.text(); /* some kind of error so abort the association */ ASC_abortAssociation(assoc_); }
--- a/OrthancServer/OrthancInitialization.cpp Tue Jul 18 08:41:09 2017 +0200 +++ b/OrthancServer/OrthancInitialization.cpp Tue Jul 18 17:33:26 2017 +0200 @@ -855,10 +855,30 @@ } - bool Configuration::IsKnownAETitle(const std::string& aet) + bool Configuration::IsKnownAETitle(const std::string& aet, + const std::string& ip) { RemoteModalityParameters modality; - return LookupDicomModalityUsingAETitle(modality, aet); + + if (!LookupDicomModalityUsingAETitle(modality, aet)) + { + LOG(WARNING) << "Modality \"" << aet + << "\" is not listed in the \"DicomModalities\" configuration option"; + return false; + } + else if (!Configuration::GetGlobalBoolParameter("DicomCheckModalityHost", false) || + ip == modality.GetHost()) + { + return true; + } + else + { + LOG(WARNING) << "Forbidding access from AET \"" << aet + << "\" given its hostname (" << ip << ") does not match " + << "the \"DicomModalities\" configuration option (" + << modality.GetHost() << " was expected)"; + return false; + } }
--- a/OrthancServer/OrthancInitialization.h Tue Jul 18 08:41:09 2017 +0200 +++ b/OrthancServer/OrthancInitialization.h Tue Jul 18 17:33:26 2017 +0200 @@ -106,7 +106,8 @@ static void GetGlobalListOfStringsParameter(std::list<std::string>& target, const std::string& key); - static bool IsKnownAETitle(const std::string& aet); + static bool IsKnownAETitle(const std::string& aet, + const std::string& ip); static bool IsSameAETitle(const std::string& aet1, const std::string& aet2);
--- a/OrthancServer/ServerEnumerations.cpp Tue Jul 18 08:41:09 2017 +0200 +++ b/OrthancServer/ServerEnumerations.cpp Tue Jul 18 17:33:26 2017 +0200 @@ -328,8 +328,8 @@ case ModalityManufacturer_GenericNoWildcardInDates: return "GenericNoWildcardInDates"; - case ModalityManufacturer_GenericNoWildcards: - return "GenericNoWildcards"; + case ModalityManufacturer_GenericNoUniversalWildcard: + return "GenericNoUniversalWildcard"; case ModalityManufacturer_StoreScp: return "StoreScp"; @@ -393,9 +393,9 @@ { return ModalityManufacturer_GenericNoWildcardInDates; } - else if (manufacturer == "GenericNoWildcards") + else if (manufacturer == "GenericNoUniversalWildcard") { - return ModalityManufacturer_GenericNoWildcards; + return ModalityManufacturer_GenericNoUniversalWildcard; } else if (manufacturer == "ClearCanvas") {
--- a/OrthancServer/ServerEnumerations.h Tue Jul 18 08:41:09 2017 +0200 +++ b/OrthancServer/ServerEnumerations.h Tue Jul 18 17:33:26 2017 +0200 @@ -60,7 +60,7 @@ { ModalityManufacturer_Generic, ModalityManufacturer_GenericNoWildcardInDates, - ModalityManufacturer_GenericNoWildcards, + ModalityManufacturer_GenericNoUniversalWildcard, ModalityManufacturer_StoreScp, ModalityManufacturer_ClearCanvas, ModalityManufacturer_Dcm4Chee,
--- a/OrthancServer/main.cpp Tue Jul 18 08:41:09 2017 +0200 +++ b/OrthancServer/main.cpp Tue Jul 18 17:33:26 2017 +0200 @@ -158,27 +158,33 @@ { } - virtual bool IsAllowedConnection(const std::string& /*remoteIp*/, - const std::string& /*remoteAet*/, - const std::string& /*calledAet*/) + virtual bool IsAllowedConnection(const std::string& remoteIp, + const std::string& remoteAet, + const std::string& calledAet) { + LOG(INFO) << "Incoming connection from AET " << remoteAet + << " on IP " << remoteIp << ", calling AET " << calledAet; + return true; } - virtual bool IsAllowedRequest(const std::string& /*remoteIp*/, + virtual bool IsAllowedRequest(const std::string& remoteIp, const std::string& remoteAet, - const std::string& /*calledAet*/, + const std::string& calledAet, DicomRequestType type) { - if (type == DicomRequestType_Store) + LOG(INFO) << "Incoming " << Orthanc::EnumerationToString(type) << " request from AET " + << remoteAet << " on IP " << remoteIp << ", calling AET " << calledAet; + + if (type == DicomRequestType_Store && + Configuration::GetGlobalBoolParameter("DicomAlwaysAllowStore", true)) { // Incoming store requests are always accepted, even from unknown AET return true; } - if (!Configuration::IsKnownAETitle(remoteAet)) + if (!Configuration::IsKnownAETitle(remoteAet, remoteIp)) { - LOG(ERROR) << "Unknown remote DICOM modality AET: \"" << remoteAet << "\""; return false; } else
--- a/Resources/Configuration.json Tue Jul 18 08:41:09 2017 +0200 +++ b/Resources/Configuration.json Tue Jul 18 17:33:26 2017 +0200 @@ -82,7 +82,8 @@ // The DICOM Application Entity Title "DicomAet" : "ORTHANC", - // Check whether the called AET corresponds during a DICOM request + // Check whether the called AET corresponds to the AET of Orthanc + // during an incoming DICOM SCU request "DicomCheckCalledAet" : false, // The DICOM port @@ -156,18 +157,31 @@ // "sample" : [ "STORESCP", "127.0.0.1", 2000 ] /** - * A fourth parameter is available to enable patches for a - * specific PACS manufacturer. The allowed values are currently - * "Generic" (default value), - * "GenericNoWildcardInDates" (to replace '*' by '' in date fields in outgoing C-Find) - * "GenericNoWildcards" (to replace '*' by '' in all fields in outgoing C-Find) - * "StoreScp" (storescp tool from DCMTK), - * "ClearCanvas", "Dcm4Chee" and "Vitrea". + * A fourth parameter is available to enable patches for + * specific PACS manufacturers. The allowed values are currently: + * - "Generic" (default value), + * - "GenericNoWildcardInDates" (to replace "*" by "" in date fields + * in outgoing C-Find requests originating from Orthanc) + * - "GenericNoUniversalWildcard" (to replace "*" by "" in all fields + * in outgoing C-Find SCU requests originating from Orthanc) + * - "StoreScp" (storescp tool from DCMTK), + * - "ClearCanvas", "Dcm4Chee" and "Vitrea". * This parameter is case-sensitive. **/ // "clearcanvas" : [ "CLEARCANVAS", "192.168.1.1", 104, "ClearCanvas" ] }, + // Whether the Orthanc SCP allows incoming C-Store requests, even + // from SCU modalities it does not know about (i.e. that are not + // listed in the "DicomModalities" option above) + "DicomAlwaysAllowStore" : true, + + // Whether Orthanc checks the IP/hostname address of the remote + // modality initiating a DICOM connection (as listed in the + // "DicomModalities" option above). If this option is set to + // "false", Orthanc only checks the AET of the remote modality. + "DicomCheckModalityHost" : false, + // The timeout (in seconds) after which the DICOM associations are // considered as closed by the Orthanc SCU (client) if the remote // DICOM SCP (server) does not answer.
--- a/UnitTestsSources/UnitTestsMain.cpp Tue Jul 18 08:41:09 2017 +0200 +++ b/UnitTestsSources/UnitTestsMain.cpp Tue Jul 18 17:33:26 2017 +0200 @@ -528,7 +528,7 @@ ASSERT_STREQ("Generic", EnumerationToString(StringToModalityManufacturer("Generic"))); ASSERT_STREQ("GenericNoWildcardInDates", EnumerationToString(StringToModalityManufacturer("GenericNoWildcardInDates"))); - ASSERT_STREQ("GenericNoWildcards", EnumerationToString(StringToModalityManufacturer("GenericNoWildcards"))); + ASSERT_STREQ("GenericNoUniversalWildcard", EnumerationToString(StringToModalityManufacturer("GenericNoUniversalWildcard"))); ASSERT_STREQ("StoreScp", EnumerationToString(StringToModalityManufacturer("StoreScp"))); ASSERT_STREQ("ClearCanvas", EnumerationToString(StringToModalityManufacturer("ClearCanvas"))); ASSERT_STREQ("Dcm4Chee", EnumerationToString(StringToModalityManufacturer("Dcm4Chee")));