Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qsslcontext_openssl.cpp
Go to the documentation of this file.
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
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
25
26Q_GLOBAL_STATIC(bool, forceSecurityLevel)
27
28namespace QTlsPrivate
29{
30// These callback functions are defined in qtls_openssl.cpp.
31extern "C" int q_X509Callback(int ok, X509_STORE_CTX *ctx);
32extern "C" int q_X509CallbackDirect(int ok, X509_STORE_CTX *ctx);
33
34#if QT_CONFIG(ocsp)
35extern "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:
42namespace dtlscallbacks
43{
44extern "C" int q_X509DtlsCallback(int ok, X509_STORE_CTX *ctx);
45extern "C" int q_generate_cookie_callback(SSL *ssl, unsigned char *dst,
46 unsigned *cookieLength);
47extern "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
53extern "C" int q_ssl_sess_set_new_cb(SSL *context, SSL_SESSION *session);
54#endif // TLS1_3_VERSION
55
57{
58 return QSslSocket::tr("Error when setting the OpenSSL configuration (%1)").arg(why);
59}
60
62{
63 return QSslSocket::tr("Error when setting the elliptic curves (%1)").arg(why);
64}
65
67{
68 qssloptions options;
69 switch (protocol) {
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;
81 options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
82 break;
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
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
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
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
114 options |= SSL_OP_CIPHER_SERVER_PREFERENCE;
115
116 return options;
117}
118
120 : ctx(nullptr),
121 pkey(nullptr),
122 session(nullptr),
123 m_sessionTicketLifeTimeHint(-1)
124{
125}
126
128{
129 if (ctx)
130 // This will decrement the reference count by 1 and free the context eventually when possible
132
133 if (pkey)
134 q_EVP_PKEY_free(pkey);
135
136 if (session)
137 q_SSL_SESSION_free(session);
138}
139
140std::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.get(), mode, configuration, allowRootCertOnDemandLoading);
145 return sslContext;
146}
147
149 bool allowRootCertOnDemandLoading)
150{
151 return sharedFromConfiguration(mode, privConfiguration, allowRootCertOnDemandLoading);
152}
153
154#ifndef OPENSSL_NO_NEXTPROTONEG
155
156static int next_proto_cb(SSL *, unsigned char **out, unsigned char *outlen,
157 const unsigned char *in, unsigned int inlen, void *arg)
158{
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, ctx->data, ctx->len);
170 switch (proto) {
171 case OPENSSL_NPN_UNSUPPORTED:
173 break;
174 case OPENSSL_NPN_NEGOTIATED:
176 break;
177 case OPENSSL_NPN_NO_OVERLAP:
179 break;
180 default:
181 qCWarning(lcTlsBackend, "OpenSSL sent unknown NPN status");
182 }
183
184 return SSL_TLSEXT_ERR_OK;
185}
186
188{
189 return m_npnContext;
190}
191#endif // !OPENSSL_NO_NEXTPROTONEG
192
193
194
195// Needs to be deleted by caller
197{
198 SSL* ssl = q_SSL_new(ctx);
199 q_SSL_clear(ssl);
200
201 if (!session && !sessionASN1().isEmpty()
203 const unsigned char *data = reinterpret_cast<const unsigned char *>(m_sessionASN1.constData());
204 session = q_d2i_SSL_SESSION(nullptr, &data, 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(ssl, session)) {
211 qCWarning(lcTlsBackend, "could not set SSL session");
212 q_SSL_SESSION_free(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(a).size() > 255) {
223 qCWarning(lcTlsBackend) << "TLS NPN extension" << protocols.at(a)
224 << "is too long and will be ignored.";
225 continue;
226 } else if (protocols.at(a).isEmpty()) {
227 continue;
228 }
229 m_supportedNPNVersions.append(protocols.at(a).size()).append(protocols.at(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();
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, alpn_callback_t(next_proto_cb), &m_npnContext);
244 // Client:
245 q_SSL_set_alpn_protos(ssl, m_npnContext.data, m_npnContext.len);
246 // And in case our peer does not support ALPN, but supports NPN:
248 }
249 }
250#endif // !OPENSSL_NO_NEXTPROTONEG
251
252 return ssl;
253}
254
255// We cache exactly one session here
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(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(QSsl::SslOptionDisableSessionPersistence)) {
271 int sessionSize = q_i2d_SSL_SESSION(session, nullptr);
272 if (sessionSize > 0) {
273 m_sessionASN1.resize(sessionSize);
274 unsigned char *data = reinterpret_cast<unsigned char *>(m_sessionASN1.data());
275 if (!q_i2d_SSL_SESSION(session, &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
285{
286 return m_sessionASN1;
287}
288
290{
291 m_sessionASN1 = session;
292}
293
295{
296 return m_sessionTicketLifeTimeHint;
297}
298
300{
301 *forceSecurityLevel() = true;
302}
303
305{
306 return errorCode;
307}
308
310{
311 return errorStr;
312}
313
314void 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;
326init_context:
327 switch (sslContext->sslConfiguration.protocol()) {
330 case QSsl::DtlsV1_0:
331 case QSsl::DtlsV1_0OrLater:
333 case QSsl::DtlsV1_2:
335#if QT_CONFIG(dtls)
336 isDtls = true;
337 sslContext->ctx = q_SSL_CTX_new(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:
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(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(0, nullptr) == 1)
363 goto init_context;
364 }
365
366 sslContext->errorStr = QSslSocket::tr("Error creating SSL context (%1)").arg(
367 unsupportedProtocol ? QSslSocket::tr("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(sslContext->ctx) > 1 && *forceSecurityLevel())
375 q_SSL_CTX_set_security_level(sslContext->ctx, 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()) {
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;
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:
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;
427 minVersion = TLS1_2_VERSION;
428 maxVersion = 0;
429 break;
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;
441 case QSsl::DtlsV1_2:
442 minVersion = DTLS1_2_VERSION;
443 maxVersion = DTLS1_2_VERSION;
444 break;
446 minVersion = DTLS1_2_VERSION;
447 maxVersion = 0;
448 break;
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
461 break;
462 }
463
464 if (minVersion != anyVersion
465 && !q_SSL_CTX_set_min_proto_version(sslContext->ctx, minVersion)) {
466 sslContext->errorStr = QSslSocket::tr("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("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(configuration.protocol(), configuration.d->sslOptions);
480 q_SSL_CTX_set_options(sslContext->ctx, 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(':');
497 cipherString.append(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(sslContext->ctx, preTls13Ciphers.data())) {
511 sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(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(sslContext->ctx, tls13Ciphers.data())) {
521 sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(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("Invalid or empty cipher list (%1)").arg(QStringLiteral(""));
529 sslContext->errorCode = QSslError::UnspecifiedError;
530 return;
531 }
532
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(q_SSL_CTX_get_cert_store(sslContext->ctx), (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(sslContext->ctx, 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("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(sslContext->ctx, (X509 *)sslContext->sslConfiguration.localCertificate().handle())) {
586 sslContext->errorStr = QSslSocket::tr("Error loading local certificate, %1").arg(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);
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(sslContext->pkey, reinterpret_cast<RSA *>(configuration.d->privateKey.handle()));
608 else if (configuration.d->privateKey.algorithm() == QSsl::Dsa)
609 q_EVP_PKEY_set1_DSA(sslContext->pkey, 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(sslContext->pkey, 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(sslContext->ctx, pkey)) {
621 sslContext->errorStr = QSslSocket::tr("Error loading private key, %1").arg(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(sslContext->ctx)) {
628 sslContext->errorStr = QSslSocket::tr("Private key does not certify public key, %1").arg(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(configuration.d->localCertificateChain)) {
636 if (first) {
637 first = false;
638 continue;
639 }
640 q_SSL_CTX_ctrl(sslContext->ctx, SSL_CTRL_EXTRA_CHAIN_CERT, 0,
641 q_X509_dup(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(sslContext->ctx, SSL_VERIFY_NONE, nullptr);
649 } else {
650 auto verificationCallback =
651 #if QT_CONFIG(dtls)
653 #endif // dtls
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(sslContext->ctx, verificationMode, 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(sslContext->ctx, 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(sslContext->ctx, dtlscallbacks::q_generate_cookie_callback);
679 q_SSL_CTX_set_cookie_verify_cb(sslContext->ctx, 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(sslContext->ctx, 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("Diffie-Hellman parameters are not valid");
696 sslContext->errorCode = QSslError::UnspecifiedError;
697 return;
698 }
699
700 if (dhparams.isEmpty()) {
701 q_SSL_CTX_set_dh_auto(sslContext->ctx, 1);
702 } else {
703#ifndef OPENSSL_NO_DEPRECATED_3_0
704 const QByteArray &params = dhparams.d->derData;
705 const char *ptr = params.constData();
706 DH *dh = q_d2i_DHparams(nullptr, reinterpret_cast<const unsigned char **>(&ptr),
707 params.size());
708 if (dh == nullptr)
709 qFatal("q_d2i_DHparams failed to convert QSslDiffieHellmanParameters to DER form");
710 q_SSL_CTX_set_tmp_dh(sslContext->ctx, dh);
711 q_DH_free(dh);
712#else
713 qCWarning(lcTlsBackend, "Diffie-Hellman parameters are not supported, because OpenSSL v3 was built with deprecated API removed");
714#endif
715 }
716
717#ifndef OPENSSL_NO_PSK
718 if (!client)
719 q_SSL_CTX_use_psk_identity_hint(sslContext->ctx, sslContext->sslConfiguration.preSharedKeyIdentityHint().constData());
720#endif // !OPENSSL_NO_PSK
721
722 const auto qcurves = sslContext->sslConfiguration.ellipticCurves();
723 if (!qcurves.isEmpty()) {
724#ifdef OPENSSL_NO_EC
725 sslContext->errorStr = msgErrorSettingEllipticCurves(QSslSocket::tr("OpenSSL version with disabled elliptic curves"));
726 sslContext->errorCode = QSslError::UnspecifiedError;
727 return;
728#else
729 // Set the curves to be used.
730 std::vector<int> curves;
731 curves.reserve(qcurves.size());
732 for (const auto &sslCurve : qcurves)
733 curves.push_back(sslCurve.id);
734 if (!q_SSL_CTX_ctrl(sslContext->ctx, SSL_CTRL_SET_CURVES, long(curves.size()), &curves[0])) {
736 sslContext->errorCode = QSslError::UnspecifiedError;
737 return;
738 }
739#endif
740 }
741
742 applyBackendConfig(sslContext);
743}
744
745void QSslContext::applyBackendConfig(QSslContext *sslContext)
746{
747 const QMap<QByteArray, QVariant> &conf = sslContext->sslConfiguration.backendConfiguration();
748 if (conf.isEmpty())
749 return;
750
751#if QT_CONFIG(ocsp)
752 auto ocspResponsePos = conf.find("Qt-OCSP-response");
753 if (ocspResponsePos != conf.end()) {
754 // This is our private, undocumented configuration option, existing only for
755 // the purpose of testing OCSP status responses. We don't even check this
756 // callback was set. If no - the test must fail.
757 q_SSL_CTX_set_tlsext_status_cb(sslContext->ctx, QTlsPrivate::qt_OCSP_status_server_callback);
758 if (conf.size() == 1)
759 return;
760 }
761#endif // ocsp
762
763 QSharedPointer<SSL_CONF_CTX> cctx(q_SSL_CONF_CTX_new(), &q_SSL_CONF_CTX_free);
764 if (cctx) {
765 q_SSL_CONF_CTX_set_ssl_ctx(cctx.data(), sslContext->ctx);
766 q_SSL_CONF_CTX_set_flags(cctx.data(), SSL_CONF_FLAG_FILE);
767
768 for (auto i = conf.constBegin(); i != conf.constEnd(); ++i) {
769 if (i.key() == "Qt-OCSP-response") // This never goes to SSL_CONF_cmd().
770 continue;
771
772 if (!i.value().canConvert(QMetaType(QMetaType::QByteArray))) {
773 sslContext->errorCode = QSslError::UnspecifiedError;
774 sslContext->errorStr = msgErrorSettingBackendConfig(
775 QSslSocket::tr("Expecting QByteArray for %1").arg(
776 QString::fromUtf8(i.key())));
777 return;
778 }
779
780 const QByteArray &value = i.value().toByteArray();
781 const int result = q_SSL_CONF_cmd(cctx.data(), i.key().constData(), value.constData());
782 if (result == 2)
783 continue;
784
785 sslContext->errorCode = QSslError::UnspecifiedError;
786 switch (result) {
787 case 0:
788 sslContext->errorStr = msgErrorSettingBackendConfig(
789 QSslSocket::tr("An error occurred attempting to set %1 to %2").arg(
791 return;
792 case 1:
793 sslContext->errorStr = msgErrorSettingBackendConfig(
794 QSslSocket::tr("Wrong value for %1 (%2)").arg(
796 return;
797 default:
798 sslContext->errorStr = msgErrorSettingBackendConfig(
799 QSslSocket::tr("Unrecognized command %1 = %2").arg(
801 return;
802 }
803 }
804
805 if (q_SSL_CONF_CTX_finish(cctx.data()) == 0) {
806 sslContext->errorStr = msgErrorSettingBackendConfig(QSslSocket::tr("SSL_CONF_finish() failed"));
807 sslContext->errorCode = QSslError::UnspecifiedError;
808 }
809 } else {
810 sslContext->errorStr = msgErrorSettingBackendConfig(QSslSocket::tr("SSL_CONF_CTX_new() failed"));
811 sslContext->errorCode = QSslError::UnspecifiedError;
812 }
813}
814
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:611
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
void clear()
Clears the contents of the byte array and makes it null.
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
QByteArray & append(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
\inmodule QtCore\reentrant
Definition qdatetime.h:283
static QDateTime currentDateTimeUtc()
iterator find(const Key &key)
Definition qmap.h:641
\inmodule QtCore
Definition qmetatype.h:341
const T * constData() const noexcept
Returns a const pointer to the shared data object.
Definition qshareddata.h:51
The QSslCertificate class provides a convenient API for an X509 certificate.
Qt::HANDLE handle() const
Returns a pointer to the native certificate handle, if there is one, else \nullptr.
bool isNull() const
Returns true if this is a null certificate (i.e., a certificate with no contents); otherwise returns ...
The QSslCipher class represents an SSL cryptographic cipher.
Definition qsslcipher.h:22
QList< QByteArray > nextAllowedProtocols
The QSslConfiguration class holds the configuration and state of an SSL connection.
QSslCertificate localCertificate() const
Returns the certificate to be presented to the peer during the SSL handshake process.
bool testSslOption(QSsl::SslOption option) const
QList< QSslCertificate > caCertificates() const
Returns this connection's CA certificate database.
QSslSocket::PeerVerifyMode peerVerifyMode() const
Returns the verify mode.
QSsl::SslProtocol protocol() const
Returns the protocol setting for this SSL configuration.
QSslDiffieHellmanParameters diffieHellmanParameters() const
QByteArray preSharedKeyIdentityHint() const
int peerVerifyDepth() const
Returns the maximum number of certificates in the peer's certificate chain to be checked during the S...
QSslKey privateKey() const
Returns the \l {QSslKey} {SSL key} assigned to this connection or a null key if none has been assigne...
QList< QSslEllipticCurve > ellipticCurves() const
QList< QSslCipher > ciphers() const
Returns this connection's current cryptographic cipher suite.
QByteArray sessionTicket() const
bool handshakeMustInterruptOnError() const
bool missingCertificateIsFatal() const
QMap< QByteArray, QVariant > backendConfiguration() const
QByteArray sessionASN1() const
static std::shared_ptr< QSslContext > sharedFromPrivateConfiguration(QSslSocket::SslMode mode, QSslConfigurationPrivate *privConfiguration, bool allowRootCertOnDemandLoading)
int sessionTicketLifeTimeHint() const
NPNContext npnContext() const
QString errorString() const
static void forceAutoTestSecurityLevel()
void setSessionASN1(const QByteArray &sessionASN1)
bool cacheSession(SSL *)
static qssloptions setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions)
QSslError::SslError error() const
static std::shared_ptr< QSslContext > sharedFromConfiguration(QSslSocket::SslMode mode, const QSslConfiguration &configuration, bool allowRootCertOnDemandLoading)
The QSslDiffieHellmanParameters class provides an interface for Diffie-Hellman parameters for servers...
SslError
Describes all recognized errors that can occur during an SSL handshake.
Definition qsslerror.h:24
@ UnspecifiedError
Definition qsslerror.h:62
QSsl::KeyAlgorithm algorithm() const
Returns the key algorithm.
Qt::HANDLE handle() const
Returns a pointer to the native key handle, if there is one, else \nullptr.
bool isNull() const
Returns true if this is a null key; otherwise false.
static QList< QByteArray > unixRootCertDirectories()
static bool rootCertOnDemandLoadingSupported()
SslMode
Describes the connection modes available for QSslSocket.
Definition qsslsocket.h:33
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8870
static QString getErrorsFromOpenSsl()
QTlsBackend is a factory class, providing implementations for the QSsl classes.
EGLContext ctx
@ Rsa
Definition qssl.h:36
@ Ec
Definition qssl.h:38
@ Opaque
Definition qssl.h:35
@ Dsa
Definition qssl.h:37
@ SslOptionDisableSessionPersistence
Definition qssl.h:80
@ SslOptionDisableCompression
Definition qssl.h:76
@ SslOptionDisableSessionTickets
Definition qssl.h:75
@ SslOptionDisableLegacyRenegotiation
Definition qssl.h:78
@ SslOptionDisableServerCipherPreference
Definition qssl.h:81
@ SslOptionDisableEmptyFragments
Definition qssl.h:74
SslProtocol
Describes the protocol of the cipher.
Definition qssl.h:50
@ DtlsV1_2
Definition qssl.h:63
@ TlsV1_2OrLater
Definition qssl.h:59
@ TlsV1_3
Definition qssl.h:66
@ DtlsV1_2OrLater
Definition qssl.h:64
@ TlsV1_3OrLater
Definition qssl.h:67
@ SecureProtocols
Definition qssl.h:55
@ TlsV1_2
Definition qssl.h:53
@ AnyProtocol
Definition qssl.h:54
@ UnknownProtocol
Definition qssl.h:69
Combined button and popup list for selecting options.
Namespace containing onternal types that TLS backends implement.
QList< QSslCipher > defaultCiphers()
int q_X509Callback(int ok, X509_STORE_CTX *ctx)
int q_X509CallbackDirect(int ok, X509_STORE_CTX *ctx)
int q_X509DtlsCallback(int ok, X509_STORE_CTX *ctx)
int q_generate_cookie_callback(SSL *ssl, unsigned char *dst, unsigned *cookieLength)
int q_verify_cookie_callback(SSL *ssl, const unsigned char *cookie, unsigned cookieLength)
static void * context
#define QT_WARNING_POP
#define QT_WARNING_DISABLE_DEPRECATED
#define QT_WARNING_PUSH
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qFatal
Definition qlogging.h:168
#define qCWarning(category,...)
static ControlElement< T > * ptr(QWidget *widget)
GLenum mode
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint id
[7]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLenum dst
GLint first
void ** params
GLuint in
GLuint64EXT * result
[6]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
static QString qtKey(CFStringRef cfkey)
static int next_proto_cb(SSL *, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
static QString msgErrorSettingEllipticCurves(const QString &why)
static QString msgErrorSettingBackendConfig(const QString &why)
SSL_CTX int void * arg
void q_SSL_CTX_free(SSL_CTX *a)
const SSL_METHOD * q_TLS_server_method()
int q_EVP_PKEY_set1_DSA(EVP_PKEY *a, DSA *b)
DH * q_d2i_DHparams(DH **a, const unsigned char **pp, long length)
void q_SSL_CONF_CTX_set_ssl_ctx(SSL_CONF_CTX *a, SSL_CTX *b)
unsigned long qssloptions
int q_SSL_CTX_get_security_level(const SSL_CTX *ctx)
void q_SSL_SESSION_free(SSL_SESSION *ses)
int q_SSL_set_session(SSL *to, SSL_SESSION *session)
long q_SSL_CTX_ctrl(SSL_CTX *a, int b, long c, void *d)
int q_SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, const char *CApath)
int q_EVP_PKEY_set1_RSA(EVP_PKEY *a, RSA *b)
unsigned int q_SSL_CONF_CTX_set_flags(SSL_CONF_CTX *a, unsigned int b)
int q_SSL_select_next_proto(unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, const unsigned char *client, unsigned int client_len)
void q_DH_free(DH *dh)
int q_EVP_PKEY_set1_EC_KEY(EVP_PKEY *a, EC_KEY *b)
X509 * q_X509_dup(X509 *a)
void q_SSL_CONF_CTX_free(SSL_CONF_CTX *a)
int q_SSL_set_alpn_protos(SSL *ssl, const unsigned char *protos, unsigned protos_len)
int q_SSL_clear(SSL *a)
void q_SSL_CTX_set_verify_depth(SSL_CTX *a, int b)
SSL_CTX * q_SSL_CTX_new(const SSL_METHOD *a)
int q_SSL_CTX_use_certificate(SSL_CTX *a, X509 *b)
SSL_SESSION * q_d2i_SSL_SESSION(SSL_SESSION **a, const unsigned char **pp, long length)
EVP_PKEY * q_EVP_PKEY_new()
void q_EVP_PKEY_free(EVP_PKEY *a)
int q_SSL_CONF_cmd(SSL_CONF_CTX *a, const char *b, const char *c)
#define q_SSL_CTX_set_min_proto_version(ctx, version)
X509_STORE * q_SSL_CTX_get_cert_store(const SSL_CTX *a)
int q_SSL_CTX_check_private_key(const SSL_CTX *a)
unsigned long q_SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *session)
int q_SSL_CTX_use_psk_identity_hint(SSL_CTX *ctx, const char *hint)
int q_i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp)
int q_EVP_PKEY_up_ref(EVP_PKEY *a)
SSL_SESSION * q_SSL_get_session(const SSL *ssl)
void q_SSL_CTX_set_next_proto_select_cb(SSL_CTX *s, int(*cb)(SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg), void *arg)
void q_SSL_CTX_set_security_level(SSL_CTX *ctx, int level)
const SSL_METHOD * q_TLS_client_method()
SSL * q_SSL_new(SSL_CTX *a)
int q_SSL_CTX_set_cipher_list(SSL_CTX *a, const char *b)
int q_X509_STORE_add_cert(X509_STORE *ctx, X509 *x)
#define q_SSL_CTX_set_tmp_dh(ctx, dh)
#define q_SSL_CTX_set_dh_auto(ctx, onoff)
SSL_SESSION * q_SSL_get1_session(SSL *ssl)
void q_SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, int(*cb)(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg), void *arg)
int q_SSL_CTX_use_PrivateKey(SSL_CTX *a, EVP_PKEY *b)
void q_SSL_CTX_set_verify(SSL_CTX *a, int b, int(*c)(int, X509_STORE_CTX *))
#define q_SSL_CTX_set_mode(ctx, op)
SSL_CONF_CTX * q_SSL_CONF_CTX_new()
int q_SSL_CONF_CTX_finish(SSL_CONF_CTX *a)
qssloptions q_SSL_CTX_set_options(SSL_CTX *ctx, qssloptions op)
int q_OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings)
#define q_SSL_CTX_set_max_proto_version(ctx, version)
#define QStringLiteral(str)
QTextStream out(stdout)
[7]
QObject::connect nullptr
QList< QSslCertificate > cert
[0]
QSslConfiguration::NextProtocolNegotiationStatus status