Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
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// Qt-Security score:critical reason:cryptography
7
8#include <QtNetwork/qsslsocket.h>
9#include <QtNetwork/qssldiffiehellmanparameters.h>
10
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
25QT_BEGIN_NAMESPACE
26
27Q_GLOBAL_STATIC(bool, forceSecurityLevel)
28
29namespace QTlsPrivate
30{
31// These callback functions are defined in qtls_openssl.cpp.
32int q_X509Callback(int ok, X509_STORE_CTX *ctx);
33int q_X509CallbackDirect(int ok, X509_STORE_CTX *ctx);
34
35#if QT_CONFIG(ocsp)
37#endif // ocsp
38
39#ifdef TLS1_3_VERSION
40int 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:
47namespace dtlscallbacks
48{
49int q_X509DtlsCallback(int ok, X509_STORE_CTX *ctx);
50int q_generate_cookie_callback(SSL *ssl, unsigned char *dst, unsigned *cookieLength);
51int q_verify_cookie_callback(SSL *ssl, const unsigned char *cookie, unsigned cookieLength);
52} // namespace dtlscallbacks
53#endif // dtls
54
55static inline QString msgErrorSettingBackendConfig(const QString &why)
56{
57 return QSslSocket::tr("Error when setting the OpenSSL configuration (%1)").arg(why);
58}
59
60static inline QString msgErrorSettingEllipticCurves(const QString &why)
61{
62 return QSslSocket::tr("Error when setting the elliptic curves (%1)").arg(why);
63}
64
65qssloptions QSslContext::setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions)
66{
67 qssloptions options;
68 switch (protocol) {
69QT_WARNING_PUSH
70QT_WARNING_DISABLE_DEPRECATED
71 case QSsl::TlsV1_0OrLater:
72 options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
73 break;
74 case QSsl::TlsV1_1OrLater:
75 options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1;
76 break;
77QT_WARNING_POP
78 case QSsl::SecureProtocols:
79 case QSsl::TlsV1_2OrLater:
80 options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
81 break;
82 case QSsl::TlsV1_3OrLater:
83 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 break;
85 default:
86 options = SSL_OP_ALL;
87 }
88
89 // This option is disabled by default, so we need to be able to clear it
90 if (sslOptions & QSsl::SslOptionDisableEmptyFragments)
91 options |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
92 else
93 options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
94
95#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
96 // This option is disabled by default, so we need to be able to clear it
97 if (sslOptions & QSsl::SslOptionDisableLegacyRenegotiation)
98 options &= ~SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
99 else
100 options |= SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
101#endif
102
103#ifdef SSL_OP_NO_TICKET
104 if (sslOptions & QSsl::SslOptionDisableSessionTickets)
105 options |= SSL_OP_NO_TICKET;
106#endif
107#ifdef SSL_OP_NO_COMPRESSION
108 if (sslOptions & QSsl::SslOptionDisableCompression)
109 options |= SSL_OP_NO_COMPRESSION;
110#endif
111
112 if (!(sslOptions & QSsl::SslOptionDisableServerCipherPreference))
113 options |= SSL_OP_CIPHER_SERVER_PREFERENCE;
114
115 return options;
116}
117
119 : ctx(nullptr),
120 pkey(nullptr),
121 session(nullptr),
122 m_sessionTicketLifeTimeHint(-1)
123{
124}
125
126QSslContext::~QSslContext()
127{
128 if (ctx)
129 // This will decrement the reference count by 1 and free the context eventually when possible
131
132 if (pkey)
134
135 if (session)
137}
138
139std::shared_ptr<QSslContext> QSslContext::sharedFromConfiguration(QSslSocket::SslMode mode, const QSslConfiguration &configuration, bool allowRootCertOnDemandLoading)
140{
141 struct AccessToPrivateCtor : QSslContext {};
142 std::shared_ptr<QSslContext> sslContext = std::make_shared<AccessToPrivateCtor>();
143 initSslContext(sslContext.get(), mode, configuration, allowRootCertOnDemandLoading);
144 return sslContext;
145}
146
147std::shared_ptr<QSslContext> QSslContext::sharedFromPrivateConfiguration(QSslSocket::SslMode mode, QSslConfigurationPrivate *privConfiguration,
148 bool allowRootCertOnDemandLoading)
149{
150 return sharedFromConfiguration(mode, privConfiguration, allowRootCertOnDemandLoading);
151}
152
153#ifndef OPENSSL_NO_NEXTPROTONEG
154
155static int next_proto_cb(SSL *, unsigned char **out, unsigned char *outlen,
156 const unsigned char *in, unsigned int inlen, void *arg)
157{
158 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 int proto = q_SSL_select_next_proto(out, outlen, in, inlen, ctx->data, ctx->len);
169 switch (proto) {
170 case OPENSSL_NPN_UNSUPPORTED:
171 ctx->status = QSslConfiguration::NextProtocolNegotiationNone;
172 break;
173 case OPENSSL_NPN_NEGOTIATED:
174 ctx->status = QSslConfiguration::NextProtocolNegotiationNegotiated;
175 break;
176 case OPENSSL_NPN_NO_OVERLAP:
177 ctx->status = QSslConfiguration::NextProtocolNegotiationUnsupported;
178 break;
179 default:
180 qCWarning(lcTlsBackend, "OpenSSL sent unknown NPN status");
181 }
182
183 return SSL_TLSEXT_ERR_OK;
184}
185
187{
188 return m_npnContext;
189}
190#endif // !OPENSSL_NO_NEXTPROTONEG
191
192
193
194// Needs to be deleted by caller
196{
197 SSL* ssl = q_SSL_new(ctx);
198 q_SSL_clear(ssl);
199
200 if (!session && !sessionASN1().isEmpty()
201 && !sslConfiguration.testSslOption(QSsl::SslOptionDisableSessionPersistence)) {
202 const unsigned char *data = reinterpret_cast<const unsigned char *>(m_sessionASN1.constData());
203 session = q_d2i_SSL_SESSION(nullptr, &data, m_sessionASN1.size());
204 // 'session' has refcount 1 already, set by the function above
205 }
206
207 if (session) {
208 // Try to resume the last session we cached
209 if (!q_SSL_set_session(ssl, session)) {
210 qCWarning(lcTlsBackend, "could not set SSL session");
212 session = nullptr;
213 }
214 }
215
216#ifndef OPENSSL_NO_NEXTPROTONEG
217 QList<QByteArray> protocols = sslConfiguration.d.constData()->nextAllowedProtocols;
218 if (!protocols.isEmpty()) {
219 m_supportedNPNVersions.clear();
220 for (int a = 0; a < protocols.size(); ++a) {
221 if (protocols.at(a).size() > 255) {
222 qCWarning(lcTlsBackend) << "TLS NPN extension" << protocols.at(a)
223 << "is too long and will be ignored.";
224 continue;
225 } else if (protocols.at(a).isEmpty()) {
226 continue;
227 }
228 m_supportedNPNVersions.append(protocols.at(a).size()).append(protocols.at(a));
229 }
230 if (m_supportedNPNVersions.size()) {
231 m_npnContext.data = reinterpret_cast<unsigned char *>(m_supportedNPNVersions.data());
232 m_npnContext.len = m_supportedNPNVersions.size();
233 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 typedef int (*alpn_callback_t) (SSL *, const unsigned char **, unsigned char *,
239 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 q_SSL_CTX_set_alpn_select_cb(ctx, alpn_callback_t(next_proto_cb), &m_npnContext);
243 // Client:
244 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 q_SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &m_npnContext);
247 }
248 }
249#endif // !OPENSSL_NO_NEXTPROTONEG
250
251 return ssl;
252}
253
254// We cache exactly one session here
256{
257 // don't cache the same session again
258 if (session && session == q_SSL_get_session(ssl))
259 return true;
260
261 // decrease refcount of currently stored session
262 // (this might happen if there are several concurrent handshakes in flight)
263 if (session)
265
266 // cache the session the caller gave us and increase reference count
267 session = q_SSL_get1_session(ssl);
268
269 if (session && !sslConfiguration.testSslOption(QSsl::SslOptionDisableSessionPersistence)) {
270 int sessionSize = q_i2d_SSL_SESSION(session, nullptr);
271 if (sessionSize > 0) {
272 m_sessionASN1.resize(sessionSize);
273 unsigned char *data = reinterpret_cast<unsigned char *>(m_sessionASN1.data());
274 if (!q_i2d_SSL_SESSION(session, &data))
275 qCWarning(lcTlsBackend, "could not store persistent version of SSL session");
276 m_sessionTicketLifeTimeHint = q_SSL_SESSION_get_ticket_lifetime_hint(session);
277 }
278 }
279
280 return (session != nullptr);
281}
282
284{
285 return m_sessionASN1;
286}
287
288void QSslContext::setSessionASN1(const QByteArray &session)
289{
290 m_sessionASN1 = session;
291}
292
294{
295 return m_sessionTicketLifeTimeHint;
296}
297
299{
300 *forceSecurityLevel() = true;
301}
302
304{
305 return errorCode;
306}
307
309{
310 return errorStr;
311}
312
313void QSslContext::setGenericPrivateKey(QSslContext *sslContext,
314 const QSslConfiguration &configuration)
315{
316 auto qtKey = QTlsBackend::backend<QTlsPrivate::TlsKeyOpenSSL>(configuration.d->privateKey);
317 Q_ASSERT(qtKey);
318 sslContext->pkey = qtKey->genericKey;
319 Q_ASSERT(sslContext->pkey);
320 q_EVP_PKEY_up_ref(sslContext->pkey);
321}
322
323void QSslContext::initSslContext(QSslContext *sslContext, QSslSocket::SslMode mode,
324 const QSslConfiguration &configuration,
325 bool allowRootCertOnDemandLoading)
326{
327 sslContext->sslConfiguration = configuration;
328 sslContext->errorCode = QSslError::NoError;
329
330 bool client = (mode == QSslSocket::SslClientMode);
331
332 bool reinitialized = false;
333 bool unsupportedProtocol = false;
334 bool isDtls = false;
335init_context:
336 switch (sslContext->sslConfiguration.protocol()) {
337QT_WARNING_PUSH
338QT_WARNING_DISABLE_DEPRECATED
339 case QSsl::DtlsV1_0:
340 case QSsl::DtlsV1_0OrLater:
341QT_WARNING_POP
342 case QSsl::DtlsV1_2:
343 case QSsl::DtlsV1_2OrLater:
344#if QT_CONFIG(dtls)
345 isDtls = true;
346 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 break;
353 case QSsl::TlsV1_3:
354 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 default:
362 // The ssl options will actually control the supported methods
364 }
365
366 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 if (!reinitialized) {
370 reinitialized = true;
371 if (q_OPENSSL_init_ssl(0, nullptr) == 1)
372 goto init_context;
373 }
374
375 sslContext->errorStr = QSslSocket::tr("Error creating SSL context (%1)").arg(
376 unsupportedProtocol ? QSslSocket::tr("unsupported protocol") : QTlsBackendOpenSSL::getErrorsFromOpenSsl()
377 );
378 sslContext->errorCode = QSslError::UnspecifiedError;
379 return;
380 }
381
382 // A nasty hacked OpenSSL using a level that will make our auto-tests fail:
383 if (q_SSL_CTX_get_security_level(sslContext->ctx) > 1 && *forceSecurityLevel())
384 q_SSL_CTX_set_security_level(sslContext->ctx, 1);
385
386 const long anyVersion =
387#if QT_CONFIG(dtls)
388 isDtls ? DTLS_ANY_VERSION : TLS_ANY_VERSION;
389#else
390 TLS_ANY_VERSION;
391#endif // dtls
392 long minVersion = anyVersion;
393 long maxVersion = anyVersion;
394
395 switch (sslContext->sslConfiguration.protocol()) {
396QT_WARNING_PUSH
397QT_WARNING_DISABLE_DEPRECATED
398 case QSsl::TlsV1_0:
399 minVersion = TLS1_VERSION;
400 maxVersion = TLS1_VERSION;
401 break;
402 case QSsl::TlsV1_1:
403 minVersion = TLS1_1_VERSION;
404 maxVersion = TLS1_1_VERSION;
405 break;
406QT_WARNING_POP
407 case QSsl::TlsV1_2:
408 minVersion = TLS1_2_VERSION;
409 maxVersion = TLS1_2_VERSION;
410 break;
411 case QSsl::TlsV1_3:
412#ifdef TLS1_3_VERSION
413 minVersion = TLS1_3_VERSION;
414 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 break;
421 // Ranges:
422 case QSsl::AnyProtocol:
423QT_WARNING_PUSH
424QT_WARNING_DISABLE_DEPRECATED
425 case QSsl::TlsV1_0OrLater:
426 minVersion = TLS1_VERSION;
427 maxVersion = 0;
428 break;
429 case QSsl::TlsV1_1OrLater:
430 minVersion = TLS1_1_VERSION;
431 maxVersion = 0;
432 break;
433QT_WARNING_POP
434 case QSsl::SecureProtocols:
435 case QSsl::TlsV1_2OrLater:
436 minVersion = TLS1_2_VERSION;
437 maxVersion = 0;
438 break;
439QT_WARNING_PUSH
440QT_WARNING_DISABLE_DEPRECATED
441 case QSsl::DtlsV1_0:
442 minVersion = DTLS1_VERSION;
443 maxVersion = DTLS1_VERSION;
444 break;
445 case QSsl::DtlsV1_0OrLater:
446 minVersion = DTLS1_VERSION;
447 maxVersion = 0;
448 break;
449QT_WARNING_POP
450 case QSsl::DtlsV1_2:
451 minVersion = DTLS1_2_VERSION;
452 maxVersion = DTLS1_2_VERSION;
453 break;
454 case QSsl::DtlsV1_2OrLater:
455 minVersion = DTLS1_2_VERSION;
456 maxVersion = 0;
457 break;
458 case QSsl::TlsV1_3OrLater:
459#ifdef TLS1_3_VERSION
460 minVersion = TLS1_3_VERSION;
461 maxVersion = 0;
462 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 case QSsl::UnknownProtocol:
470 break;
471 }
472
473 if (minVersion != anyVersion
474 && !q_SSL_CTX_set_min_proto_version(sslContext->ctx, minVersion)) {
475 sslContext->errorStr = QSslSocket::tr("Error while setting the minimal protocol version");
476 sslContext->errorCode = QSslError::UnspecifiedError;
477 return;
478 }
479
480 if (maxVersion != anyVersion
481 && !q_SSL_CTX_set_max_proto_version(sslContext->ctx, maxVersion)) {
482 sslContext->errorStr = QSslSocket::tr("Error while setting the maximum protocol version");
483 sslContext->errorCode = QSslError::UnspecifiedError;
484 return;
485 }
486
487 // Enable bug workarounds.
488 const qssloptions options = setupOpenSslOptions(configuration.protocol(), configuration.d->sslOptions);
489 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 q_SSL_CTX_set_mode(sslContext->ctx, SSL_MODE_RELEASE_BUFFERS);
494
495 auto filterCiphers = [](const QList<QSslCipher> &ciphers, bool selectTls13)
496 {
497 QByteArray cipherString;
498
499 for (const QSslCipher &cipher : ciphers) {
500 const bool isTls13Cipher = cipher.protocol() == QSsl::TlsV1_3 || cipher.protocol() == QSsl::TlsV1_3OrLater;
501 if (selectTls13 != isTls13Cipher)
502 continue;
503
504 if (cipherString.size())
505 cipherString.append(':');
506 cipherString.append(cipher.name().toLatin1());
507 }
508 return cipherString;
509 };
510
511 // Initialize ciphers
512 QList<QSslCipher> ciphers = sslContext->sslConfiguration.ciphers();
513 if (ciphers.isEmpty())
514 ciphers = isDtls ? QTlsBackend::defaultDtlsCiphers() : QTlsBackend::defaultCiphers();
515
516 const QByteArray preTls13Ciphers = filterCiphers(ciphers, false);
517
518 if (preTls13Ciphers.size()) {
519 if (!q_SSL_CTX_set_cipher_list(sslContext->ctx, preTls13Ciphers.data())) {
520 sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl());
521 sslContext->errorCode = QSslError::UnspecifiedError;
522 return;
523 }
524 }
525
526 const QByteArray tls13Ciphers = filterCiphers(ciphers, true);
527#ifdef TLS1_3_VERSION
528 if (tls13Ciphers.size()) {
529 if (!q_SSL_CTX_set_ciphersuites(sslContext->ctx, tls13Ciphers.data())) {
530 sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl());
531 sslContext->errorCode = QSslError::UnspecifiedError;
532 return;
533 }
534 }
535#endif // TLS1_3_VERSION
536 if (!preTls13Ciphers.size() && !tls13Ciphers.size()) {
537 sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QStringLiteral(""));
538 sslContext->errorCode = QSslError::UnspecifiedError;
539 return;
540 }
541
542 const QDateTime now = QDateTime::currentDateTimeUtc();
543
544 // Add all our CAs to this store.
545 const auto caCertificates = sslContext->sslConfiguration.caCertificates();
546 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 if (caCertificate.expiryDate() >= now) {
559 q_X509_STORE_add_cert(q_SSL_CTX_get_cert_store(sslContext->ctx), (X509 *)caCertificate.handle());
560 }
561 }
562
563 if (QSslSocketPrivate::rootCertOnDemandLoadingSupported() && allowRootCertOnDemandLoading) {
564 // tell OpenSSL the directories where to look up the root certs on demand
565 const QList<QByteArray> unixDirs = QSslSocketPrivate::unixRootCertDirectories();
566 int success = 1;
567#if OPENSSL_VERSION_MAJOR < 3
568 for (const QByteArray &unixDir : unixDirs) {
569 if ((success = q_SSL_CTX_load_verify_locations(sslContext->ctx, nullptr, unixDir.constData())) != 1)
570 break;
571 }
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 if (success != 1) {
579 const auto qtErrors = QTlsBackendOpenSSL::getErrorsFromOpenSsl();
580 qCWarning(lcTlsBackend) << "An error encountered while to set root certificates location:"
581 << qtErrors;
582 }
583 }
584
585 if (!sslContext->sslConfiguration.localCertificate().isNull()) {
586 // Require a private key as well.
587 if (sslContext->sslConfiguration.privateKey().isNull()) {
588 sslContext->errorStr = QSslSocket::tr("Cannot provide a certificate with no key");
589 sslContext->errorCode = QSslError::UnspecifiedError;
590 return;
591 }
592
593 // Load certificate
594 if (!q_SSL_CTX_use_certificate(sslContext->ctx, (X509 *)sslContext->sslConfiguration.localCertificate().handle())) {
595 sslContext->errorStr = QSslSocket::tr("Error loading local certificate, %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl());
596 sslContext->errorCode = QSslError::UnspecifiedError;
597 return;
598 }
599
600 const auto algorithm = configuration.d->privateKey.algorithm();
601 bool useOpaqueHandle = false;
602
603 if (algorithm == QSsl::Opaque)
604 useOpaqueHandle = true;
605#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 if (algorithm == QSsl::MlDsa)
609 useOpaqueHandle = true;
610#endif
611
612 if (useOpaqueHandle) {
613 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 } else {
619#ifdef OPENSSL_NO_DEPRECATED_3_0
620 setGenericPrivateKey(sslContext, configuration);
621#else
622 // Load private key
623 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 if (algorithm == QSsl::Rsa)
628 q_EVP_PKEY_set1_RSA(sslContext->pkey, reinterpret_cast<RSA *>(configuration.d->privateKey.handle()));
629 else if (algorithm == QSsl::Dsa)
630 q_EVP_PKEY_set1_DSA(sslContext->pkey, reinterpret_cast<DSA *>(configuration.d->privateKey.handle()));
631#ifndef OPENSSL_NO_EC
632 else if (algorithm == QSsl::Ec)
633 q_EVP_PKEY_set1_EC_KEY(sslContext->pkey, reinterpret_cast<EC_KEY *>(configuration.d->privateKey.handle()));
634#endif // OPENSSL_NO_EC
635#endif // OPENSSL_NO_DEPRECATED_3_0
636 }
637 auto pkey = sslContext->pkey;
638 if (useOpaqueHandle)
639 sslContext->pkey = nullptr; // Don't free the private key, it belongs to QSslKey
640
641 if (!q_SSL_CTX_use_PrivateKey(sslContext->ctx, pkey)) {
642 sslContext->errorStr = QSslSocket::tr("Error loading private key, %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl());
643 sslContext->errorCode = QSslError::UnspecifiedError;
644 return;
645 }
646
647 // Check if the certificate matches the private key.
648 if (!q_SSL_CTX_check_private_key(sslContext->ctx)) {
649 sslContext->errorStr = QSslSocket::tr("Private key does not certify public key, %1").arg(QTlsBackendOpenSSL::getErrorsFromOpenSsl());
650 sslContext->errorCode = QSslError::UnspecifiedError;
651 return;
652 }
653
654 // If we have any intermediate certificates then we need to add them to our chain
655 bool first = true;
656 for (const QSslCertificate &cert : std::as_const(configuration.d->localCertificateChain)) {
657 if (first) {
658 first = false;
659 continue;
660 }
661 q_SSL_CTX_ctrl(sslContext->ctx, SSL_CTRL_EXTRA_CHAIN_CERT, 0,
662 q_X509_dup(reinterpret_cast<X509 *>(cert.handle())));
663 }
664 }
665
666 // Initialize peer verification, different callbacks, TLS/DTLS verification first
667 // (note, all these set_some_callback do not have return value):
668 if (sslContext->sslConfiguration.peerVerifyMode() == QSslSocket::VerifyNone) {
669 q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_NONE, nullptr);
670 } else {
671 auto verificationCallback =
672 #if QT_CONFIG(dtls)
673 isDtls ? dtlscallbacks::q_X509DtlsCallback :
674 #endif // dtls
676
677 if (!isDtls && configuration.handshakeMustInterruptOnError())
678 verificationCallback = QTlsPrivate::q_X509CallbackDirect;
679
680 auto verificationMode = SSL_VERIFY_PEER;
681 if (!isDtls && sslContext->sslConfiguration.missingCertificateIsFatal())
682 verificationMode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
683
684 q_SSL_CTX_set_verify(sslContext->ctx, verificationMode, verificationCallback);
685 }
686
687#ifdef TLS1_3_VERSION
688 // NewSessionTicket callback:
689 if (mode == QSslSocket::SslClientMode && !isDtls) {
690 q_SSL_CTX_sess_set_new_cb(sslContext->ctx, QTlsPrivate::q_ssl_sess_set_new_cb);
691 q_SSL_CTX_set_session_cache_mode(sslContext->ctx, SSL_SESS_CACHE_CLIENT);
692 }
693
694#endif // TLS1_3_VERSION
695
696#if QT_CONFIG(dtls)
697 // DTLS cookies:
698 if (mode == QSslSocket::SslServerMode && isDtls && configuration.dtlsCookieVerificationEnabled()) {
699 q_SSL_CTX_set_cookie_generate_cb(sslContext->ctx, dtlscallbacks::q_generate_cookie_callback);
700 q_SSL_CTX_set_cookie_verify_cb(sslContext->ctx, dtlscallbacks::q_verify_cookie_callback);
701 }
702#endif // dtls
703
704 // Set verification depth.
705 if (sslContext->sslConfiguration.peerVerifyDepth() != 0)
706 q_SSL_CTX_set_verify_depth(sslContext->ctx, sslContext->sslConfiguration.peerVerifyDepth());
707
708 // set persisted session if the user set it
709 if (!configuration.sessionTicket().isEmpty())
710 sslContext->setSessionASN1(configuration.sessionTicket());
711
712 // Set temp DH params
713 QSslDiffieHellmanParameters dhparams = configuration.diffieHellmanParameters();
714
715 if (!dhparams.isValid()) {
716 sslContext->errorStr = QSslSocket::tr("Diffie-Hellman parameters are not valid");
717 sslContext->errorCode = QSslError::UnspecifiedError;
718 return;
719 }
720
721 if (dhparams.isEmpty()) {
722 q_SSL_CTX_set_dh_auto(sslContext->ctx, 1);
723 } else {
724#ifndef OPENSSL_NO_DEPRECATED_3_0
725 const QByteArray &params = dhparams.d->derData;
726 const char *ptr = params.constData();
727 DH *dh = q_d2i_DHparams(nullptr, reinterpret_cast<const unsigned char **>(&ptr),
728 params.size());
729 if (dh == nullptr)
730 qFatal("q_d2i_DHparams failed to convert QSslDiffieHellmanParameters to DER form");
731 q_SSL_CTX_set_tmp_dh(sslContext->ctx, dh);
732 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 }
737
738#ifndef OPENSSL_NO_PSK
739 if (!client)
740 q_SSL_CTX_use_psk_identity_hint(sslContext->ctx, sslContext->sslConfiguration.preSharedKeyIdentityHint().constData());
741#endif // !OPENSSL_NO_PSK
742
743 const auto qcurves = sslContext->sslConfiguration.ellipticCurves();
744 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 std::vector<int> curves;
752 curves.reserve(qcurves.size());
753 for (const auto &sslCurve : qcurves)
754 curves.push_back(sslCurve.id);
755 if (!q_SSL_CTX_ctrl(sslContext->ctx, SSL_CTRL_SET_CURVES, long(curves.size()), &curves[0])) {
756 sslContext->errorStr = msgErrorSettingEllipticCurves(QTlsBackendOpenSSL::getErrorsFromOpenSsl());
757 sslContext->errorCode = QSslError::UnspecifiedError;
758 return;
759 }
760#endif
761 }
762
763 applyBackendConfig(sslContext);
764}
765
766void QSslContext::applyBackendConfig(QSslContext *sslContext)
767{
768 const QMap<QByteArray, QVariant> &conf = sslContext->sslConfiguration.backendConfiguration();
769 if (conf.isEmpty())
770 return;
771
772#if QT_CONFIG(ocsp)
773 auto ocspResponsePos = conf.find("Qt-OCSP-response");
774 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 q_SSL_CTX_set_tlsext_status_cb(sslContext->ctx, QTlsPrivate::qt_OCSP_status_server_callback);
779 if (conf.size() == 1)
780 return;
781 }
782#endif // ocsp
783
784 QSharedPointer<SSL_CONF_CTX> cctx(q_SSL_CONF_CTX_new(), &q_SSL_CONF_CTX_free);
785 if (cctx) {
786 q_SSL_CONF_CTX_set_ssl_ctx(cctx.data(), sslContext->ctx);
787 q_SSL_CONF_CTX_set_flags(cctx.data(), SSL_CONF_FLAG_FILE);
788
789 for (auto i = conf.constBegin(); i != conf.constEnd(); ++i) {
790 if (i.key() == "Qt-OCSP-response") // This never goes to SSL_CONF_cmd().
791 continue;
792
793 if (!i.value().canConvert(QMetaType(QMetaType::QByteArray))) {
794 sslContext->errorCode = QSslError::UnspecifiedError;
795 sslContext->errorStr = msgErrorSettingBackendConfig(
796 QSslSocket::tr("Expecting QByteArray for %1").arg(i.key()));
797 return;
798 }
799
800 const QByteArray &value = i.value().toByteArray();
801 const int result = q_SSL_CONF_cmd(cctx.data(), i.key().constData(), value.constData());
802 if (result == 2)
803 continue;
804
805 sslContext->errorCode = QSslError::UnspecifiedError;
806 switch (result) {
807 case 0:
808 sslContext->errorStr = msgErrorSettingBackendConfig(
809 QSslSocket::tr("An error occurred attempting to set %1 to %2")
810 .arg(i.key(), value));
811 return;
812 case 1:
813 sslContext->errorStr = msgErrorSettingBackendConfig(
814 QSslSocket::tr("Wrong value for %1 (%2)").arg(i.key(), value));
815 return;
816 default:
817 sslContext->errorStr = msgErrorSettingBackendConfig(
818 QSslSocket::tr("Unrecognized command %1 = %2").arg(i.key(), value));
819 return;
820 }
821 }
822
823 if (q_SSL_CONF_CTX_finish(cctx.data()) == 0) {
824 sslContext->errorStr = msgErrorSettingBackendConfig(QSslSocket::tr("SSL_CONF_finish() failed"));
825 sslContext->errorCode = QSslError::UnspecifiedError;
826 }
827 } else {
828 sslContext->errorStr = msgErrorSettingBackendConfig(QSslSocket::tr("SSL_CONF_CTX_new() failed"));
829 sslContext->errorCode = QSslError::UnspecifiedError;
830 }
831}
832
833QT_END_NAMESPACE
QByteArray sessionASN1() const
int sessionTicketLifeTimeHint() const
NPNContext npnContext() const
QString errorString() const
static void forceAutoTestSecurityLevel()
void setSessionASN1(const QByteArray &sessionASN1)
bool cacheSession(SSL *)
QSslError::SslError error() const
Namespace containing onternal types that TLS backends implement.
int q_X509Callback(int ok, X509_STORE_CTX *ctx)
int q_X509CallbackDirect(int ok, X509_STORE_CTX *ctx)
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)
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)
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)
int q_EVP_PKEY_set1_RSA(EVP_PKEY *a, RSA *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)
int q_SSL_clear(SSL *a)
SSL_CTX * q_SSL_CTX_new(const SSL_METHOD *a)
int q_SSL_CTX_use_certificate(SSL_CTX *a, X509 *b)
EVP_PKEY * q_EVP_PKEY_new()
void q_EVP_PKEY_free(EVP_PKEY *a)
#define q_SSL_CTX_set_min_proto_version(ctx, version)
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_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_security_level(SSL_CTX *ctx, int level)
const SSL_METHOD * q_TLS_client_method()
SSL * q_SSL_new(SSL_CTX *a)
#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)
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)
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)