/src/qtbase/src/plugins/tls/openssl/qsslcontext_openssl.cpp
Line | Count | Source |
1 | | // Copyright (C) 2017 The Qt Company Ltd. |
2 | | // Copyright (C) 2014 BlackBerry Limited. All rights reserved. |
3 | | // Copyright (C) 2014 Governikus GmbH & Co. KG. |
4 | | // Copyright (C) 2016 Richard J. Moore <rich@kde.org> |
5 | | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
6 | | // Qt-Security score:critical reason:cryptography |
7 | | |
8 | | #include <QtNetwork/qsslsocket.h> |
9 | | #include <QtNetwork/qssldiffiehellmanparameters.h> |
10 | | |
11 | | #include "qsslsocket_openssl_symbols_p.h" |
12 | | #include "qsslcontext_openssl_p.h" |
13 | | #include "qtlsbackend_openssl_p.h" |
14 | | #include "qtlskey_openssl_p.h" |
15 | | #include "qopenssl_p.h" |
16 | | |
17 | | #include <QtNetwork/private/qssl_p.h> |
18 | | #include <QtNetwork/private/qsslsocket_p.h> |
19 | | #include <QtNetwork/private/qtlsbackend_p.h> |
20 | | |
21 | | #include <QtNetwork/private/qssldiffiehellmanparameters_p.h> |
22 | | |
23 | | #include <vector> |
24 | | |
25 | | QT_BEGIN_NAMESPACE |
26 | | |
27 | | Q_GLOBAL_STATIC(bool, forceSecurityLevel) |
28 | | |
29 | | namespace QTlsPrivate |
30 | | { |
31 | | // These callback functions are defined in qtls_openssl.cpp. |
32 | | int q_X509Callback(int ok, X509_STORE_CTX *ctx); |
33 | | int q_X509CallbackDirect(int ok, X509_STORE_CTX *ctx); |
34 | | |
35 | | #if QT_CONFIG(ocsp) |
36 | | int qt_OCSP_status_server_callback(SSL *ssl, void *); |
37 | | #endif // ocsp |
38 | | |
39 | | #ifdef TLS1_3_VERSION |
40 | | int q_ssl_sess_set_new_cb(SSL *context, SSL_SESSION *session); |
41 | | #endif // TLS1_3_VERSION |
42 | | |
43 | | } // namespace QTlsPrivate |
44 | | |
45 | | #if QT_CONFIG(dtls) |
46 | | // defined in qdtls_openssl.cpp: |
47 | | namespace dtlscallbacks |
48 | | { |
49 | | int q_X509DtlsCallback(int ok, X509_STORE_CTX *ctx); |
50 | | int q_generate_cookie_callback(SSL *ssl, unsigned char *dst, unsigned *cookieLength); |
51 | | int q_verify_cookie_callback(SSL *ssl, const unsigned char *cookie, unsigned cookieLength); |
52 | | } // namespace dtlscallbacks |
53 | | #endif // dtls |
54 | | |
55 | | static inline QString msgErrorSettingBackendConfig(const QString &why) |
56 | 0 | { |
57 | 0 | return QSslSocket::tr("Error when setting the OpenSSL configuration (%1)").arg(why); |
58 | 0 | } |
59 | | |
60 | | static inline QString msgErrorSettingEllipticCurves(const QString &why) |
61 | 0 | { |
62 | 0 | return QSslSocket::tr("Error when setting the elliptic curves (%1)").arg(why); |
63 | 0 | } |
64 | | |
65 | | qssloptions QSslContext::setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions) |
66 | 0 | { |
67 | 0 | qssloptions options; |
68 | 0 | switch (protocol) { |
69 | 0 | QT_WARNING_PUSH |
70 | 0 | QT_WARNING_DISABLE_DEPRECATED |
71 | 0 | case QSsl::TlsV1_0OrLater: |
72 | 0 | options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; |
73 | 0 | break; |
74 | 0 | case QSsl::TlsV1_1OrLater: |
75 | 0 | options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1; |
76 | 0 | break; |
77 | 0 | QT_WARNING_POP |
78 | 0 | case QSsl::SecureProtocols: |
79 | 0 | case QSsl::TlsV1_2OrLater: |
80 | 0 | options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1; |
81 | 0 | break; |
82 | 0 | case QSsl::TlsV1_3OrLater: |
83 | 0 | options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2; |
84 | 0 | break; |
85 | 0 | default: |
86 | 0 | options = SSL_OP_ALL; |
87 | 0 | } |
88 | | |
89 | | // This option is disabled by default, so we need to be able to clear it |
90 | 0 | if (sslOptions & QSsl::SslOptionDisableEmptyFragments) |
91 | 0 | options |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; |
92 | 0 | else |
93 | 0 | options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; |
94 | |
|
95 | 0 | #ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION |
96 | | // This option is disabled by default, so we need to be able to clear it |
97 | 0 | if (sslOptions & QSsl::SslOptionDisableLegacyRenegotiation) |
98 | 0 | options &= ~SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION; |
99 | 0 | else |
100 | 0 | options |= SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION; |
101 | 0 | #endif |
102 | |
|
103 | 0 | #ifdef SSL_OP_NO_TICKET |
104 | 0 | if (sslOptions & QSsl::SslOptionDisableSessionTickets) |
105 | 0 | options |= SSL_OP_NO_TICKET; |
106 | 0 | #endif |
107 | 0 | #ifdef SSL_OP_NO_COMPRESSION |
108 | 0 | if (sslOptions & QSsl::SslOptionDisableCompression) |
109 | 0 | options |= SSL_OP_NO_COMPRESSION; |
110 | 0 | #endif |
111 | |
|
112 | 0 | if (!(sslOptions & QSsl::SslOptionDisableServerCipherPreference)) |
113 | 0 | options |= SSL_OP_CIPHER_SERVER_PREFERENCE; |
114 | |
|
115 | 0 | return options; |
116 | 0 | } |
117 | | |
118 | | QSslContext::QSslContext() |
119 | 0 | : ctx(nullptr), |
120 | 0 | pkey(nullptr), |
121 | 0 | session(nullptr), |
122 | 0 | m_sessionTicketLifeTimeHint(-1) |
123 | 0 | { |
124 | 0 | } |
125 | | |
126 | | QSslContext::~QSslContext() |
127 | 0 | { |
128 | 0 | if (ctx) |
129 | | // This will decrement the reference count by 1 and free the context eventually when possible |
130 | 0 | q_SSL_CTX_free(ctx); |
131 | |
|
132 | 0 | if (pkey) |
133 | 0 | q_EVP_PKEY_free(pkey); |
134 | |
|
135 | 0 | if (session) |
136 | 0 | q_SSL_SESSION_free(session); |
137 | 0 | } |
138 | | |
139 | | std::shared_ptr<QSslContext> QSslContext::sharedFromConfiguration(QSslSocket::SslMode mode, const QSslConfiguration &configuration, bool allowRootCertOnDemandLoading) |
140 | 0 | { |
141 | 0 | struct AccessToPrivateCtor : QSslContext {}; |
142 | 0 | std::shared_ptr<QSslContext> sslContext = std::make_shared<AccessToPrivateCtor>(); |
143 | 0 | initSslContext(sslContext.get(), mode, configuration, allowRootCertOnDemandLoading); |
144 | 0 | return sslContext; |
145 | 0 | } |
146 | | |
147 | | std::shared_ptr<QSslContext> QSslContext::sharedFromPrivateConfiguration(QSslSocket::SslMode mode, QSslConfigurationPrivate *privConfiguration, |
148 | | bool allowRootCertOnDemandLoading) |
149 | 0 | { |
150 | 0 | return sharedFromConfiguration(mode, privConfiguration, allowRootCertOnDemandLoading); |
151 | 0 | } |
152 | | |
153 | | #ifndef OPENSSL_NO_NEXTPROTONEG |
154 | | |
155 | | static int next_proto_cb(SSL *, unsigned char **out, unsigned char *outlen, |
156 | | const unsigned char *in, unsigned int inlen, void *arg) |
157 | 0 | { |
158 | 0 | QSslContext::NPNContext *ctx = reinterpret_cast<QSslContext::NPNContext *>(arg); |
159 | | |
160 | | // comment out to debug: |
161 | | // QList<QByteArray> supportedVersions; |
162 | | // for (unsigned int i = 0; i < inlen; ) { |
163 | | // QByteArray version(reinterpret_cast<const char *>(&in[i+1]), in[i]); |
164 | | // supportedVersions << version; |
165 | | // i += in[i] + 1; |
166 | | // } |
167 | |
|
168 | 0 | int proto = q_SSL_select_next_proto(out, outlen, in, inlen, ctx->data, ctx->len); |
169 | 0 | switch (proto) { |
170 | 0 | case OPENSSL_NPN_UNSUPPORTED: |
171 | 0 | ctx->status = QSslConfiguration::NextProtocolNegotiationNone; |
172 | 0 | break; |
173 | 0 | case OPENSSL_NPN_NEGOTIATED: |
174 | 0 | ctx->status = QSslConfiguration::NextProtocolNegotiationNegotiated; |
175 | 0 | break; |
176 | 0 | case OPENSSL_NPN_NO_OVERLAP: |
177 | 0 | ctx->status = QSslConfiguration::NextProtocolNegotiationUnsupported; |
178 | 0 | break; |
179 | 0 | default: |
180 | 0 | qCWarning(lcTlsBackend, "OpenSSL sent unknown NPN status"); |
181 | 0 | } |
182 | | |
183 | 0 | return SSL_TLSEXT_ERR_OK; |
184 | 0 | } |
185 | | |
186 | | QSslContext::NPNContext QSslContext::npnContext() const |
187 | 0 | { |
188 | 0 | return m_npnContext; |
189 | 0 | } |
190 | | #endif // !OPENSSL_NO_NEXTPROTONEG |
191 | | |
192 | | |
193 | | |
194 | | // Needs to be deleted by caller |
195 | | SSL* QSslContext::createSsl() |
196 | 0 | { |
197 | 0 | SSL* ssl = q_SSL_new(ctx); |
198 | 0 | q_SSL_clear(ssl); |
199 | |
|
200 | 0 | if (!session && !sessionASN1().isEmpty() |
201 | 0 | && !sslConfiguration.testSslOption(QSsl::SslOptionDisableSessionPersistence)) { |
202 | 0 | const unsigned char *data = reinterpret_cast<const unsigned char *>(m_sessionASN1.constData()); |
203 | 0 | session = q_d2i_SSL_SESSION(nullptr, &data, m_sessionASN1.size()); |
204 | | // 'session' has refcount 1 already, set by the function above |
205 | 0 | } |
206 | |
|
207 | 0 | if (session) { |
208 | | // Try to resume the last session we cached |
209 | 0 | if (!q_SSL_set_session(ssl, session)) { |
210 | 0 | qCWarning(lcTlsBackend, "could not set SSL session"); |
211 | 0 | q_SSL_SESSION_free(session); |
212 | 0 | session = nullptr; |
213 | 0 | } |
214 | 0 | } |
215 | |
|
216 | 0 | #ifndef OPENSSL_NO_NEXTPROTONEG |
217 | 0 | QList<QByteArray> protocols = sslConfiguration.d.constData()->nextAllowedProtocols; |
218 | 0 | if (!protocols.isEmpty()) { |
219 | 0 | m_supportedNPNVersions.clear(); |
220 | 0 | for (int a = 0; a < protocols.size(); ++a) { |
221 | 0 | if (protocols.at(a).size() > 255) { |
222 | 0 | qCWarning(lcTlsBackend) << "TLS NPN extension" << protocols.at(a) |
223 | 0 | << "is too long and will be ignored."; |
224 | 0 | continue; |
225 | 0 | } else if (protocols.at(a).isEmpty()) { |
226 | 0 | continue; |
227 | 0 | } |
228 | 0 | m_supportedNPNVersions.append(protocols.at(a).size()).append(protocols.at(a)); |
229 | 0 | } |
230 | 0 | if (m_supportedNPNVersions.size()) { |
231 | 0 | m_npnContext.data = reinterpret_cast<unsigned char *>(m_supportedNPNVersions.data()); |
232 | 0 | m_npnContext.len = m_supportedNPNVersions.size(); |
233 | 0 | m_npnContext.status = QSslConfiguration::NextProtocolNegotiationNone; |
234 | | // Callback's type has a parameter 'const unsigned char ** out' |
235 | | // since it was introduced in 1.0.2. Internally, OpenSSL's own code |
236 | | // (tests/examples) cast it to unsigned char * (since it's 'out'). |
237 | | // We just re-use our NPN callback and cast here: |
238 | 0 | typedef int (*alpn_callback_t) (SSL *, const unsigned char **, unsigned char *, |
239 | 0 | const unsigned char *, unsigned int, void *); |
240 | | // With ALPN callback is for a server side only, for a client m_npnContext.status |
241 | | // will stay in NextProtocolNegotiationNone. |
242 | 0 | q_SSL_CTX_set_alpn_select_cb(ctx, alpn_callback_t(next_proto_cb), &m_npnContext); |
243 | | // Client: |
244 | 0 | q_SSL_set_alpn_protos(ssl, m_npnContext.data, m_npnContext.len); |
245 | | // And in case our peer does not support ALPN, but supports NPN: |
246 | 0 | q_SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &m_npnContext); |
247 | 0 | } |
248 | 0 | } |
249 | 0 | #endif // !OPENSSL_NO_NEXTPROTONEG |
250 | |
|
251 | 0 | return ssl; |
252 | 0 | } |
253 | | |
254 | | // We cache exactly one session here |
255 | | bool QSslContext::cacheSession(SSL* ssl) |
256 | 0 | { |
257 | | // don't cache the same session again |
258 | 0 | if (session && session == q_SSL_get_session(ssl)) |
259 | 0 | return true; |
260 | | |
261 | | // decrease refcount of currently stored session |
262 | | // (this might happen if there are several concurrent handshakes in flight) |
263 | 0 | if (session) |
264 | 0 | q_SSL_SESSION_free(session); |
265 | | |
266 | | // cache the session the caller gave us and increase reference count |
267 | 0 | session = q_SSL_get1_session(ssl); |
268 | |
|
269 | 0 | if (session && !sslConfiguration.testSslOption(QSsl::SslOptionDisableSessionPersistence)) { |
270 | 0 | int sessionSize = q_i2d_SSL_SESSION(session, nullptr); |
271 | 0 | if (sessionSize > 0) { |
272 | 0 | m_sessionASN1.resize(sessionSize); |
273 | 0 | unsigned char *data = reinterpret_cast<unsigned char *>(m_sessionASN1.data()); |
274 | 0 | if (!q_i2d_SSL_SESSION(session, &data)) |
275 | 0 | qCWarning(lcTlsBackend, "could not store persistent version of SSL session"); |
276 | 0 | m_sessionTicketLifeTimeHint = q_SSL_SESSION_get_ticket_lifetime_hint(session); |
277 | 0 | } |
278 | 0 | } |
279 | |
|
280 | 0 | return (session != nullptr); |
281 | 0 | } |
282 | | |
283 | | QByteArray QSslContext::sessionASN1() const |
284 | 0 | { |
285 | 0 | return m_sessionASN1; |
286 | 0 | } |
287 | | |
288 | | void QSslContext::setSessionASN1(const QByteArray &session) |
289 | 0 | { |
290 | 0 | m_sessionASN1 = session; |
291 | 0 | } |
292 | | |
293 | | int QSslContext::sessionTicketLifeTimeHint() const |
294 | 0 | { |
295 | 0 | return m_sessionTicketLifeTimeHint; |
296 | 0 | } |
297 | | |
298 | | void QSslContext::forceAutoTestSecurityLevel() |
299 | 0 | { |
300 | 0 | *forceSecurityLevel() = true; |
301 | 0 | } |
302 | | |
303 | | QSslError::SslError QSslContext::error() const |
304 | 0 | { |
305 | 0 | return errorCode; |
306 | 0 | } |
307 | | |
308 | | QString QSslContext::errorString() const |
309 | 0 | { |
310 | 0 | return errorStr; |
311 | 0 | } |
312 | | |
313 | | void QSslContext::setGenericPrivateKey(QSslContext *sslContext, |
314 | | const QSslConfiguration &configuration) |
315 | 0 | { |
316 | 0 | auto qtKey = QTlsBackend::backend<QTlsPrivate::TlsKeyOpenSSL>(configuration.d->privateKey); |
317 | 0 | Q_ASSERT(qtKey); |
318 | 0 | sslContext->pkey = qtKey->genericKey; |
319 | 0 | Q_ASSERT(sslContext->pkey); |
320 | 0 | q_EVP_PKEY_up_ref(sslContext->pkey); |
321 | 0 | } |
322 | | |
323 | | void QSslContext::initSslContext(QSslContext *sslContext, QSslSocket::SslMode mode, |
324 | | const QSslConfiguration &configuration, |
325 | | bool allowRootCertOnDemandLoading) |
326 | 0 | { |
327 | 0 | sslContext->sslConfiguration = configuration; |
328 | 0 | sslContext->errorCode = QSslError::NoError; |
329 | |
|
330 | 0 | bool client = (mode == QSslSocket::SslClientMode); |
331 | |
|
332 | 0 | bool reinitialized = false; |
333 | 0 | bool unsupportedProtocol = false; |
334 | 0 | bool isDtls = false; |
335 | 0 | init_context: |
336 | 0 | switch (sslContext->sslConfiguration.protocol()) { |
337 | 0 | QT_WARNING_PUSH |
338 | 0 | QT_WARNING_DISABLE_DEPRECATED |
339 | 0 | case QSsl::DtlsV1_0: |
340 | 0 | case QSsl::DtlsV1_0OrLater: |
341 | 0 | QT_WARNING_POP |
342 | 0 | case QSsl::DtlsV1_2: |
343 | 0 | case QSsl::DtlsV1_2OrLater: |
344 | 0 | #if QT_CONFIG(dtls) |
345 | 0 | isDtls = true; |
346 | 0 | sslContext->ctx = q_SSL_CTX_new(client ? q_DTLS_client_method() : q_DTLS_server_method()); |
347 | | #else // dtls |
348 | | sslContext->ctx = nullptr; |
349 | | unsupportedProtocol = true; |
350 | | qCWarning(lcTlsBackend, "DTLS protocol requested, but feature 'dtls' is disabled"); |
351 | | #endif // dtls |
352 | 0 | break; |
353 | 0 | case QSsl::TlsV1_3: |
354 | 0 | case QSsl::TlsV1_3OrLater: |
355 | | #if !defined(TLS1_3_VERSION) |
356 | | qCWarning(lcTlsBackend, "TLS 1.3 is not supported"); |
357 | | sslContext->ctx = nullptr; |
358 | | unsupportedProtocol = true; |
359 | | break; |
360 | | #endif // TLS1_3_VERSION |
361 | 0 | default: |
362 | | // The ssl options will actually control the supported methods |
363 | 0 | sslContext->ctx = q_SSL_CTX_new(client ? q_TLS_client_method() : q_TLS_server_method()); |
364 | 0 | } |
365 | | |
366 | 0 | if (!sslContext->ctx) { |
367 | | // After stopping Flash 10 the SSL library loses its ciphers. Try re-adding them |
368 | | // by re-initializing the library. |
369 | 0 | if (!reinitialized) { |
370 | 0 | reinitialized = true; |
371 | 0 | if (q_OPENSSL_init_ssl(0, nullptr) == 1) |
372 | 0 | goto init_context; |
373 | 0 | } |
374 | | |
375 | 0 | sslContext->errorStr = QSslSocket::tr("Error creating SSL context (%1)").arg( |
376 | 0 | unsupportedProtocol ? QSslSocket::tr("unsupported protocol") : QTlsBackendOpenSSL::getErrorsFromOpenSsl() |
377 | 0 | ); |
378 | 0 | sslContext->errorCode = QSslError::UnspecifiedError; |
379 | 0 | return; |
380 | 0 | } |
381 | | |
382 | | // A nasty hacked OpenSSL using a level that will make our auto-tests fail: |
383 | 0 | if (q_SSL_CTX_get_security_level(sslContext->ctx) > 1 && *forceSecurityLevel()) |
384 | 0 | q_SSL_CTX_set_security_level(sslContext->ctx, 1); |
385 | |
|
386 | 0 | const long anyVersion = |
387 | 0 | #if QT_CONFIG(dtls) |
388 | 0 | isDtls ? DTLS_ANY_VERSION : TLS_ANY_VERSION; |
389 | | #else |
390 | | TLS_ANY_VERSION; |
391 | | #endif // dtls |
392 | 0 | long minVersion = anyVersion; |
393 | 0 | long maxVersion = anyVersion; |
394 | |
|
395 | 0 | switch (sslContext->sslConfiguration.protocol()) { |
396 | 0 | QT_WARNING_PUSH |
397 | 0 | QT_WARNING_DISABLE_DEPRECATED |
398 | 0 | case QSsl::TlsV1_0: |
399 | 0 | minVersion = TLS1_VERSION; |
400 | 0 | maxVersion = TLS1_VERSION; |
401 | 0 | break; |
402 | 0 | case QSsl::TlsV1_1: |
403 | 0 | minVersion = TLS1_1_VERSION; |
404 | 0 | maxVersion = TLS1_1_VERSION; |
405 | 0 | break; |
406 | 0 | QT_WARNING_POP |
407 | 0 | case QSsl::TlsV1_2: |
408 | 0 | minVersion = TLS1_2_VERSION; |
409 | 0 | maxVersion = TLS1_2_VERSION; |
410 | 0 | break; |
411 | 0 | case QSsl::TlsV1_3: |
412 | 0 | #ifdef TLS1_3_VERSION |
413 | 0 | minVersion = TLS1_3_VERSION; |
414 | 0 | maxVersion = TLS1_3_VERSION; |
415 | | #else |
416 | | // This protocol is not supported by OpenSSL 1.1 and we handle |
417 | | // it as an error (see the code above). |
418 | | Q_UNREACHABLE(); |
419 | | #endif // TLS1_3_VERSION |
420 | 0 | break; |
421 | | // Ranges: |
422 | 0 | case QSsl::AnyProtocol: |
423 | 0 | QT_WARNING_PUSH |
424 | 0 | QT_WARNING_DISABLE_DEPRECATED |
425 | 0 | case QSsl::TlsV1_0OrLater: |
426 | 0 | minVersion = TLS1_VERSION; |
427 | 0 | maxVersion = 0; |
428 | 0 | break; |
429 | 0 | case QSsl::TlsV1_1OrLater: |
430 | 0 | minVersion = TLS1_1_VERSION; |
431 | 0 | maxVersion = 0; |
432 | 0 | break; |
433 | 0 | QT_WARNING_POP |
434 | 0 | case QSsl::SecureProtocols: |
435 | 0 | case QSsl::TlsV1_2OrLater: |
436 | 0 | minVersion = TLS1_2_VERSION; |
437 | 0 | maxVersion = 0; |
438 | 0 | break; |
439 | 0 | QT_WARNING_PUSH |
440 | 0 | QT_WARNING_DISABLE_DEPRECATED |
441 | 0 | case QSsl::DtlsV1_0: |
442 | 0 | minVersion = DTLS1_VERSION; |
443 | 0 | maxVersion = DTLS1_VERSION; |
444 | 0 | break; |
445 | 0 | case QSsl::DtlsV1_0OrLater: |
446 | 0 | minVersion = DTLS1_VERSION; |
447 | 0 | maxVersion = 0; |
448 | 0 | break; |
449 | 0 | QT_WARNING_POP |
450 | 0 | case QSsl::DtlsV1_2: |
451 | 0 | minVersion = DTLS1_2_VERSION; |
452 | 0 | maxVersion = DTLS1_2_VERSION; |
453 | 0 | break; |
454 | 0 | case QSsl::DtlsV1_2OrLater: |
455 | 0 | minVersion = DTLS1_2_VERSION; |
456 | 0 | maxVersion = 0; |
457 | 0 | break; |
458 | 0 | case QSsl::TlsV1_3OrLater: |
459 | 0 | #ifdef TLS1_3_VERSION |
460 | 0 | minVersion = TLS1_3_VERSION; |
461 | 0 | maxVersion = 0; |
462 | 0 | break; |
463 | | #else |
464 | | // This protocol is not supported by OpenSSL 1.1 and we handle |
465 | | // it as an error (see the code above). |
466 | | Q_UNREACHABLE(); |
467 | | break; |
468 | | #endif // TLS1_3_VERSION |
469 | 0 | case QSsl::UnknownProtocol: |
470 | 0 | break; |
471 | 0 | } |
472 | | |
473 | 0 | if (minVersion != anyVersion |
474 | 0 | && !q_SSL_CTX_set_min_proto_version(sslContext->ctx, minVersion)) { |
475 | 0 | sslContext->errorStr = QSslSocket::tr("Error while setting the minimal protocol version"); |
476 | 0 | sslContext->errorCode = QSslError::UnspecifiedError; |
477 | 0 | return; |
478 | 0 | } |
479 | | |
480 | 0 | if (maxVersion != anyVersion |
481 | 0 | && !q_SSL_CTX_set_max_proto_version(sslContext->ctx, maxVersion)) { |
482 | 0 | sslContext->errorStr = QSslSocket::tr("Error while setting the maximum protocol version"); |
483 | 0 | sslContext->errorCode = QSslError::UnspecifiedError; |
484 | 0 | return; |
485 | 0 | } |
486 | | |
487 | | // Enable bug workarounds. |
488 | 0 | const qssloptions options = setupOpenSslOptions(configuration.protocol(), configuration.d->sslOptions); |
489 | 0 | q_SSL_CTX_set_options(sslContext->ctx, options); |
490 | | |
491 | | // Tell OpenSSL to release memory early |
492 | | // http://www.openssl.org/docs/ssl/SSL_CTX_set_mode.html |
493 | 0 | q_SSL_CTX_set_mode(sslContext->ctx, SSL_MODE_RELEASE_BUFFERS); |
494 | |
|
495 | 0 | auto filterCiphers = [](const QList<QSslCipher> &ciphers, bool selectTls13) |
496 | 0 | { |
497 | 0 | QByteArray cipherString; |
498 | |
|
499 | 0 | for (const QSslCipher &cipher : ciphers) { |
500 | 0 | const bool isTls13Cipher = cipher.protocol() == QSsl::TlsV1_3 || cipher.protocol() == QSsl::TlsV1_3OrLater; |
501 | 0 | if (selectTls13 != isTls13Cipher) |
502 | 0 | continue; |
503 | | |
504 | 0 | if (cipherString.size()) |
505 | 0 | cipherString.append(':'); |
506 | 0 | cipherString.append(cipher.name().toLatin1()); |
507 | 0 | } |
508 | 0 | return cipherString; |
509 | 0 | }; |
510 | | |
511 | | // Initialize ciphers |
512 | 0 | QList<QSslCipher> ciphers = sslContext->sslConfiguration.ciphers(); |
513 | 0 | if (ciphers.isEmpty()) |
514 | 0 | ciphers = isDtls ? QTlsBackend::defaultDtlsCiphers() : QTlsBackend::defaultCiphers(); |
515 | |
|
516 | 0 | const QByteArray preTls13Ciphers = filterCiphers(ciphers, false); |
517 | |
|
518 | 0 | if (preTls13Ciphers.size()) { |
519 | 0 | if (!q_SSL_CTX_set_cipher_list(sslContext->ctx, preTls13Ciphers.data())) { |
520 | 0 | sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()); |
521 | 0 | sslContext->errorCode = QSslError::UnspecifiedError; |
522 | 0 | return; |
523 | 0 | } |
524 | 0 | } |
525 | | |
526 | 0 | const QByteArray tls13Ciphers = filterCiphers(ciphers, true); |
527 | 0 | #ifdef TLS1_3_VERSION |
528 | 0 | if (tls13Ciphers.size()) { |
529 | 0 | if (!q_SSL_CTX_set_ciphersuites(sslContext->ctx, tls13Ciphers.data())) { |
530 | 0 | sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()); |
531 | 0 | sslContext->errorCode = QSslError::UnspecifiedError; |
532 | 0 | return; |
533 | 0 | } |
534 | 0 | } |
535 | 0 | #endif // TLS1_3_VERSION |
536 | 0 | if (!preTls13Ciphers.size() && !tls13Ciphers.size()) { |
537 | 0 | sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QStringLiteral("")); |
538 | 0 | sslContext->errorCode = QSslError::UnspecifiedError; |
539 | 0 | return; |
540 | 0 | } |
541 | | |
542 | 0 | const QDateTime now = QDateTime::currentDateTimeUtc(); |
543 | | |
544 | | // Add all our CAs to this store. |
545 | 0 | const auto caCertificates = sslContext->sslConfiguration.caCertificates(); |
546 | 0 | for (const QSslCertificate &caCertificate : caCertificates) { |
547 | | // From https://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html: |
548 | | // |
549 | | // If several CA certificates matching the name, key identifier, and |
550 | | // serial number condition are available, only the first one will be |
551 | | // examined. This may lead to unexpected results if the same CA |
552 | | // certificate is available with different expiration dates. If a |
553 | | // ``certificate expired'' verification error occurs, no other |
554 | | // certificate will be searched. Make sure to not have expired |
555 | | // certificates mixed with valid ones. |
556 | | // |
557 | | // See also: QSslSocketBackendPrivate::verify() |
558 | 0 | if (caCertificate.expiryDate() >= now) { |
559 | 0 | q_X509_STORE_add_cert(q_SSL_CTX_get_cert_store(sslContext->ctx), (X509 *)caCertificate.handle()); |
560 | 0 | } |
561 | 0 | } |
562 | |
|
563 | 0 | if (QSslSocketPrivate::rootCertOnDemandLoadingSupported() && allowRootCertOnDemandLoading) { |
564 | | // tell OpenSSL the directories where to look up the root certs on demand |
565 | 0 | const QList<QByteArray> unixDirs = QSslSocketPrivate::unixRootCertDirectories(); |
566 | 0 | int success = 1; |
567 | 0 | #if OPENSSL_VERSION_MAJOR < 3 |
568 | 0 | for (const QByteArray &unixDir : unixDirs) { |
569 | 0 | if ((success = q_SSL_CTX_load_verify_locations(sslContext->ctx, nullptr, unixDir.constData())) != 1) |
570 | 0 | break; |
571 | 0 | } |
572 | | #else |
573 | | for (const QByteArray &unixDir : unixDirs) { |
574 | | if ((success = q_SSL_CTX_load_verify_dir(sslContext->ctx, unixDir.constData())) != 1) |
575 | | break; |
576 | | } |
577 | | #endif // OPENSSL_VERSION_MAJOR |
578 | 0 | if (success != 1) { |
579 | 0 | const auto qtErrors = QTlsBackendOpenSSL::getErrorsFromOpenSsl(); |
580 | 0 | qCWarning(lcTlsBackend) << "An error encountered while to set root certificates location:" |
581 | 0 | << qtErrors; |
582 | 0 | } |
583 | 0 | } |
584 | |
|
585 | 0 | if (!sslContext->sslConfiguration.localCertificate().isNull()) { |
586 | | // Require a private key as well. |
587 | 0 | if (sslContext->sslConfiguration.privateKey().isNull()) { |
588 | 0 | sslContext->errorStr = QSslSocket::tr("Cannot provide a certificate with no key"); |
589 | 0 | sslContext->errorCode = QSslError::UnspecifiedError; |
590 | 0 | return; |
591 | 0 | } |
592 | | |
593 | | // Load certificate |
594 | 0 | if (!q_SSL_CTX_use_certificate(sslContext->ctx, (X509 *)sslContext->sslConfiguration.localCertificate().handle())) { |
595 | 0 | sslContext->errorStr = QSslSocket::tr("Error loading local certificate, %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()); |
596 | 0 | sslContext->errorCode = QSslError::UnspecifiedError; |
597 | 0 | return; |
598 | 0 | } |
599 | | |
600 | 0 | const auto algorithm = configuration.d->privateKey.algorithm(); |
601 | 0 | bool useOpaqueHandle = false; |
602 | |
|
603 | 0 | if (algorithm == QSsl::Opaque) |
604 | 0 | useOpaqueHandle = true; |
605 | 0 | #if OPENSSL_VERSION_NUMBER < 0x3050000fL |
606 | | // ML-DSA is only supported in OpenSSL 3.5+, therefore treat it as Qssl::Opaque so it still |
607 | | // works and loads correctly. |
608 | 0 | if (algorithm == QSsl::MlDsa) |
609 | 0 | useOpaqueHandle = true; |
610 | 0 | #endif |
611 | |
|
612 | 0 | if (useOpaqueHandle) { |
613 | 0 | sslContext->pkey = reinterpret_cast<EVP_PKEY *>(configuration.d->privateKey.handle()); |
614 | | #if OPENSSL_VERSION_NUMBER >= 0x3050000fL |
615 | | } else if (algorithm == QSsl::MlDsa) { |
616 | | setGenericPrivateKey(sslContext, configuration); |
617 | | #endif |
618 | 0 | } else { |
619 | | #ifdef OPENSSL_NO_DEPRECATED_3_0 |
620 | | setGenericPrivateKey(sslContext, configuration); |
621 | | #else |
622 | | // Load private key |
623 | 0 | sslContext->pkey = q_EVP_PKEY_new(); |
624 | | // before we were using EVP_PKEY_assign_R* functions and did not use EVP_PKEY_free. |
625 | | // this lead to a memory leak. Now we use the *_set1_* functions which do not |
626 | | // take ownership of the RSA/DSA key instance because the QSslKey already has ownership. |
627 | 0 | if (algorithm == QSsl::Rsa) |
628 | 0 | q_EVP_PKEY_set1_RSA(sslContext->pkey, reinterpret_cast<RSA *>(configuration.d->privateKey.handle())); |
629 | 0 | else if (algorithm == QSsl::Dsa) |
630 | 0 | q_EVP_PKEY_set1_DSA(sslContext->pkey, reinterpret_cast<DSA *>(configuration.d->privateKey.handle())); |
631 | 0 | #ifndef OPENSSL_NO_EC |
632 | 0 | else if (algorithm == QSsl::Ec) |
633 | 0 | q_EVP_PKEY_set1_EC_KEY(sslContext->pkey, reinterpret_cast<EC_KEY *>(configuration.d->privateKey.handle())); |
634 | 0 | #endif // OPENSSL_NO_EC |
635 | 0 | #endif // OPENSSL_NO_DEPRECATED_3_0 |
636 | 0 | } |
637 | 0 | auto pkey = sslContext->pkey; |
638 | 0 | if (useOpaqueHandle) |
639 | 0 | sslContext->pkey = nullptr; // Don't free the private key, it belongs to QSslKey |
640 | |
|
641 | 0 | if (!q_SSL_CTX_use_PrivateKey(sslContext->ctx, pkey)) { |
642 | 0 | sslContext->errorStr = QSslSocket::tr("Error loading private key, %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()); |
643 | 0 | sslContext->errorCode = QSslError::UnspecifiedError; |
644 | 0 | return; |
645 | 0 | } |
646 | | |
647 | | // Check if the certificate matches the private key. |
648 | 0 | if (!q_SSL_CTX_check_private_key(sslContext->ctx)) { |
649 | 0 | sslContext->errorStr = QSslSocket::tr("Private key does not certify public key, %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl()); |
650 | 0 | sslContext->errorCode = QSslError::UnspecifiedError; |
651 | 0 | return; |
652 | 0 | } |
653 | | |
654 | | // If we have any intermediate certificates then we need to add them to our chain |
655 | 0 | bool first = true; |
656 | 0 | for (const QSslCertificate &cert : std::as_const(configuration.d->localCertificateChain)) { |
657 | 0 | if (first) { |
658 | 0 | first = false; |
659 | 0 | continue; |
660 | 0 | } |
661 | 0 | q_SSL_CTX_ctrl(sslContext->ctx, SSL_CTRL_EXTRA_CHAIN_CERT, 0, |
662 | 0 | q_X509_dup(reinterpret_cast<X509 *>(cert.handle()))); |
663 | 0 | } |
664 | 0 | } |
665 | | |
666 | | // Initialize peer verification, different callbacks, TLS/DTLS verification first |
667 | | // (note, all these set_some_callback do not have return value): |
668 | 0 | if (sslContext->sslConfiguration.peerVerifyMode() == QSslSocket::VerifyNone) { |
669 | 0 | q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_NONE, nullptr); |
670 | 0 | } else { |
671 | 0 | auto verificationCallback = |
672 | 0 | #if QT_CONFIG(dtls) |
673 | 0 | isDtls ? dtlscallbacks::q_X509DtlsCallback : |
674 | 0 | #endif // dtls |
675 | 0 | QTlsPrivate::q_X509Callback; |
676 | |
|
677 | 0 | if (!isDtls && configuration.handshakeMustInterruptOnError()) |
678 | 0 | verificationCallback = QTlsPrivate::q_X509CallbackDirect; |
679 | |
|
680 | 0 | auto verificationMode = SSL_VERIFY_PEER; |
681 | 0 | if (!isDtls && sslContext->sslConfiguration.missingCertificateIsFatal()) |
682 | 0 | verificationMode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; |
683 | |
|
684 | 0 | q_SSL_CTX_set_verify(sslContext->ctx, verificationMode, verificationCallback); |
685 | 0 | } |
686 | |
|
687 | 0 | #ifdef TLS1_3_VERSION |
688 | | // NewSessionTicket callback: |
689 | 0 | if (mode == QSslSocket::SslClientMode && !isDtls) { |
690 | 0 | q_SSL_CTX_sess_set_new_cb(sslContext->ctx, QTlsPrivate::q_ssl_sess_set_new_cb); |
691 | 0 | q_SSL_CTX_set_session_cache_mode(sslContext->ctx, SSL_SESS_CACHE_CLIENT); |
692 | 0 | } |
693 | |
|
694 | 0 | #endif // TLS1_3_VERSION |
695 | |
|
696 | 0 | #if QT_CONFIG(dtls) |
697 | | // DTLS cookies: |
698 | 0 | if (mode == QSslSocket::SslServerMode && isDtls && configuration.dtlsCookieVerificationEnabled()) { |
699 | 0 | q_SSL_CTX_set_cookie_generate_cb(sslContext->ctx, dtlscallbacks::q_generate_cookie_callback); |
700 | 0 | q_SSL_CTX_set_cookie_verify_cb(sslContext->ctx, dtlscallbacks::q_verify_cookie_callback); |
701 | 0 | } |
702 | 0 | #endif // dtls |
703 | | |
704 | | // Set verification depth. |
705 | 0 | if (sslContext->sslConfiguration.peerVerifyDepth() != 0) |
706 | 0 | q_SSL_CTX_set_verify_depth(sslContext->ctx, sslContext->sslConfiguration.peerVerifyDepth()); |
707 | | |
708 | | // set persisted session if the user set it |
709 | 0 | if (!configuration.sessionTicket().isEmpty()) |
710 | 0 | sslContext->setSessionASN1(configuration.sessionTicket()); |
711 | | |
712 | | // Set temp DH params |
713 | 0 | QSslDiffieHellmanParameters dhparams = configuration.diffieHellmanParameters(); |
714 | |
|
715 | 0 | if (!dhparams.isValid()) { |
716 | 0 | sslContext->errorStr = QSslSocket::tr("Diffie-Hellman parameters are not valid"); |
717 | 0 | sslContext->errorCode = QSslError::UnspecifiedError; |
718 | 0 | return; |
719 | 0 | } |
720 | | |
721 | 0 | if (dhparams.isEmpty()) { |
722 | 0 | q_SSL_CTX_set_dh_auto(sslContext->ctx, 1); |
723 | 0 | } else { |
724 | 0 | #ifndef OPENSSL_NO_DEPRECATED_3_0 |
725 | 0 | const QByteArray ¶ms = dhparams.d->derData; |
726 | 0 | const char *ptr = params.constData(); |
727 | 0 | DH *dh = q_d2i_DHparams(nullptr, reinterpret_cast<const unsigned char **>(&ptr), |
728 | 0 | params.size()); |
729 | 0 | if (dh == nullptr) |
730 | 0 | qFatal("q_d2i_DHparams failed to convert QSslDiffieHellmanParameters to DER form"); |
731 | 0 | q_SSL_CTX_set_tmp_dh(sslContext->ctx, dh); |
732 | 0 | q_DH_free(dh); |
733 | | #else |
734 | | qCWarning(lcTlsBackend, "Diffie-Hellman parameters are not supported, because OpenSSL v3 was built with deprecated API removed"); |
735 | | #endif |
736 | 0 | } |
737 | |
|
738 | 0 | #ifndef OPENSSL_NO_PSK |
739 | 0 | if (!client) |
740 | 0 | q_SSL_CTX_use_psk_identity_hint(sslContext->ctx, sslContext->sslConfiguration.preSharedKeyIdentityHint().constData()); |
741 | 0 | #endif // !OPENSSL_NO_PSK |
742 | |
|
743 | 0 | const auto qcurves = sslContext->sslConfiguration.ellipticCurves(); |
744 | 0 | if (!qcurves.isEmpty()) { |
745 | | #ifdef OPENSSL_NO_EC |
746 | | sslContext->errorStr = msgErrorSettingEllipticCurves(QSslSocket::tr("OpenSSL version with disabled elliptic curves")); |
747 | | sslContext->errorCode = QSslError::UnspecifiedError; |
748 | | return; |
749 | | #else |
750 | | // Set the curves to be used. |
751 | 0 | std::vector<int> curves; |
752 | 0 | curves.reserve(qcurves.size()); |
753 | 0 | for (const auto &sslCurve : qcurves) |
754 | 0 | curves.push_back(sslCurve.id); |
755 | 0 | if (!q_SSL_CTX_ctrl(sslContext->ctx, SSL_CTRL_SET_CURVES, long(curves.size()), &curves[0])) { |
756 | 0 | sslContext->errorStr = msgErrorSettingEllipticCurves(QTlsBackendOpenSSL::getErrorsFromOpenSsl()); |
757 | 0 | sslContext->errorCode = QSslError::UnspecifiedError; |
758 | 0 | return; |
759 | 0 | } |
760 | 0 | #endif |
761 | 0 | } |
762 | | |
763 | 0 | applyBackendConfig(sslContext); |
764 | 0 | } |
765 | | |
766 | | void QSslContext::applyBackendConfig(QSslContext *sslContext) |
767 | 0 | { |
768 | 0 | const QMap<QByteArray, QVariant> &conf = sslContext->sslConfiguration.backendConfiguration(); |
769 | 0 | if (conf.isEmpty()) |
770 | 0 | return; |
771 | | |
772 | 0 | #if QT_CONFIG(ocsp) |
773 | 0 | auto ocspResponsePos = conf.find("Qt-OCSP-response"); |
774 | 0 | if (ocspResponsePos != conf.end()) { |
775 | | // This is our private, undocumented configuration option, existing only for |
776 | | // the purpose of testing OCSP status responses. We don't even check this |
777 | | // callback was set. If no - the test must fail. |
778 | 0 | q_SSL_CTX_set_tlsext_status_cb(sslContext->ctx, QTlsPrivate::qt_OCSP_status_server_callback); |
779 | 0 | if (conf.size() == 1) |
780 | 0 | return; |
781 | 0 | } |
782 | 0 | #endif // ocsp |
783 | | |
784 | 0 | QSharedPointer<SSL_CONF_CTX> cctx(q_SSL_CONF_CTX_new(), &q_SSL_CONF_CTX_free); |
785 | 0 | if (cctx) { |
786 | 0 | q_SSL_CONF_CTX_set_ssl_ctx(cctx.data(), sslContext->ctx); |
787 | 0 | q_SSL_CONF_CTX_set_flags(cctx.data(), SSL_CONF_FLAG_FILE); |
788 | |
|
789 | 0 | for (auto i = conf.constBegin(); i != conf.constEnd(); ++i) { |
790 | 0 | if (i.key() == "Qt-OCSP-response") // This never goes to SSL_CONF_cmd(). |
791 | 0 | continue; |
792 | | |
793 | 0 | if (!i.value().canConvert(QMetaType(QMetaType::QByteArray))) { |
794 | 0 | sslContext->errorCode = QSslError::UnspecifiedError; |
795 | 0 | sslContext->errorStr = msgErrorSettingBackendConfig( |
796 | 0 | QSslSocket::tr("Expecting QByteArray for %1").arg(i.key())); |
797 | 0 | return; |
798 | 0 | } |
799 | | |
800 | 0 | const QByteArray &value = i.value().toByteArray(); |
801 | 0 | const int result = q_SSL_CONF_cmd(cctx.data(), i.key().constData(), value.constData()); |
802 | 0 | if (result == 2) |
803 | 0 | continue; |
804 | | |
805 | 0 | sslContext->errorCode = QSslError::UnspecifiedError; |
806 | 0 | switch (result) { |
807 | 0 | case 0: |
808 | 0 | sslContext->errorStr = msgErrorSettingBackendConfig( |
809 | 0 | QSslSocket::tr("An error occurred attempting to set %1 to %2") |
810 | 0 | .arg(i.key(), value)); |
811 | 0 | return; |
812 | 0 | case 1: |
813 | 0 | sslContext->errorStr = msgErrorSettingBackendConfig( |
814 | 0 | QSslSocket::tr("Wrong value for %1 (%2)").arg(i.key(), value)); |
815 | 0 | return; |
816 | 0 | default: |
817 | 0 | sslContext->errorStr = msgErrorSettingBackendConfig( |
818 | 0 | QSslSocket::tr("Unrecognized command %1 = %2").arg(i.key(), value)); |
819 | 0 | return; |
820 | 0 | } |
821 | 0 | } |
822 | | |
823 | 0 | if (q_SSL_CONF_CTX_finish(cctx.data()) == 0) { |
824 | 0 | sslContext->errorStr = msgErrorSettingBackendConfig(QSslSocket::tr("SSL_CONF_finish() failed")); |
825 | 0 | sslContext->errorCode = QSslError::UnspecifiedError; |
826 | 0 | } |
827 | 0 | } else { |
828 | 0 | sslContext->errorStr = msgErrorSettingBackendConfig(QSslSocket::tr("SSL_CONF_CTX_new() failed")); |
829 | 0 | sslContext->errorCode = QSslError::UnspecifiedError; |
830 | 0 | } |
831 | 0 | } |
832 | | |
833 | | QT_END_NAMESPACE |