12#include "qdtls_openssl_p.h"
15#include <QtNetwork/private/qsslcipher_p.h>
17#include <QtNetwork/qsslcipher.h>
18#include <QtNetwork/qssl.h>
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>
33using namespace Qt::StringLiterals;
35#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
36constexpr auto DefaultWarningLevel = QtCriticalMsg;
44 QList<QSslCipher> &defaultCiphers)
48 STACK_OF(SSL_CIPHER) *supportedCiphers = q_SSL_get_ciphers(connection);
51 const auto ciph = QTlsBackendOpenSSL::qt_OpenSSL_cipher_to_QSslCipher(cipher);
54 if (!ciph.name().toLower().startsWith(
"adh"_L1) &&
55 !ciph.name().toLower().startsWith(
"exp-adh"_L1) &&
56 !ciph.name().toLower().startsWith(
"aecdh"_L1)) {
59 if (ciph.usedBits() >= 128)
60 defaultCiphers << ciph;
69QString QTlsBackendOpenSSL::getErrorsFromOpenSsl()
75 if (!errorString.isEmpty())
76 errorString.append(
", "_L1);
78 errorString.append(QLatin1StringView(buf));
85 const auto errors = getErrorsFromOpenSsl();
87 qCWarning(lcTlsBackend) <<
"Discarding errors:" << errors;
96bool QTlsBackendOpenSSL::ensureLibraryLoaded()
98 static bool libraryLoaded = []() {
107 qCWarning(lcTlsBackend,
"QSslSocket: OpenSSL >= 1.1.1 is required; %s was found instead",
q_OpenSSL_version(OPENSSL_VERSION
));
125#if defined(OPENSSL_VERSION_MAJOR
) && OPENSSL_VERSION_MAJOR
>= 3
126 if (!q_EVP_default_properties_is_fips_enabled(
nullptr)) {
127 static OSSL_PROVIDER *s_defaultProvider = q_OSSL_PROVIDER_load(
nullptr,
"default");
128 (
void)s_defaultProvider;
134 qWarning(
"Random number generator not seeded, disabling SSL support");
141 return libraryLoaded;
146 return builtinBackendNames[nameIndexOpenSSL];
151 return ensureLibraryLoaded();
165 return QString::fromLatin1(versionString);
170 return OPENSSL_VERSION_NUMBER;
178 return QStringLiteral(OPENSSL_VERSION_TEXT);
189 ensureCiphersAndCertsLoaded();
192void QTlsBackendOpenSSL::ensureCiphersAndCertsLoaded()
const
194 Q_CONSTINIT
static bool initializationStarted =
false;
195 Q_CONSTINIT
static QAtomicInt initialized = Q_BASIC_ATOMIC_INITIALIZER(0);
196 Q_CONSTINIT
static QRecursiveMutex initMutex;
198 if (initialized.loadAcquire())
201 const QMutexLocker locker(&initMutex);
203 if (initializationStarted || initialized.loadAcquire())
209 initializationStarted =
true;
211 auto guard = qScopeGuard([] { initialized.storeRelease(1); });
213 resetDefaultCiphers();
214 resetDefaultEllipticCurves();
216#if QT_CONFIG(library)
219 QSslSocketPrivate::setRootCertOnDemandLoadingSupported(
true);
220#elif defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
222 const QList<QByteArray> dirs = QSslSocketPrivate::unixRootCertDirectories();
223 const QStringList symLinkFilter{
224 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};
225 for (
const auto &dir : dirs) {
226 QDirListing dirList(QString::fromLatin1(dir), symLinkFilter,
227 QDirListing::IteratorFlag::FilesOnly);
228 if (dirList.cbegin() != dirList.cend()) {
229 QSslSocketPrivate::setRootCertOnDemandLoadingSupported(
true);
236 if (!QSslSocketPrivate::rootCertOnDemandLoadingSupported())
237 setDefaultCaCertificates(systemCaCertificates());
244 QSslSocketPrivate::setRootCertOnDemandLoadingSupported(
true);
248void QTlsBackendOpenSSL::resetDefaultCiphers()
258 QList<QSslCipher> ciphers;
259 QList<QSslCipher> defaultCiphers;
261 q_loadCiphersForConnection(mySsl, ciphers, defaultCiphers);
266 setDefaultSupportedCiphers(ciphers);
267 setDefaultCiphers(defaultCiphers);
271 defaultCiphers.clear();
272 myCtx = q_SSL_CTX_new(q_DTLS_client_method());
274 mySsl = q_SSL_new(myCtx);
276 q_loadCiphersForConnection(mySsl, ciphers, defaultCiphers);
277 setDefaultDtlsCiphers(defaultCiphers);
280 q_SSL_CTX_free(myCtx);
287 QList<QSsl::SslProtocol> protocols;
289 protocols << QSsl::AnyProtocol;
290 protocols << QSsl::SecureProtocols;
292QT_WARNING_DISABLE_DEPRECATED
293 protocols << QSsl::TlsV1_0;
294 protocols << QSsl::TlsV1_0OrLater;
295 protocols << QSsl::TlsV1_1;
296 protocols << QSsl::TlsV1_1OrLater;
298 protocols << QSsl::TlsV1_2;
299 protocols << QSsl::TlsV1_2OrLater;
302 protocols << QSsl::TlsV1_3;
303 protocols << QSsl::TlsV1_3OrLater;
308QT_WARNING_DISABLE_DEPRECATED
309 protocols << QSsl::DtlsV1_0;
310 protocols << QSsl::DtlsV1_0OrLater;
312 protocols << QSsl::DtlsV1_2;
313 protocols << QSsl::DtlsV1_2OrLater;
321 QList<QSsl::SupportedFeature> features;
323 features << QSsl::SupportedFeature::CertificateVerification;
325#if !defined(OPENSSL_NO_TLSEXT)
326 features << QSsl::SupportedFeature::ClientSideAlpn;
327 features << QSsl::SupportedFeature::ServerSideAlpn;
330 features << QSsl::SupportedFeature::Ocsp;
331 features << QSsl::SupportedFeature::Psk;
332 features << QSsl::SupportedFeature::SessionTicket;
333 features << QSsl::SupportedFeature::Alerts;
340 QList<QSsl::ImplementedClass> classes;
342 classes << QSsl::ImplementedClass::Key;
343 classes << QSsl::ImplementedClass::Certificate;
344 classes << QSsl::ImplementedClass::Socket;
346 classes << QSsl::ImplementedClass::Dtls;
347 classes << QSsl::ImplementedClass::DtlsCookie;
349 classes << QSsl::ImplementedClass::EllipticCurve;
350 classes << QSsl::ImplementedClass::DiffieHellman;
376#ifdef QSSLSOCKET_DEBUG
380 QList<QSslCertificate> systemCerts;
382 HCERTSTORE hSystemStore;
384 CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0,
385 CERT_STORE_READONLY_FLAG | CERT_SYSTEM_STORE_CURRENT_USER, L"ROOT");
387 PCCERT_CONTEXT pc =
nullptr;
389 pc = CertFindCertificateInStore(hSystemStore, X509_ASN_ENCODING, 0, CERT_FIND_ANY,
nullptr, pc);
392 QByteArray der(
reinterpret_cast<
const char *>(pc->pbCertEncoded),
393 static_cast<
int>(pc->cbCertEncoded));
394 QSslCertificate cert(der, QSsl::Der);
395 systemCerts.append(cert);
397 CertCloseStore(hSystemStore, 0);
399#elif defined(Q_OS_ANDROID)
400 const QList<QByteArray> certData = fetchSslCertificateData();
401 for (
auto certDatum : certData)
402 systemCerts.append(QSslCertificate::fromData(certDatum, QSsl::Der));
403#elif defined(Q_OS_UNIX)
405 const QList<QByteArray> directories = QSslSocketPrivate::unixRootCertDirectories();
406 QSet<QString> certFiles = {
407 QStringLiteral(
"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem"),
408 QStringLiteral(
"/etc/pki/tls/certs/ca-bundle.crt"),
409 QStringLiteral(
"/usr/local/share/certs/ca-root-nss.crt")
412 static const size_t extLen = strlen(
".pem");
413 auto hasMatchingExtension = [](
const QString &fileName) {
414 if (size_t(fileName.size()) < extLen + 1)
416 auto ext = QStringView{fileName}.last(extLen);
417 return ext ==
".pem"_L1 || ext ==
".crt"_L1;
420 using F = QDirListing::IteratorFlag;
421 constexpr auto flags = F::FilesOnly | F::ResolveSymlinks;
422 for (
const QByteArray &directory : directories) {
423 for (
const auto &dirEntry : QDirListing(QFile::decodeName(directory), flags)) {
425 if (hasMatchingExtension(dirEntry.fileName())) {
426 QString canonicalPath = dirEntry.canonicalFilePath();
429 if (!canonicalPath.isEmpty())
430 certFiles.insert(canonicalPath);
434 for (
const QString& file : std::as_const(certFiles))
435 systemCerts.append(QSslCertificate::fromFile(file, QSsl::Pem));
438#ifdef QSSLSOCKET_DEBUG
439 qCDebug(lcTlsBackend) <<
"systemCaCertificates retrieval time " << timer.elapsed() <<
"ms";
440 qCDebug(lcTlsBackend) <<
"imported " << systemCerts.count() <<
" certificates";
450 return QTlsPrivate::systemCaCertificates();
456 return new QDtlsClientVerifierOpenSSL;
458 qCWarning(lcTlsBackend,
"Feature 'dtls' is disabled, cannot verify DTLS cookies");
471 return new QDtlsPrivateOpenSSL(q, QSslSocket::SslMode(mode));
475 qCWarning(lcTlsBackend,
"Feature 'dtls' is disabled, cannot encrypt UDP datagrams");
482 return QTlsPrivate::X509CertificateOpenSSL::verify;
487 return QTlsPrivate::X509CertificateOpenSSL::certificatesFromPem;
492 return QTlsPrivate::X509CertificateOpenSSL::certificatesFromDer;
497 return QTlsPrivate::X509CertificateOpenSSL::importPkcs12;
505 const size_t curveCount = q_EC_get_builtin_curves(
nullptr, 0);
506 QVarLengthArray<EC_builtin_curve> builtinCurves(
static_cast<
int>(curveCount));
508 if (q_EC_get_builtin_curves(builtinCurves.data(), curveCount) == curveCount) {
509 ids.reserve(curveCount);
510 for (
const auto &ec : builtinCurves)
511 ids.push_back(ec.nid);
526 const QByteArray curveNameLatin1 = name.toLatin1();
527 nid = q_OBJ_sn2nid(curveNameLatin1.data());
530 nid = q_EC_curve_nist2nid(curveNameLatin1.data());
545 const QByteArray curveNameLatin1 = name.toLatin1();
546 nid = q_OBJ_ln2nid(curveNameLatin1.data());
558 result = QString::fromLatin1(q_OBJ_nid2sn(id));
570 result = QString::fromLatin1(q_OBJ_nid2ln(id));
599 NID_X9_62_prime192v1,
603 NID_X9_62_prime256v1,
617 const int *
const tlsNamedCurveNIDsEnd =
tlsNamedCurveNIDs + tlsNamedCurveNIDCount;
621QString QTlsBackendOpenSSL::msgErrorsDuringHandshake()
623 return QSslSocket::tr(
"Error during SSL handshake: %1").arg(getErrorsFromOpenSsl());
626QSslCipher QTlsBackendOpenSSL::qt_OpenSSL_cipher_to_QSslCipher(
const SSL_CIPHER *cipher)
630 const QString desc = QString::fromLatin1(q_SSL_CIPHER_description(cipher, buf,
sizeof(buf)));
631 int supportedBits = 0;
633 return createCiphersuite(desc, bits, supportedBits);
643#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
static void clearErrorQueue()
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 int s_indexForSSLExtraData
Namespace containing onternal types that TLS backends implement.
QList< QSslCertificate > systemCaCertificates()
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
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()
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)