Mercurial > hg > orthanc
annotate OrthancFramework/Sources/DicomNetworking/Internals/DicomTls.cpp @ 5623:56e1e7d0f92a tls-ignore-unexpected-tls
TLS test code
author | Alain Mazy <am@orthanc.team> |
---|---|
date | Tue, 14 May 2024 09:44:01 +0200 |
parents | 335e2079de1f |
children | d9824edad40a |
rev | line source |
---|---|
4432 | 1 /** |
2 * Orthanc - A Lightweight, RESTful DICOM Store | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
5485
48b8dae6dc77
upgrade to year 2024
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5185
diff
changeset
|
5 * Copyright (C) 2017-2024 Osimis S.A., Belgium |
48b8dae6dc77
upgrade to year 2024
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5185
diff
changeset
|
6 * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium |
4432 | 7 * |
8 * This program is free software: you can redistribute it and/or | |
9 * modify it under the terms of the GNU Lesser General Public License | |
10 * as published by the Free Software Foundation, either version 3 of | |
11 * the License, or (at your option) any later version. | |
12 * | |
13 * This program is distributed in the hope that it will be useful, but | |
14 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 * Lesser General Public License for more details. | |
17 * | |
18 * You should have received a copy of the GNU Lesser General Public | |
19 * License along with this program. If not, see | |
20 * <http://www.gnu.org/licenses/>. | |
21 **/ | |
22 | |
23 | |
24 #include "../../PrecompiledHeaders.h" | |
25 #include "DicomTls.h" | |
26 | |
27 #include "../../Logging.h" | |
28 #include "../../OrthancException.h" | |
29 #include "../../SystemToolbox.h" | |
5622
335e2079de1f
testing OpenSSL ignore_unexpected_eof option
Alain Mazy <am@orthanc.team>
parents:
5598
diff
changeset
|
30 #include <openssl/ssl.h> |
4432 | 31 |
32 #if DCMTK_VERSION_NUMBER < 364 | |
33 # define DCF_Filetype_PEM SSL_FILETYPE_PEM | |
34 # if OPENSSL_VERSION_NUMBER >= 0x0090700fL | |
35 // This seems to correspond to TSP_Profile_AES: https://support.dcmtk.org/docs/tlsciphr_8h.html | |
36 static std::string opt_ciphersuites(TLS1_TXT_RSA_WITH_AES_128_SHA ":" SSL3_TXT_RSA_DES_192_CBC3_SHA); | |
37 # else | |
38 // This seems to correspond to TSP_Profile_Basic in DCMTK >= 3.6.4: https://support.dcmtk.org/docs/tlsciphr_8h.html | |
39 static std::string opt_ciphersuites(SSL3_TXT_RSA_DES_192_CBC3_SHA); | |
40 # endif | |
41 #endif | |
42 | |
43 | |
44 namespace Orthanc | |
45 { | |
46 namespace Internals | |
47 { | |
4997
1f93dc290628
fix compatibility with dcmtk 3.6.7
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4870
diff
changeset
|
48 #if DCMTK_VERSION_NUMBER >= 367 |
1f93dc290628
fix compatibility with dcmtk 3.6.7
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4870
diff
changeset
|
49 static bool IsFailure(OFCondition cond) |
1f93dc290628
fix compatibility with dcmtk 3.6.7
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4870
diff
changeset
|
50 { |
1f93dc290628
fix compatibility with dcmtk 3.6.7
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4870
diff
changeset
|
51 return !cond.good(); |
1f93dc290628
fix compatibility with dcmtk 3.6.7
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4870
diff
changeset
|
52 } |
1f93dc290628
fix compatibility with dcmtk 3.6.7
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4870
diff
changeset
|
53 #else |
1f93dc290628
fix compatibility with dcmtk 3.6.7
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4870
diff
changeset
|
54 static bool IsFailure(DcmTransportLayerStatus status) |
1f93dc290628
fix compatibility with dcmtk 3.6.7
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4870
diff
changeset
|
55 { |
1f93dc290628
fix compatibility with dcmtk 3.6.7
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4870
diff
changeset
|
56 return (status != TCS_ok); |
1f93dc290628
fix compatibility with dcmtk 3.6.7
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4870
diff
changeset
|
57 } |
1f93dc290628
fix compatibility with dcmtk 3.6.7
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4870
diff
changeset
|
58 #endif |
1f93dc290628
fix compatibility with dcmtk 3.6.7
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4870
diff
changeset
|
59 |
1f93dc290628
fix compatibility with dcmtk 3.6.7
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4870
diff
changeset
|
60 |
4432 | 61 DcmTLSTransportLayer* InitializeDicomTls(T_ASC_Network *network, |
62 T_ASC_NetworkRole role, | |
4438
4a4e33c9082d
configuration options for DICOM TLS in Orthanc SCU
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4437
diff
changeset
|
63 const std::string& ownPrivateKeyPath, |
4a4e33c9082d
configuration options for DICOM TLS in Orthanc SCU
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4437
diff
changeset
|
64 const std::string& ownCertificatePath, |
4656
82a314325351
New configuration option: "DicomTlsRemoteCertificateRequired"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4438
diff
changeset
|
65 const std::string& trustedCertificatesPath, |
82a314325351
New configuration option: "DicomTlsRemoteCertificateRequired"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4438
diff
changeset
|
66 bool requireRemoteCertificate) |
4432 | 67 { |
68 if (network == NULL) | |
69 { | |
70 throw OrthancException(ErrorCode_NullPointer); | |
71 } | |
72 | |
73 if (role != NET_ACCEPTOR && | |
74 role != NET_REQUESTOR) | |
75 { | |
76 throw OrthancException(ErrorCode_ParameterOutOfRange, "Unknown role"); | |
77 } | |
78 | |
5598
317850cd46bc
fix DicomTlsTrustedCertificates configuration that should not be mandatory if DicomTlsRemoteCertificateRequired is false
Alain Mazy <am@orthanc.team>
parents:
5487
diff
changeset
|
79 if (requireRemoteCertificate && !SystemToolbox::IsRegularFile(trustedCertificatesPath)) |
4432 | 80 { |
81 throw OrthancException(ErrorCode_InexistentFile, "Cannot read file with trusted certificates for DICOM TLS: " + | |
4438
4a4e33c9082d
configuration options for DICOM TLS in Orthanc SCU
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4437
diff
changeset
|
82 trustedCertificatesPath); |
4432 | 83 } |
84 | |
4438
4a4e33c9082d
configuration options for DICOM TLS in Orthanc SCU
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4437
diff
changeset
|
85 if (!SystemToolbox::IsRegularFile(ownPrivateKeyPath)) |
4432 | 86 { |
87 throw OrthancException(ErrorCode_InexistentFile, "Cannot read file with own private key for DICOM TLS: " + | |
4438
4a4e33c9082d
configuration options for DICOM TLS in Orthanc SCU
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4437
diff
changeset
|
88 ownPrivateKeyPath); |
4432 | 89 } |
90 | |
4438
4a4e33c9082d
configuration options for DICOM TLS in Orthanc SCU
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4437
diff
changeset
|
91 if (!SystemToolbox::IsRegularFile(ownCertificatePath)) |
4432 | 92 { |
93 throw OrthancException(ErrorCode_InexistentFile, "Cannot read file with own certificate for DICOM TLS: " + | |
4438
4a4e33c9082d
configuration options for DICOM TLS in Orthanc SCU
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4437
diff
changeset
|
94 ownCertificatePath); |
4432 | 95 } |
96 | |
97 CLOG(INFO, DICOM) << "Initializing DICOM TLS for Orthanc " | |
98 << (role == NET_ACCEPTOR ? "SCP" : "SCU"); | |
99 | |
100 #if DCMTK_VERSION_NUMBER >= 364 | |
101 const T_ASC_NetworkRole tmpRole = role; | |
102 #else | |
103 int tmpRole; | |
104 switch (role) | |
105 { | |
106 case NET_ACCEPTOR: | |
107 tmpRole = DICOM_APPLICATION_ACCEPTOR; | |
108 break; | |
109 | |
110 case NET_REQUESTOR: | |
111 tmpRole = DICOM_APPLICATION_REQUESTOR; | |
112 break; | |
113 | |
114 default: | |
115 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
116 } | |
117 #endif | |
118 | |
119 std::unique_ptr<DcmTLSTransportLayer> tls( | |
120 new DcmTLSTransportLayer(tmpRole /*opt_networkRole*/, NULL /*opt_readSeedFile*/, | |
121 OFFalse /*initializeOpenSSL, done by Orthanc::Toolbox::InitializeOpenSsl()*/)); | |
122 | |
5598
317850cd46bc
fix DicomTlsTrustedCertificates configuration that should not be mandatory if DicomTlsRemoteCertificateRequired is false
Alain Mazy <am@orthanc.team>
parents:
5487
diff
changeset
|
123 if (requireRemoteCertificate && IsFailure(tls->addTrustedCertificateFile(trustedCertificatesPath.c_str(), DCF_Filetype_PEM /*opt_keyFileFormat*/))) |
4432 | 124 { |
125 throw OrthancException(ErrorCode_BadFileFormat, "Cannot parse PEM file with trusted certificates for DICOM TLS: " + | |
4438
4a4e33c9082d
configuration options for DICOM TLS in Orthanc SCU
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4437
diff
changeset
|
126 trustedCertificatesPath); |
4432 | 127 } |
128 | |
4997
1f93dc290628
fix compatibility with dcmtk 3.6.7
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4870
diff
changeset
|
129 if (IsFailure(tls->setPrivateKeyFile(ownPrivateKeyPath.c_str(), DCF_Filetype_PEM /*opt_keyFileFormat*/))) |
4432 | 130 { |
131 throw OrthancException(ErrorCode_BadFileFormat, "Cannot parse PEM file with private key for DICOM TLS: " + | |
4438
4a4e33c9082d
configuration options for DICOM TLS in Orthanc SCU
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4437
diff
changeset
|
132 ownPrivateKeyPath); |
4432 | 133 } |
134 | |
5487
33f8e180edcf
upgraded static build to dcmtk 3.6.8
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5485
diff
changeset
|
135 if (IsFailure(tls->setCertificateFile( |
33f8e180edcf
upgraded static build to dcmtk 3.6.8
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5485
diff
changeset
|
136 ownCertificatePath.c_str(), DCF_Filetype_PEM /*opt_keyFileFormat*/ |
33f8e180edcf
upgraded static build to dcmtk 3.6.8
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5485
diff
changeset
|
137 #if DCMTK_VERSION_NUMBER >= 368 |
33f8e180edcf
upgraded static build to dcmtk 3.6.8
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5485
diff
changeset
|
138 /** |
33f8e180edcf
upgraded static build to dcmtk 3.6.8
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5485
diff
changeset
|
139 * DICOM BCP 195 RFC 8996 TLS Profile, based on RFC 8996 and RFC 9325. |
33f8e180edcf
upgraded static build to dcmtk 3.6.8
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5485
diff
changeset
|
140 * This profile only negotiates TLS 1.2 or newer, and will not fall back to |
33f8e180edcf
upgraded static build to dcmtk 3.6.8
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5485
diff
changeset
|
141 * previous TLS versions. It provides the higher security level offered by the |
33f8e180edcf
upgraded static build to dcmtk 3.6.8
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5485
diff
changeset
|
142 * 2021 revised edition of BCP 195. |
33f8e180edcf
upgraded static build to dcmtk 3.6.8
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5485
diff
changeset
|
143 **/ |
33f8e180edcf
upgraded static build to dcmtk 3.6.8
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5485
diff
changeset
|
144 , TSP_Profile_BCP_195_RFC_8996 |
33f8e180edcf
upgraded static build to dcmtk 3.6.8
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5485
diff
changeset
|
145 #endif |
33f8e180edcf
upgraded static build to dcmtk 3.6.8
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5485
diff
changeset
|
146 ))) |
4432 | 147 { |
148 throw OrthancException(ErrorCode_BadFileFormat, "Cannot parse PEM file with own certificate for DICOM TLS: " + | |
4438
4a4e33c9082d
configuration options for DICOM TLS in Orthanc SCU
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4437
diff
changeset
|
149 ownCertificatePath); |
4432 | 150 } |
151 | |
152 if (!tls->checkPrivateKeyMatchesCertificate()) | |
153 { | |
154 throw OrthancException(ErrorCode_BadFileFormat, "The private key doesn't match the own certificate: " + | |
4438
4a4e33c9082d
configuration options for DICOM TLS in Orthanc SCU
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4437
diff
changeset
|
155 ownPrivateKeyPath + " vs. " + ownCertificatePath); |
4432 | 156 } |
157 | |
158 #if DCMTK_VERSION_NUMBER >= 364 | |
5623 | 159 // TODO: add parameters to select TSP_Profile ? |
160 if (IsFailure(tls->setTLSProfile(TSP_Profile_AES /*opt_tlsProfile*/))) | |
161 //if (IsFailure(tls->setTLSProfile(TSP_Profile_BCP195 /*opt_tlsProfile*/))) | |
4432 | 162 { |
163 throw OrthancException(ErrorCode_InternalError, "Cannot set the DICOM TLS profile"); | |
164 } | |
165 | |
4997
1f93dc290628
fix compatibility with dcmtk 3.6.7
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4870
diff
changeset
|
166 if (IsFailure(tls->activateCipherSuites())) |
4432 | 167 { |
168 throw OrthancException(ErrorCode_InternalError, "Cannot activate the cipher suites for DICOM TLS"); | |
169 } | |
5622
335e2079de1f
testing OpenSSL ignore_unexpected_eof option
Alain Mazy <am@orthanc.team>
parents:
5598
diff
changeset
|
170 |
5623 | 171 // TODO: add parameters to enable/disable IGNORE_UNEXPECTED_EOF ? |
5622
335e2079de1f
testing OpenSSL ignore_unexpected_eof option
Alain Mazy <am@orthanc.team>
parents:
5598
diff
changeset
|
172 DcmTLSTransportLayer::native_handle_type sslNativeHandle = tls->getNativeHandle(); |
335e2079de1f
testing OpenSSL ignore_unexpected_eof option
Alain Mazy <am@orthanc.team>
parents:
5598
diff
changeset
|
173 SSL_CTX_set_options(sslNativeHandle, SSL_OP_IGNORE_UNEXPECTED_EOF); |
5623 | 174 |
4432 | 175 #else |
176 CLOG(INFO, DICOM) << "Using the following cipher suites for DICOM TLS: " << opt_ciphersuites; | |
4997
1f93dc290628
fix compatibility with dcmtk 3.6.7
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4870
diff
changeset
|
177 if (IsFailure(tls->setCipherSuites(opt_ciphersuites.c_str()))) |
4432 | 178 { |
179 throw OrthancException(ErrorCode_InternalError, "Unable to set cipher suites to: " + opt_ciphersuites); | |
180 } | |
181 #endif | |
182 | |
4656
82a314325351
New configuration option: "DicomTlsRemoteCertificateRequired"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4438
diff
changeset
|
183 if (requireRemoteCertificate) |
82a314325351
New configuration option: "DicomTlsRemoteCertificateRequired"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4438
diff
changeset
|
184 { |
82a314325351
New configuration option: "DicomTlsRemoteCertificateRequired"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4438
diff
changeset
|
185 // Check remote certificate, fail if no certificate is present |
82a314325351
New configuration option: "DicomTlsRemoteCertificateRequired"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4438
diff
changeset
|
186 tls->setCertificateVerification(DCV_requireCertificate /*opt_certVerification*/); |
82a314325351
New configuration option: "DicomTlsRemoteCertificateRequired"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4438
diff
changeset
|
187 } |
82a314325351
New configuration option: "DicomTlsRemoteCertificateRequired"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4438
diff
changeset
|
188 else |
82a314325351
New configuration option: "DicomTlsRemoteCertificateRequired"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4438
diff
changeset
|
189 { |
82a314325351
New configuration option: "DicomTlsRemoteCertificateRequired"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4438
diff
changeset
|
190 // Check remote certificate if present, succeed if no certificate is present |
82a314325351
New configuration option: "DicomTlsRemoteCertificateRequired"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4438
diff
changeset
|
191 tls->setCertificateVerification(DCV_checkCertificate /*opt_certVerification*/); |
82a314325351
New configuration option: "DicomTlsRemoteCertificateRequired"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
4438
diff
changeset
|
192 } |
4432 | 193 |
194 if (ASC_setTransportLayer(network, tls.get(), 0).bad()) | |
195 { | |
196 throw OrthancException(ErrorCode_InternalError, "Cannot enable DICOM TLS in the Orthanc " + | |
197 std::string(role == NET_ACCEPTOR ? "SCP" : "SCU")); | |
198 } | |
199 | |
200 return tls.release(); | |
201 } | |
202 } | |
203 } |