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
qtlsbackend_openssl.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
9
10#if QT_CONFIG(dtls)
11#include "qdtls_openssl_p.h"
12#endif // QT_CONFIG(dtls)
13
14#include <QtNetwork/private/qsslcipher_p.h>
15
16#include <QtNetwork/qsslcipher.h>
17#include <QtNetwork/qssl.h>
18
19#include <QtCore/qdir.h>
20#include <QtCore/qdirlisting.h>
21#include <QtCore/qlist.h>
22#include <QtCore/qmutex.h>
23#include <QtCore/qscopeguard.h>
24#include <QtCore/qset.h>
25
26#include "qopenssl_p.h"
27
28#include <algorithm>
29
31
32using namespace Qt::StringLiterals;
33
34#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
35constexpr auto DefaultWarningLevel = QtCriticalMsg;
36#else
37constexpr auto DefaultWarningLevel = QtDebugMsg;
38#endif
39
40Q_LOGGING_CATEGORY(lcTlsBackend, "qt.tlsbackend.ossl", DefaultWarningLevel);
41
42static void q_loadCiphersForConnection(SSL *connection, QList<QSslCipher> &ciphers,
43 QList<QSslCipher> &defaultCiphers)
44{
45 Q_ASSERT(connection);
46
47 STACK_OF(SSL_CIPHER) *supportedCiphers = q_SSL_get_ciphers(connection);
48 for (int i = 0; i < q_sk_SSL_CIPHER_num(supportedCiphers); ++i) {
49 if (SSL_CIPHER *cipher = q_sk_SSL_CIPHER_value(supportedCiphers, i)) {
50 const auto ciph = QTlsBackendOpenSSL::qt_OpenSSL_cipher_to_QSslCipher(cipher);
51 if (!ciph.isNull()) {
52 // Unconditionally exclude ADH and AECDH ciphers since they offer no MITM protection
53 if (!ciph.name().toLower().startsWith("adh"_L1) &&
54 !ciph.name().toLower().startsWith("exp-adh"_L1) &&
55 !ciph.name().toLower().startsWith("aecdh"_L1)) {
56 ciphers << ciph;
57
58 if (ciph.usedBits() >= 128)
59 defaultCiphers << ciph;
60 }
61 }
62 }
63 }
64}
65
67
68QString QTlsBackendOpenSSL::getErrorsFromOpenSsl()
69{
70 QString errorString;
71 char buf[256] = {}; // OpenSSL docs claim both 120 and 256; use the larger.
72 unsigned long errNum;
73 while ((errNum = q_ERR_get_error())) {
74 if (!errorString.isEmpty())
75 errorString.append(", "_L1);
76 q_ERR_error_string_n(errNum, buf, sizeof buf);
77 errorString.append(QLatin1StringView(buf)); // error is ascii according to man ERR_error_string
78 }
79 return errorString;
80}
81
83{
84 const auto errors = getErrorsFromOpenSsl();
85 if (errors.size())
86 qCWarning(lcTlsBackend) << "Discarding errors:" << errors;
87}
88
94
95bool QTlsBackendOpenSSL::ensureLibraryLoaded()
96{
97 static bool libraryLoaded = []() {
99 return false;
100
101 // Initialize OpenSSL.
102 if (q_OPENSSL_init_ssl(0, nullptr) != 1)
103 return false;
104
105 if (q_OpenSSL_version_num() < 0x10101000L) {
106 qCWarning(lcTlsBackend, "QSslSocket: OpenSSL >= 1.1.1 is required; %s was found instead", q_OpenSSL_version(OPENSSL_VERSION));
107 return false;
108 }
109
112
113 s_indexForSSLExtraData = q_CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL, 0L, nullptr, nullptr,
114 nullptr, nullptr);
115
116 // Initialize OpenSSL's random seed.
117 if (!q_RAND_status()) {
118 qWarning("Random number generator not seeded, disabling SSL support");
119 return false;
120 }
121
122 return true;
123 }();
124
125 return libraryLoaded;
126}
127
129{
130 return builtinBackendNames[nameIndexOpenSSL];
131}
132
134{
135 return ensureLibraryLoaded();
136}
137
142
144{
145 const char *versionString = q_OpenSSL_version(OPENSSL_VERSION);
146 if (!versionString)
147 return QString();
148
149 return QString::fromLatin1(versionString);
150}
151
153{
154 return OPENSSL_VERSION_NUMBER;
155}
156
158{
159 // Using QStringLiteral to store the version string as unicode and
160 // avoid false positives from Google searching the playstore for old
161 // SSL versions. See QTBUG-46265
162 return QStringLiteral(OPENSSL_VERSION_TEXT);
163}
164
166{
167 // Old qsslsocket_openssl calls supportsSsl() (which means
168 // library found and symbols resolved, this already assured
169 // by the fact we end up in this function (isValid() returned
170 // true for the backend, see its code). The qsslsocket_openssl
171 // proceedes with loading certificate, ciphers and elliptic
172 // curves.
173 ensureCiphersAndCertsLoaded();
174}
175
176void QTlsBackendOpenSSL::ensureCiphersAndCertsLoaded() const
177{
178 Q_CONSTINIT static bool initializationStarted = false;
179 Q_CONSTINIT static QAtomicInt initialized = Q_BASIC_ATOMIC_INITIALIZER(0);
180 Q_CONSTINIT static QRecursiveMutex initMutex;
181
182 if (initialized.loadAcquire())
183 return;
184
185 const QMutexLocker locker(&initMutex);
186
187 if (initializationStarted || initialized.loadAcquire())
188 return;
189
190 // Indicate that the initialization has already started in the current
191 // thread in case of recursive calls. The atomic variable cannot be used
192 // for this because it is checked without holding the init mutex.
193 initializationStarted = true;
194
195 auto guard = qScopeGuard([] { initialized.storeRelease(1); });
196
197 resetDefaultCiphers();
198 resetDefaultEllipticCurves();
199
200#if QT_CONFIG(library)
201 //load symbols needed to receive certificates from system store
202#if defined(Q_OS_QNX)
203 QSslSocketPrivate::setRootCertOnDemandLoadingSupported(true);
204#elif defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
205 // check whether we can enable on-demand root-cert loading (i.e. check whether the sym links are there)
206 const QList<QByteArray> dirs = QSslSocketPrivate::unixRootCertDirectories();
207 const QStringList symLinkFilter{
208 u"[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].[0-9]"_s};
209 for (const auto &dir : dirs) {
210 QDirListing dirList(QString::fromLatin1(dir), symLinkFilter,
211 QDirListing::IteratorFlag::FilesOnly);
212 if (dirList.cbegin() != dirList.cend()) { // Not empty
213 QSslSocketPrivate::setRootCertOnDemandLoadingSupported(true);
214 break;
215 }
216 }
217#endif
218#endif // QT_CONFIG(library)
219 // if on-demand loading was not enabled, load the certs now
220 if (!QSslSocketPrivate::rootCertOnDemandLoadingSupported())
221 setDefaultCaCertificates(systemCaCertificates());
222#ifdef Q_OS_WIN
223 //Enabled for fetching additional root certs from windows update on windows.
224 //This flag is set false by setDefaultCaCertificates() indicating the app uses
225 //its own cert bundle rather than the system one.
226 //Same logic that disables the unix on demand cert loading.
227 //Unlike unix, we do preload the certificates from the cert store.
228 QSslSocketPrivate::setRootCertOnDemandLoadingSupported(true);
229#endif
230}
231
232void QTlsBackendOpenSSL::resetDefaultCiphers()
233{
235 // Note, we assert, not just silently return/bail out early:
236 // this should never happen and problems with OpenSSL's initialization
237 // must be caught before this (see supportsSsl()).
238 Q_ASSERT(myCtx);
239 SSL *mySsl = q_SSL_new(myCtx);
240 Q_ASSERT(mySsl);
241
242 QList<QSslCipher> ciphers;
243 QList<QSslCipher> defaultCiphers;
244
245 q_loadCiphersForConnection(mySsl, ciphers, defaultCiphers);
246
247 q_SSL_CTX_free(myCtx);
248 q_SSL_free(mySsl);
249
250 setDefaultSupportedCiphers(ciphers);
251 setDefaultCiphers(defaultCiphers);
252
253#if QT_CONFIG(dtls)
254 ciphers.clear();
255 defaultCiphers.clear();
256 myCtx = q_SSL_CTX_new(q_DTLS_client_method());
257 if (myCtx) {
258 mySsl = q_SSL_new(myCtx);
259 if (mySsl) {
260 q_loadCiphersForConnection(mySsl, ciphers, defaultCiphers);
261 setDefaultDtlsCiphers(defaultCiphers);
262 q_SSL_free(mySsl);
263 }
264 q_SSL_CTX_free(myCtx);
265 }
266#endif // dtls
267}
268
270{
271 QList<QSsl::SslProtocol> protocols;
272
273 protocols << QSsl::AnyProtocol;
274 protocols << QSsl::SecureProtocols;
275QT_WARNING_PUSH
276QT_WARNING_DISABLE_DEPRECATED
277 protocols << QSsl::TlsV1_0;
278 protocols << QSsl::TlsV1_0OrLater;
279 protocols << QSsl::TlsV1_1;
280 protocols << QSsl::TlsV1_1OrLater;
281QT_WARNING_POP
282 protocols << QSsl::TlsV1_2;
283 protocols << QSsl::TlsV1_2OrLater;
284
285#ifdef TLS1_3_VERSION
286 protocols << QSsl::TlsV1_3;
287 protocols << QSsl::TlsV1_3OrLater;
288#endif // TLS1_3_VERSION
289
290#if QT_CONFIG(dtls)
291QT_WARNING_PUSH
292QT_WARNING_DISABLE_DEPRECATED
293 protocols << QSsl::DtlsV1_0;
294 protocols << QSsl::DtlsV1_0OrLater;
295QT_WARNING_POP
296 protocols << QSsl::DtlsV1_2;
297 protocols << QSsl::DtlsV1_2OrLater;
298#endif // dtls
299
300 return protocols;
301}
302
304{
305 QList<QSsl::SupportedFeature> features;
306
307 features << QSsl::SupportedFeature::CertificateVerification;
308
309#if !defined(OPENSSL_NO_TLSEXT)
310 features << QSsl::SupportedFeature::ClientSideAlpn;
311 features << QSsl::SupportedFeature::ServerSideAlpn;
312#endif // !OPENSSL_NO_TLSEXT
313
314 features << QSsl::SupportedFeature::Ocsp;
315 features << QSsl::SupportedFeature::Psk;
316 features << QSsl::SupportedFeature::SessionTicket;
317 features << QSsl::SupportedFeature::Alerts;
318
319 return features;
320}
321
323{
324 QList<QSsl::ImplementedClass> classes;
325
326 classes << QSsl::ImplementedClass::Key;
327 classes << QSsl::ImplementedClass::Certificate;
328 classes << QSsl::ImplementedClass::Socket;
329#if QT_CONFIG(dtls)
330 classes << QSsl::ImplementedClass::Dtls;
331 classes << QSsl::ImplementedClass::DtlsCookie;
332#endif
333 classes << QSsl::ImplementedClass::EllipticCurve;
334 classes << QSsl::ImplementedClass::DiffieHellman;
335
336 return classes;
337}
338
340{
341 return new QTlsPrivate::TlsKeyOpenSSL;
342}
343
345{
346 return new QTlsPrivate::X509CertificateOpenSSL;
347}
348
349namespace QTlsPrivate {
350
351#ifdef Q_OS_ANDROID
353#endif
354
356
357#ifndef Q_OS_DARWIN
359{
360#ifdef QSSLSOCKET_DEBUG
361 QElapsedTimer timer;
362 timer.start();
363#endif
364 QList<QSslCertificate> systemCerts;
365#if defined(Q_OS_WIN)
366 HCERTSTORE hSystemStore;
367 hSystemStore =
368 CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0,
369 CERT_STORE_READONLY_FLAG | CERT_SYSTEM_STORE_CURRENT_USER, L"ROOT");
370 if (hSystemStore) {
371 PCCERT_CONTEXT pc = nullptr;
372 while (1) {
373 pc = CertFindCertificateInStore(hSystemStore, X509_ASN_ENCODING, 0, CERT_FIND_ANY, nullptr, pc);
374 if (!pc)
375 break;
376 QByteArray der(reinterpret_cast<const char *>(pc->pbCertEncoded),
377 static_cast<int>(pc->cbCertEncoded));
378 QSslCertificate cert(der, QSsl::Der);
379 systemCerts.append(cert);
380 }
381 CertCloseStore(hSystemStore, 0);
382 }
383#elif defined(Q_OS_ANDROID)
384 const QList<QByteArray> certData = fetchSslCertificateData();
385 for (auto certDatum : certData)
386 systemCerts.append(QSslCertificate::fromData(certDatum, QSsl::Der));
387#elif defined(Q_OS_UNIX)
388 {
389 const QList<QByteArray> directories = QSslSocketPrivate::unixRootCertDirectories();
390 QSet<QString> certFiles = {
391 QStringLiteral("/etc/pki/tls/certs/ca-bundle.crt"), // Fedora, Mandriva
392 QStringLiteral("/usr/local/share/certs/ca-root-nss.crt") // FreeBSD's ca_root_nss
393 };
394 static const QStringList nameFilters = {u"*.pem"_s, u"*.crt"_s};
395 using F = QDirListing::IteratorFlag;
396 constexpr auto flags = F::FilesOnly | F::ResolveSymlinks; // Files and symlinks to files
397 for (const auto &directory : directories) {
398 for (const auto &dirEntry : QDirListing(directory, nameFilters, flags)) {
399 // use canonical path here to not load the same certificate twice if symlinked
400 certFiles.insert(dirEntry.canonicalFilePath());
401 }
402 }
403 for (const QString& file : std::as_const(certFiles))
404 systemCerts.append(QSslCertificate::fromPath(file, QSsl::Pem));
405 }
406#endif // platform
407#ifdef QSSLSOCKET_DEBUG
408 qCDebug(lcTlsBackend) << "systemCaCertificates retrieval time " << timer.elapsed() << "ms";
409 qCDebug(lcTlsBackend) << "imported " << systemCerts.count() << " certificates";
410#endif
411
412 return systemCerts;
413}
414#endif // !Q_OS_DARWIN
415} // namespace QTlsPrivate
416
418{
419 return QTlsPrivate::systemCaCertificates();
420}
421
423{
424#if QT_CONFIG(dtls)
425 return new QDtlsClientVerifierOpenSSL;
426#else
427 qCWarning(lcTlsBackend, "Feature 'dtls' is disabled, cannot verify DTLS cookies");
428 return nullptr;
429#endif // QT_CONFIG(dtls)
430}
431
436
438{
439#if QT_CONFIG(dtls)
440 return new QDtlsPrivateOpenSSL(q, QSslSocket::SslMode(mode));
441#else
442 Q_UNUSED(q);
443 Q_UNUSED(mode);
444 qCWarning(lcTlsBackend, "Feature 'dtls' is disabled, cannot encrypt UDP datagrams");
445 return nullptr;
446#endif // QT_CONFIG(dtls)
447}
448
450{
451 return QTlsPrivate::X509CertificateOpenSSL::verify;
452}
453
455{
456 return QTlsPrivate::X509CertificateOpenSSL::certificatesFromPem;
457}
458
460{
461 return QTlsPrivate::X509CertificateOpenSSL::certificatesFromDer;
462}
463
465{
466 return QTlsPrivate::X509CertificateOpenSSL::importPkcs12;
467}
468
470{
471 QList<int> ids;
472
473#ifndef OPENSSL_NO_EC
474 const size_t curveCount = q_EC_get_builtin_curves(nullptr, 0);
475 QVarLengthArray<EC_builtin_curve> builtinCurves(static_cast<int>(curveCount));
476
477 if (q_EC_get_builtin_curves(builtinCurves.data(), curveCount) == curveCount) {
478 ids.reserve(curveCount);
479 for (const auto &ec : builtinCurves)
480 ids.push_back(ec.nid);
481 }
482#endif // OPENSSL_NO_EC
483
484 return ids;
485}
486
487 int QTlsBackendOpenSSL::curveIdFromShortName(const QString &name) const
488 {
489 int nid = 0;
490 if (name.isEmpty())
491 return nid;
492
493 ensureInitialized(); // TLSTODO: check if it's needed!
494#ifndef OPENSSL_NO_EC
495 const QByteArray curveNameLatin1 = name.toLatin1();
496 nid = q_OBJ_sn2nid(curveNameLatin1.data());
497
498 if (nid == 0)
499 nid = q_EC_curve_nist2nid(curveNameLatin1.data());
500#endif // !OPENSSL_NO_EC
501
502 return nid;
503 }
504
505 int QTlsBackendOpenSSL::curveIdFromLongName(const QString &name) const
506 {
507 int nid = 0;
508 if (name.isEmpty())
509 return nid;
510
512
513#ifndef OPENSSL_NO_EC
514 const QByteArray curveNameLatin1 = name.toLatin1();
515 nid = q_OBJ_ln2nid(curveNameLatin1.data());
516#endif
517
518 return nid;
519 }
520
522 {
523 QString result;
524
525#ifndef OPENSSL_NO_EC
526 if (id != 0)
527 result = QString::fromLatin1(q_OBJ_nid2sn(id));
528#endif
529
530 return result;
531 }
532
534{
535 QString result;
536
537#ifndef OPENSSL_NO_EC
538 if (id != 0)
539 result = QString::fromLatin1(q_OBJ_nid2ln(id));
540#endif
541
542 return result;
543}
544
545// NIDs of named curves allowed in TLS as per RFCs 4492 and 7027,
546// see also https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
547static const int tlsNamedCurveNIDs[] = {
548 // RFC 4492
549 NID_sect163k1,
550 NID_sect163r1,
551 NID_sect163r2,
552 NID_sect193r1,
553 NID_sect193r2,
554 NID_sect233k1,
555 NID_sect233r1,
556 NID_sect239k1,
557 NID_sect283k1,
558 NID_sect283r1,
559 NID_sect409k1,
560 NID_sect409r1,
561 NID_sect571k1,
562 NID_sect571r1,
563
564 NID_secp160k1,
565 NID_secp160r1,
566 NID_secp160r2,
567 NID_secp192k1,
568 NID_X9_62_prime192v1, // secp192r1
569 NID_secp224k1,
570 NID_secp224r1,
571 NID_secp256k1,
572 NID_X9_62_prime256v1, // secp256r1
573 NID_secp384r1,
574 NID_secp521r1,
575
576 // RFC 7027
577 NID_brainpoolP256r1,
578 NID_brainpoolP384r1,
579 NID_brainpoolP512r1
580};
581
583
585{
586 const int *const tlsNamedCurveNIDsEnd = tlsNamedCurveNIDs + tlsNamedCurveNIDCount;
587 return std::find(tlsNamedCurveNIDs, tlsNamedCurveNIDsEnd, id) != tlsNamedCurveNIDsEnd;
588}
589
590QString QTlsBackendOpenSSL::msgErrorsDuringHandshake()
591{
592 return QSslSocket::tr("Error during SSL handshake: %1").arg(getErrorsFromOpenSsl());
593}
594
595QSslCipher QTlsBackendOpenSSL::qt_OpenSSL_cipher_to_QSslCipher(const SSL_CIPHER *cipher)
596{
597 Q_ASSERT(cipher);
598 char buf [256] = {};
599 const QString desc = QString::fromLatin1(q_SSL_CIPHER_description(cipher, buf, sizeof(buf)));
600 int supportedBits = 0;
601 const int bits = q_SSL_CIPHER_get_bits(cipher, &supportedBits);
602 return createCiphersuite(desc, bits, supportedBits);
603}
604
609
610QT_END_NAMESPACE
611
612#include "moc_qtlsbackend_openssl_p.cpp"
static void forceAutoTestSecurityLevel()
QTlsPrivate::X509DerReaderPtr X509DerReader() const override
QString tlsLibraryBuildVersionString() const override
QList< QSsl::SslProtocol > supportedProtocols() const override
QTlsPrivate::X509ChainVerifyPtr X509Verifier() const override
bool isTlsNamedCurve(int cid) const override
long tlsLibraryVersionNumber() const override
long tlsLibraryBuildVersionNumber() const override
QTlsPrivate::DtlsCryptograph * createDtlsCryptograph(QDtls *q, int mode) const override
static void logAndClearErrorQueue()
int curveIdFromShortName(const QString &name) const override
QTlsPrivate::X509PemReaderPtr X509PemReader() const override
int curveIdFromLongName(const QString &name) const override
bool isValid() const override
QList< int > ellipticCurvesIds() const override
QString shortNameForId(int cid) const override
QTlsPrivate::X509Certificate * createCertificate() const override
void forceAutotestSecurityLevel() override
QTlsPrivate::X509Pkcs12ReaderPtr X509Pkcs12Reader() const override
QString longNameForId(int cid) const override
QList< QSslCertificate > systemCaCertificates() const override
QList< QSsl::SupportedFeature > supportedFeatures() const override
QList< QSsl::ImplementedClass > implementedClasses() const override
QTlsPrivate::DtlsCookieVerifier * createDtlsCookieVerifier() const override
void ensureInitialized() const override
QTlsPrivate::TlsKey * createKey() const override
QTlsPrivate::TlsCryptograph * createTlsCryptograph() const override
QString backendName() const override
QString tlsLibraryVersionString() const override
static QString getErrorsFromOpenSsl()
Combined button and popup list for selecting options.
Namespace containing onternal types that TLS backends implement.
QList< QSslCertificate > systemCaCertificates()
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
void q_SSL_free(SSL *a)
void q_SSL_CTX_free(SSL_CTX *a)
const char * q_OpenSSL_version(int type)
bool q_resolveOpenSslSymbols()
#define q_SSL_load_error_strings()
unsigned long q_ERR_get_error()
void q_ERR_error_string_n(unsigned long e, char *buf, size_t len)
#define q_OpenSSL_add_all_algorithms()
int q_RAND_status()
SSL_CTX * q_SSL_CTX_new(const SSL_METHOD *a)
#define q_sk_SSL_CIPHER_num(st)
const SSL_METHOD * q_TLS_client_method()
SSL * q_SSL_new(SSL_CTX *a)
int q_SSL_CIPHER_get_bits(const SSL_CIPHER *a, int *b)
long q_OpenSSL_version_num()
int q_CRYPTO_get_ex_new_index(int class_index, long argl, void *argp, CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func)
#define q_sk_SSL_CIPHER_value(st, i)
int q_OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings)
constexpr auto DefaultWarningLevel
const size_t tlsNamedCurveNIDCount
static const int tlsNamedCurveNIDs[]
static void q_loadCiphersForConnection(SSL *connection, QList< QSslCipher > &ciphers, QList< QSslCipher > &defaultCiphers)