comparison OrthancFramework/Sources/DicomNetworking/DicomAssociationParameters.cpp @ 4438:4a4e33c9082d

configuration options for DICOM TLS in Orthanc SCU
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 07 Jan 2021 16:53:35 +0100
parents d9473bd5ed43
children f4dbdb2dcba6
comparison
equal deleted inserted replaced
4437:d9473bd5ed43 4438:4a4e33c9082d
25 25
26 #include "../Compatibility.h" 26 #include "../Compatibility.h"
27 #include "../Logging.h" 27 #include "../Logging.h"
28 #include "../OrthancException.h" 28 #include "../OrthancException.h"
29 #include "../SerializationToolbox.h" 29 #include "../SerializationToolbox.h"
30 #include "../SystemToolbox.h"
30 #include "NetworkingCompatibility.h" 31 #include "NetworkingCompatibility.h"
31 32
32 #include <boost/thread/mutex.hpp> 33 #include <boost/thread/mutex.hpp>
33 34
34 // By default, the timeout for client DICOM connections is set to 10 seconds 35 // By default, the default timeout for client DICOM connections is set to 10 seconds
35 static boost::mutex defaultTimeoutMutex_; 36 static boost::mutex defaultConfigurationMutex_;
36 static uint32_t defaultTimeout_ = 10; 37 static uint32_t defaultTimeout_ = 10;
38 static std::string defaultOwnPrivateKeyPath_;
39 static std::string defaultOwnCertificatePath_;
40 static std::string defaultTrustedCertificatesPath_;
37 41
38 42
39 namespace Orthanc 43 namespace Orthanc
40 { 44 {
41 void DicomAssociationParameters::CheckHost(const std::string& host) 45 void DicomAssociationParameters::CheckHost(const std::string& host)
48 } 52 }
49 53
50 54
51 uint32_t DicomAssociationParameters::GetDefaultTimeout() 55 uint32_t DicomAssociationParameters::GetDefaultTimeout()
52 { 56 {
53 boost::mutex::scoped_lock lock(defaultTimeoutMutex_); 57 boost::mutex::scoped_lock lock(defaultConfigurationMutex_);
54 return defaultTimeout_; 58 return defaultTimeout_;
59 }
60
61
62 void DicomAssociationParameters::SetDefaultParameters()
63 {
64 boost::mutex::scoped_lock lock(defaultConfigurationMutex_);
65 timeout_ = defaultTimeout_;
66 ownPrivateKeyPath_ = defaultOwnPrivateKeyPath_;
67 ownCertificatePath_ = defaultOwnCertificatePath_;
68 trustedCertificatesPath_ = defaultTrustedCertificatesPath_;
55 } 69 }
56 70
57 71
58 DicomAssociationParameters::DicomAssociationParameters() : 72 DicomAssociationParameters::DicomAssociationParameters() :
59 localAet_("ORTHANC"), 73 localAet_("ORTHANC"),
60 timeout_(GetDefaultTimeout()) 74 timeout_(0) // Will be set by SetDefaultParameters()
61 { 75 {
62 remote_.SetApplicationEntityTitle("ANY-SCP"); 76 remote_.SetApplicationEntityTitle("ANY-SCP");
77 SetDefaultParameters();
63 } 78 }
64 79
65 80
66 DicomAssociationParameters::DicomAssociationParameters(const std::string& localAet, 81 DicomAssociationParameters::DicomAssociationParameters(const std::string& localAet,
67 const RemoteModalityParameters& remote) : 82 const RemoteModalityParameters& remote) :
68 localAet_(localAet), 83 localAet_(localAet),
69 timeout_(GetDefaultTimeout()) 84 timeout_(0) // Will be set by SetDefaultParameters()
70 { 85 {
71 SetRemoteModality(remote); 86 SetRemoteModality(remote);
87 SetDefaultParameters();
72 } 88 }
73 89
74 const std::string &DicomAssociationParameters::GetLocalApplicationEntityTitle() const 90 const std::string &DicomAssociationParameters::GetLocalApplicationEntityTitle() const
75 { 91 {
76 return localAet_; 92 return localAet_;
140 { 156 {
141 return timeout_ != 0; 157 return timeout_ != 0;
142 } 158 }
143 159
144 160
161 void DicomAssociationParameters::CheckDicomTlsConfiguration() const
162 {
163 if (!remote_.IsDicomTlsEnabled())
164 {
165 throw OrthancException(ErrorCode_BadSequenceOfCalls, "DICOM TLS is not enabled");
166 }
167 else if (ownPrivateKeyPath_.empty())
168 {
169 throw OrthancException(ErrorCode_BadSequenceOfCalls,
170 "DICOM TLS - No path to the private key of the local certificate was provided");
171 }
172 else if (ownCertificatePath_.empty())
173 {
174 throw OrthancException(ErrorCode_BadSequenceOfCalls,
175 "DICOM TLS - No path to the local certificate was provided");
176 }
177 else if (trustedCertificatesPath_.empty())
178 {
179 throw OrthancException(ErrorCode_BadSequenceOfCalls,
180 "DICOM TLS - No path to the trusted remote certificates was provided");
181 }
182 }
183
184 void DicomAssociationParameters::SetOwnCertificatePath(const std::string& privateKeyPath,
185 const std::string& certificatePath)
186 {
187 ownPrivateKeyPath_ = privateKeyPath;
188 ownCertificatePath_ = certificatePath;
189 }
190
191 void DicomAssociationParameters::SetTrustedCertificatesPath(const std::string& path)
192 {
193 trustedCertificatesPath_ = path;
194 }
195
196 const std::string& DicomAssociationParameters::GetOwnPrivateKeyPath() const
197 {
198 CheckDicomTlsConfiguration();
199 return ownPrivateKeyPath_;
200 }
201
202 const std::string& DicomAssociationParameters::GetOwnCertificatePath() const
203 {
204 CheckDicomTlsConfiguration();
205 return ownCertificatePath_;
206 }
207
208 const std::string& DicomAssociationParameters::GetTrustedCertificatesPath() const
209 {
210 CheckDicomTlsConfiguration();
211 return trustedCertificatesPath_;
212 }
213
214
215
145 static const char* const LOCAL_AET = "LocalAet"; 216 static const char* const LOCAL_AET = "LocalAet";
146 static const char* const REMOTE = "Remote"; 217 static const char* const REMOTE = "Remote";
147 static const char* const TIMEOUT = "Timeout"; // New in Orthanc in 1.7.0 218 static const char* const TIMEOUT = "Timeout"; // New in Orthanc in 1.7.0
219 static const char* const OWN_PRIVATE_KEY = "OwnPrivateKey"; // New in Orthanc 1.9.0
220 static const char* const OWN_CERTIFICATE = "OwnCertificate"; // New in Orthanc 1.9.0
221 static const char* const TRUSTED_CERTIFICATES = "TrustedCertificates"; // New in Orthanc 1.9.0
148 222
149 223
150 void DicomAssociationParameters::SerializeJob(Json::Value& target) const 224 void DicomAssociationParameters::SerializeJob(Json::Value& target) const
151 { 225 {
152 if (target.type() != Json::objectValue) 226 if (target.type() != Json::objectValue)
156 else 230 else
157 { 231 {
158 target[LOCAL_AET] = localAet_; 232 target[LOCAL_AET] = localAet_;
159 remote_.Serialize(target[REMOTE], true /* force advanced format */); 233 remote_.Serialize(target[REMOTE], true /* force advanced format */);
160 target[TIMEOUT] = timeout_; 234 target[TIMEOUT] = timeout_;
235
236 // Don't write the DICOM TLS parameters if they are not required
237 if (ownPrivateKeyPath_.empty())
238 {
239 target.removeMember(OWN_PRIVATE_KEY);
240 }
241 else
242 {
243 target[OWN_PRIVATE_KEY] = ownPrivateKeyPath_;
244 }
245
246 if (ownCertificatePath_.empty())
247 {
248 target.removeMember(OWN_CERTIFICATE);
249 }
250 else
251 {
252 target[OWN_CERTIFICATE] = ownCertificatePath_;
253 }
254
255 if (trustedCertificatesPath_.empty())
256 {
257 target.removeMember(TRUSTED_CERTIFICATES);
258 }
259 else
260 {
261 target[TRUSTED_CERTIFICATES] = trustedCertificatesPath_;
262 }
161 } 263 }
162 } 264 }
163 265
164 266
165 DicomAssociationParameters DicomAssociationParameters::UnserializeJob(const Json::Value& serialized) 267 DicomAssociationParameters DicomAssociationParameters::UnserializeJob(const Json::Value& serialized)
166 { 268 {
167 if (serialized.type() == Json::objectValue) 269 if (serialized.type() == Json::objectValue)
168 { 270 {
169 DicomAssociationParameters result; 271 DicomAssociationParameters result;
170 272
273 if (!serialized.isMember(REMOTE))
274 {
275 throw OrthancException(ErrorCode_BadFileFormat);
276 }
277
171 result.remote_ = RemoteModalityParameters(serialized[REMOTE]); 278 result.remote_ = RemoteModalityParameters(serialized[REMOTE]);
172 result.localAet_ = SerializationToolbox::ReadString(serialized, LOCAL_AET); 279 result.localAet_ = SerializationToolbox::ReadString(serialized, LOCAL_AET);
173 result.timeout_ = SerializationToolbox::ReadInteger(serialized, TIMEOUT, GetDefaultTimeout()); 280 result.timeout_ = SerializationToolbox::ReadInteger(serialized, TIMEOUT, GetDefaultTimeout());
174 281
282 if (serialized.isMember(OWN_PRIVATE_KEY))
283 {
284 result.ownPrivateKeyPath_ = SerializationToolbox::ReadString(serialized, OWN_PRIVATE_KEY);
285 }
286 else
287 {
288 result.ownPrivateKeyPath_.clear();
289 }
290
291 if (serialized.isMember(OWN_CERTIFICATE))
292 {
293 result.ownCertificatePath_ = SerializationToolbox::ReadString(serialized, OWN_CERTIFICATE);
294 }
295 else
296 {
297 result.ownCertificatePath_.clear();
298 }
299
300 if (serialized.isMember(TRUSTED_CERTIFICATES))
301 {
302 result.trustedCertificatesPath_ = SerializationToolbox::ReadString(serialized, TRUSTED_CERTIFICATES);
303 }
304 else
305 {
306 result.trustedCertificatesPath_.clear();
307 }
308
175 return result; 309 return result;
176 } 310 }
177 else 311 else
178 { 312 {
179 throw OrthancException(ErrorCode_BadFileFormat); 313 throw OrthancException(ErrorCode_BadFileFormat);
185 { 319 {
186 CLOG(INFO, DICOM) << "Default timeout for DICOM connections if Orthanc acts as SCU (client): " 320 CLOG(INFO, DICOM) << "Default timeout for DICOM connections if Orthanc acts as SCU (client): "
187 << seconds << " seconds (0 = no timeout)"; 321 << seconds << " seconds (0 = no timeout)";
188 322
189 { 323 {
190 boost::mutex::scoped_lock lock(defaultTimeoutMutex_); 324 boost::mutex::scoped_lock lock(defaultConfigurationMutex_);
191 defaultTimeout_ = seconds; 325 defaultTimeout_ = seconds;
192 } 326 }
193 } 327 }
328
329
330 void DicomAssociationParameters::SetDefaultOwnCertificatePath(const std::string& privateKeyPath,
331 const std::string& certificatePath)
332 {
333 if (!privateKeyPath.empty() &&
334 !certificatePath.empty())
335 {
336 CLOG(INFO, DICOM) << "Setting the default TLS certificate for DICOM SCU connections: "
337 << privateKeyPath << " (key), " << certificatePath << " (certificate)";
338
339 if (certificatePath.empty())
340 {
341 throw OrthancException(ErrorCode_ParameterOutOfRange, "No path to the default DICOM TLS certificate was provided");
342 }
343
344 if (privateKeyPath.empty())
345 {
346 throw OrthancException(ErrorCode_ParameterOutOfRange,
347 "No path to the private key for the default DICOM TLS certificate was provided");
348 }
349
350 if (!SystemToolbox::IsRegularFile(privateKeyPath))
351 {
352 throw OrthancException(ErrorCode_InexistentFile, "Inexistent file: " + privateKeyPath);
353 }
354
355 if (!SystemToolbox::IsRegularFile(certificatePath))
356 {
357 throw OrthancException(ErrorCode_InexistentFile, "Inexistent file: " + certificatePath);
358 }
359
360 {
361 boost::mutex::scoped_lock lock(defaultConfigurationMutex_);
362 defaultOwnPrivateKeyPath_ = privateKeyPath;
363 defaultOwnCertificatePath_ = certificatePath;
364 }
365 }
366 else
367 {
368 boost::mutex::scoped_lock lock(defaultConfigurationMutex_);
369 defaultOwnPrivateKeyPath_.clear();
370 defaultOwnCertificatePath_.clear();
371 }
372 }
373
374
375 void DicomAssociationParameters::SetDefaultTrustedCertificatesPath(const std::string& path)
376 {
377 if (!path.empty())
378 {
379 CLOG(INFO, DICOM) << "Setting the default trusted certificates for DICOM SCU connections: " << path;
380
381 if (!SystemToolbox::IsRegularFile(path))
382 {
383 throw OrthancException(ErrorCode_InexistentFile, "Inexistent file: " + path);
384 }
385
386 {
387 boost::mutex::scoped_lock lock(defaultConfigurationMutex_);
388 defaultTrustedCertificatesPath_ = path;
389 }
390 }
391 else
392 {
393 boost::mutex::scoped_lock lock(defaultConfigurationMutex_);
394 defaultTrustedCertificatesPath_.clear();
395 }
396 }
194 } 397 }