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