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

source code of qtbase/src/plugins/tls/openssl/qtlsbackend_openssl.cpp