diff options
Diffstat (limited to 'src/network/ssl/qsslcontext_openssl.cpp')
-rw-r--r-- | src/network/ssl/qsslcontext_openssl.cpp | 550 |
1 files changed, 476 insertions, 74 deletions
diff --git a/src/network/ssl/qsslcontext_openssl.cpp b/src/network/ssl/qsslcontext_openssl.cpp index 8566d78aef..562aa4f518 100644 --- a/src/network/ssl/qsslcontext_openssl.cpp +++ b/src/network/ssl/qsslcontext_openssl.cpp @@ -3,6 +3,7 @@ ** Copyright (C) 2017 The Qt Company Ltd. ** Copyright (C) 2014 BlackBerry Limited. All rights reserved. ** Copyright (C) 2014 Governikus GmbH & Co. KG. +** Copyright (C) 2016 Richard J. Moore <rich@kde.org> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -39,21 +40,49 @@ ** ****************************************************************************/ - #include <QtNetwork/qsslsocket.h> +#include <QtNetwork/qssldiffiehellmanparameters.h> #include "private/qssl_p.h" +#include "private/qsslsocket_p.h" #include "private/qsslcontext_openssl_p.h" #include "private/qsslsocket_openssl_p.h" #include "private/qsslsocket_openssl_symbols_p.h" +#include "private/qssldiffiehellmanparameters_p.h" + +#include <vector> QT_BEGIN_NAMESPACE +// defined in qsslsocket_openssl.cpp: +extern int q_X509Callback(int ok, X509_STORE_CTX *ctx); +extern QString getErrorsFromOpenSsl(); + +#if QT_CONFIG(dtls) +// defined in qdtls_openssl.cpp: +namespace dtlscallbacks +{ +extern "C" int q_X509DtlsCallback(int ok, X509_STORE_CTX *ctx); +extern "C" int q_generate_cookie_callback(SSL *ssl, unsigned char *dst, + unsigned *cookieLength); +extern "C" int q_verify_cookie_callback(SSL *ssl, const unsigned char *cookie, + unsigned cookieLength); +} +#endif // dtls + +// Defined in qsslsocket.cpp +QList<QSslCipher> q_getDefaultDtlsCiphers(); + static inline QString msgErrorSettingBackendConfig(const QString &why) { return QSslSocket::tr("Error when setting the OpenSSL configuration (%1)").arg(why); } +static inline QString msgErrorSettingEllipticCurves(const QString &why) +{ + return QSslSocket::tr("Error when setting the elliptic curves (%1)").arg(why); +} + QSslContext::QSslContext() : ctx(nullptr), pkey(nullptr), @@ -89,7 +118,7 @@ QSharedPointer<QSslContext> QSslContext::sharedFromConfiguration(QSslSocket::Ssl return sslContext; } -#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG) +#ifndef OPENSSL_NO_NEXTPROTONEG static int next_proto_cb(SSL *, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) @@ -126,7 +155,9 @@ QSslContext::NPNContext QSslContext::npnContext() const { return m_npnContext; } -#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ... +#endif // !OPENSSL_NO_NEXTPROTONEG + + // Needs to be deleted by caller SSL* QSslContext::createSsl() @@ -150,7 +181,7 @@ SSL* QSslContext::createSsl() } } -#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG) +#ifndef OPENSSL_NO_NEXTPROTONEG QList<QByteArray> protocols = sslConfiguration.d->nextAllowedProtocols; if (!protocols.isEmpty()) { m_supportedNPNVersions.clear(); @@ -168,27 +199,22 @@ SSL* QSslContext::createSsl() m_npnContext.data = reinterpret_cast<unsigned char *>(m_supportedNPNVersions.data()); m_npnContext.len = m_supportedNPNVersions.count(); m_npnContext.status = QSslConfiguration::NextProtocolNegotiationNone; -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - if (QSslSocket::sslLibraryVersionNumber() >= 0x10002000L) { - // Callback's type has a parameter 'const unsigned char ** out' - // since it was introduced in 1.0.2. Internally, OpenSSL's own code - // (tests/examples) cast it to unsigned char * (since it's 'out'). - // We just re-use our NPN callback and cast here: - typedef int (*alpn_callback_t) (SSL *, const unsigned char **, unsigned char *, - const unsigned char *, unsigned int, void *); - // With ALPN callback is for a server side only, for a client m_npnContext.status - // will stay in NextProtocolNegotiationNone. - q_SSL_CTX_set_alpn_select_cb(ctx, alpn_callback_t(next_proto_cb), &m_npnContext); - // Client: - q_SSL_set_alpn_protos(ssl, m_npnContext.data, m_npnContext.len); - } -#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L ... - + // Callback's type has a parameter 'const unsigned char ** out' + // since it was introduced in 1.0.2. Internally, OpenSSL's own code + // (tests/examples) cast it to unsigned char * (since it's 'out'). + // We just re-use our NPN callback and cast here: + typedef int (*alpn_callback_t) (SSL *, const unsigned char **, unsigned char *, + const unsigned char *, unsigned int, void *); + // With ALPN callback is for a server side only, for a client m_npnContext.status + // will stay in NextProtocolNegotiationNone. + q_SSL_CTX_set_alpn_select_cb(ctx, alpn_callback_t(next_proto_cb), &m_npnContext); + // Client: + q_SSL_set_alpn_protos(ssl, m_npnContext.data, m_npnContext.len); // And in case our peer does not support ALPN, but supports NPN: q_SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &m_npnContext); } } -#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ... +#endif // !OPENSSL_NO_NEXTPROTONEG return ssl; } @@ -247,6 +273,392 @@ QString QSslContext::errorString() const return errorStr; } +void QSslContext::initSslContext(QSslContext *sslContext, QSslSocket::SslMode mode, + const QSslConfiguration &configuration, + bool allowRootCertOnDemandLoading) +{ + sslContext->sslConfiguration = configuration; + sslContext->errorCode = QSslError::NoError; + + bool client = (mode == QSslSocket::SslClientMode); + + bool reinitialized = false; + bool unsupportedProtocol = false; + bool isDtls = false; +init_context: + if (sslContext->sslConfiguration.protocol() == QSsl::SslV2) { + // SSL 2 is no longer supported, but chosen deliberately -> error + sslContext->ctx = nullptr; + unsupportedProtocol = true; + } else if (sslContext->sslConfiguration.protocol() == QSsl::SslV3) { + // SSL 3 is no longer supported, but chosen deliberately -> error + sslContext->ctx = nullptr; + unsupportedProtocol = true; + } else { + switch (sslContext->sslConfiguration.protocol()) { + case QSsl::DtlsV1_0: + case QSsl::DtlsV1_0OrLater: + case QSsl::DtlsV1_2: + case QSsl::DtlsV1_2OrLater: +#if QT_CONFIG(dtls) + isDtls = true; + sslContext->ctx = q_SSL_CTX_new(client ? q_DTLS_client_method() : q_DTLS_server_method()); +#else // dtls + sslContext->ctx = nullptr; + unsupportedProtocol = true; + qCWarning(lcSsl, "DTLS protocol requested, but feature 'dtls' is disabled"); + +#endif // dtls + break; + case QSsl::TlsV1_3: + case QSsl::TlsV1_3OrLater: +#if !defined(TLS1_3_VERSION) + qCWarning(lcSsl, "TLS 1.3 is not supported"); + sslContext->ctx = nullptr; + unsupportedProtocol = true; + break; +#endif // TLS1_3_VERSION + default: + // The ssl options will actually control the supported methods + sslContext->ctx = q_SSL_CTX_new(client ? q_TLS_client_method() : q_TLS_server_method()); + } + } + + if (!sslContext->ctx) { + // After stopping Flash 10 the SSL library loses its ciphers. Try re-adding them + // by re-initializing the library. + if (!reinitialized) { + reinitialized = true; + if (q_OPENSSL_init_ssl(0, nullptr) == 1) + goto init_context; + } + + sslContext->errorStr = QSslSocket::tr("Error creating SSL context (%1)").arg( + unsupportedProtocol ? QSslSocket::tr("unsupported protocol") : QSslSocketBackendPrivate::getErrorsFromOpenSsl() + ); + sslContext->errorCode = QSslError::UnspecifiedError; + return; + } + + const long anyVersion = +#if QT_CONFIG(dtls) + isDtls ? DTLS_ANY_VERSION : TLS_ANY_VERSION; +#else + TLS_ANY_VERSION; +#endif // dtls + long minVersion = anyVersion; + long maxVersion = anyVersion; + + switch (sslContext->sslConfiguration.protocol()) { + case QSsl::TlsV1_0: + minVersion = TLS1_VERSION; + maxVersion = TLS1_VERSION; + break; + case QSsl::TlsV1_1: + minVersion = TLS1_1_VERSION; + maxVersion = TLS1_1_VERSION; + break; + case QSsl::TlsV1_2: + minVersion = TLS1_2_VERSION; + maxVersion = TLS1_2_VERSION; + break; + case QSsl::TlsV1_3: +#ifdef TLS1_3_VERSION + minVersion = TLS1_3_VERSION; + maxVersion = TLS1_3_VERSION; +#else + // This protocol is not supported by OpenSSL 1.1 and we handle + // it as an error (see the code above). + Q_UNREACHABLE(); +#endif // TLS1_3_VERSION + break; + // Ranges: + case QSsl::TlsV1SslV3: + case QSsl::AnyProtocol: + case QSsl::SecureProtocols: + case QSsl::TlsV1_0OrLater: + minVersion = TLS1_VERSION; + maxVersion = 0; + break; + case QSsl::TlsV1_1OrLater: + minVersion = TLS1_1_VERSION; + maxVersion = 0; + break; + case QSsl::TlsV1_2OrLater: + minVersion = TLS1_2_VERSION; + maxVersion = 0; + break; + case QSsl::DtlsV1_0: + minVersion = DTLS1_VERSION; + maxVersion = DTLS1_VERSION; + break; + case QSsl::DtlsV1_0OrLater: + minVersion = DTLS1_VERSION; + maxVersion = DTLS_MAX_VERSION; + break; + case QSsl::DtlsV1_2: + minVersion = DTLS1_2_VERSION; + maxVersion = DTLS1_2_VERSION; + break; + case QSsl::DtlsV1_2OrLater: + minVersion = DTLS1_2_VERSION; + maxVersion = DTLS_MAX_VERSION; + break; + case QSsl::TlsV1_3OrLater: +#ifdef TLS1_3_VERSION + minVersion = TLS1_3_VERSION; + maxVersion = 0; + break; +#else + // This protocol is not supported by OpenSSL 1.1 and we handle + // it as an error (see the code above). + Q_UNREACHABLE(); + break; +#endif // TLS1_3_VERSION + case QSsl::SslV2: + case QSsl::SslV3: + // These protocols are not supported, and we handle + // them as an error (see the code above). + Q_UNREACHABLE(); + break; + case QSsl::UnknownProtocol: + break; + } + + if (minVersion != anyVersion + && !q_SSL_CTX_set_min_proto_version(sslContext->ctx, minVersion)) { + sslContext->errorStr = QSslSocket::tr("Error while setting the minimal protocol version"); + sslContext->errorCode = QSslError::UnspecifiedError; + return; + } + + if (maxVersion != anyVersion + && !q_SSL_CTX_set_max_proto_version(sslContext->ctx, maxVersion)) { + sslContext->errorStr = QSslSocket::tr("Error while setting the maximum protocol version"); + sslContext->errorCode = QSslError::UnspecifiedError; + return; + } + + // Enable bug workarounds. + long options = QSslSocketBackendPrivate::setupOpenSslOptions(configuration.protocol(), configuration.d->sslOptions); + q_SSL_CTX_set_options(sslContext->ctx, options); + + // Tell OpenSSL to release memory early + // http://www.openssl.org/docs/ssl/SSL_CTX_set_mode.html + q_SSL_CTX_set_mode(sslContext->ctx, SSL_MODE_RELEASE_BUFFERS); + + auto filterCiphers = [](const QList<QSslCipher> &ciphers, bool selectTls13) + { + QByteArray cipherString; + bool first = true; + + for (const QSslCipher &cipher : qAsConst(ciphers)) { + const bool isTls13Cipher = cipher.protocol() == QSsl::TlsV1_3 || cipher.protocol() == QSsl::TlsV1_3OrLater; + if (selectTls13 != isTls13Cipher) + continue; + + if (first) + first = false; + else + cipherString.append(':'); + cipherString.append(cipher.name().toLatin1()); + } + return cipherString; + }; + + // Initialize ciphers + QList<QSslCipher> ciphers = sslContext->sslConfiguration.ciphers(); + if (ciphers.isEmpty()) + ciphers = isDtls ? q_getDefaultDtlsCiphers() : QSslSocketPrivate::defaultCiphers(); + + const QByteArray preTls13Ciphers = filterCiphers(ciphers, false); + + if (preTls13Ciphers.size()) { + if (!q_SSL_CTX_set_cipher_list(sslContext->ctx, preTls13Ciphers.data())) { + sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl()); + sslContext->errorCode = QSslError::UnspecifiedError; + return; + } + } + + const QByteArray tls13Ciphers = filterCiphers(ciphers, true); +#ifdef TLS1_3_VERSION + if (tls13Ciphers.size()) { + if (!q_SSL_CTX_set_ciphersuites(sslContext->ctx, tls13Ciphers.data())) { + sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl()); + sslContext->errorCode = QSslError::UnspecifiedError; + return; + } + } +#endif // TLS1_3_VERSION + if (!preTls13Ciphers.size() && !tls13Ciphers.size()) { + sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QStringLiteral("")); + sslContext->errorCode = QSslError::UnspecifiedError; + return; + } + + const QDateTime now = QDateTime::currentDateTimeUtc(); + + // Add all our CAs to this store. + const auto caCertificates = sslContext->sslConfiguration.caCertificates(); + for (const QSslCertificate &caCertificate : caCertificates) { + // From https://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html: + // + // If several CA certificates matching the name, key identifier, and + // serial number condition are available, only the first one will be + // examined. This may lead to unexpected results if the same CA + // certificate is available with different expiration dates. If a + // ``certificate expired'' verification error occurs, no other + // certificate will be searched. Make sure to not have expired + // certificates mixed with valid ones. + // + // See also: QSslSocketBackendPrivate::verify() + if (caCertificate.expiryDate() >= now) { + q_X509_STORE_add_cert(q_SSL_CTX_get_cert_store(sslContext->ctx), (X509 *)caCertificate.handle()); + } + } + + if (QSslSocketPrivate::s_loadRootCertsOnDemand && allowRootCertOnDemandLoading) { + // tell OpenSSL the directories where to look up the root certs on demand + const QList<QByteArray> unixDirs = QSslSocketPrivate::unixRootCertDirectories(); + for (const QByteArray &unixDir : unixDirs) + q_SSL_CTX_load_verify_locations(sslContext->ctx, nullptr, unixDir.constData()); + } + + if (!sslContext->sslConfiguration.localCertificate().isNull()) { + // Require a private key as well. + if (sslContext->sslConfiguration.privateKey().isNull()) { + sslContext->errorStr = QSslSocket::tr("Cannot provide a certificate with no key, %1").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl()); + sslContext->errorCode = QSslError::UnspecifiedError; + return; + } + + // Load certificate + if (!q_SSL_CTX_use_certificate(sslContext->ctx, (X509 *)sslContext->sslConfiguration.localCertificate().handle())) { + sslContext->errorStr = QSslSocket::tr("Error loading local certificate, %1").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl()); + sslContext->errorCode = QSslError::UnspecifiedError; + return; + } + + if (configuration.d->privateKey.algorithm() == QSsl::Opaque) { + sslContext->pkey = reinterpret_cast<EVP_PKEY *>(configuration.d->privateKey.handle()); + } else { + // Load private key + sslContext->pkey = q_EVP_PKEY_new(); + // before we were using EVP_PKEY_assign_R* functions and did not use EVP_PKEY_free. + // this lead to a memory leak. Now we use the *_set1_* functions which do not + // take ownership of the RSA/DSA key instance because the QSslKey already has ownership. + if (configuration.d->privateKey.algorithm() == QSsl::Rsa) + q_EVP_PKEY_set1_RSA(sslContext->pkey, reinterpret_cast<RSA *>(configuration.d->privateKey.handle())); + else if (configuration.d->privateKey.algorithm() == QSsl::Dsa) + q_EVP_PKEY_set1_DSA(sslContext->pkey, reinterpret_cast<DSA *>(configuration.d->privateKey.handle())); +#ifndef OPENSSL_NO_EC + else if (configuration.d->privateKey.algorithm() == QSsl::Ec) + q_EVP_PKEY_set1_EC_KEY(sslContext->pkey, reinterpret_cast<EC_KEY *>(configuration.d->privateKey.handle())); +#endif + } + + if (!q_SSL_CTX_use_PrivateKey(sslContext->ctx, sslContext->pkey)) { + sslContext->errorStr = QSslSocket::tr("Error loading private key, %1").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl()); + sslContext->errorCode = QSslError::UnspecifiedError; + return; + } + if (configuration.d->privateKey.algorithm() == QSsl::Opaque) + sslContext->pkey = nullptr; // Don't free the private key, it belongs to QSslKey + + // Check if the certificate matches the private key. + if (!q_SSL_CTX_check_private_key(sslContext->ctx)) { + sslContext->errorStr = QSslSocket::tr("Private key does not certify public key, %1").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl()); + sslContext->errorCode = QSslError::UnspecifiedError; + return; + } + + // If we have any intermediate certificates then we need to add them to our chain + bool first = true; + for (const QSslCertificate &cert : qAsConst(configuration.d->localCertificateChain)) { + if (first) { + first = false; + continue; + } + q_SSL_CTX_ctrl(sslContext->ctx, SSL_CTRL_EXTRA_CHAIN_CERT, 0, + q_X509_dup(reinterpret_cast<X509 *>(cert.handle()))); + } + } + + // Initialize peer verification. + if (sslContext->sslConfiguration.peerVerifyMode() == QSslSocket::VerifyNone) { + q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_NONE, nullptr); + } else { + q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_PEER, +#if QT_CONFIG(dtls) + isDtls ? dtlscallbacks::q_X509DtlsCallback : +#endif // dtls + q_X509Callback); + } + +#if QT_CONFIG(dtls) + if (mode == QSslSocket::SslServerMode && isDtls && configuration.dtlsCookieVerificationEnabled()) { + q_SSL_CTX_set_cookie_generate_cb(sslContext->ctx, dtlscallbacks::q_generate_cookie_callback); + q_SSL_CTX_set_cookie_verify_cb(sslContext->ctx, dtlscallbacks::q_verify_cookie_callback); + } +#endif // dtls + + // Set verification depth. + if (sslContext->sslConfiguration.peerVerifyDepth() != 0) + q_SSL_CTX_set_verify_depth(sslContext->ctx, sslContext->sslConfiguration.peerVerifyDepth()); + + // set persisted session if the user set it + if (!configuration.sessionTicket().isEmpty()) + sslContext->setSessionASN1(configuration.sessionTicket()); + + // Set temp DH params + QSslDiffieHellmanParameters dhparams = configuration.diffieHellmanParameters(); + + if (!dhparams.isValid()) { + sslContext->errorStr = QSslSocket::tr("Diffie-Hellman parameters are not valid"); + sslContext->errorCode = QSslError::UnspecifiedError; + return; + } + + if (!dhparams.isEmpty()) { + const QByteArray ¶ms = dhparams.d->derData; + const char *ptr = params.constData(); + DH *dh = q_d2i_DHparams(nullptr, reinterpret_cast<const unsigned char **>(&ptr), + params.length()); + if (dh == nullptr) + qFatal("q_d2i_DHparams failed to convert QSslDiffieHellmanParameters to DER form"); + q_SSL_CTX_set_tmp_dh(sslContext->ctx, dh); + q_DH_free(dh); + } + +#ifndef OPENSSL_NO_PSK + if (!client) + q_SSL_CTX_use_psk_identity_hint(sslContext->ctx, sslContext->sslConfiguration.preSharedKeyIdentityHint().constData()); +#endif // !OPENSSL_NO_PSK + + const QVector<QSslEllipticCurve> qcurves = sslContext->sslConfiguration.ellipticCurves(); + if (!qcurves.isEmpty()) { +#ifdef OPENSSL_NO_EC + sslContext->errorStr = msgErrorSettingEllipticCurves(QSslSocket::tr("OpenSSL version with disabled elliptic curves")); + sslContext->errorCode = QSslError::UnspecifiedError; + return; +#else + // Set the curves to be used. + std::vector<int> curves; + curves.reserve(qcurves.size()); + for (const auto &sslCurve : qcurves) + curves.push_back(sslCurve.id); + if (!q_SSL_CTX_ctrl(sslContext->ctx, SSL_CTRL_SET_CURVES, long(curves.size()), &curves[0])) { + sslContext->errorStr = msgErrorSettingEllipticCurves(QSslSocketBackendPrivate::getErrorsFromOpenSsl()); + sslContext->errorCode = QSslError::UnspecifiedError; + return; + } +#endif + } + + applyBackendConfig(sslContext); +} + #if QT_CONFIG(ocsp) extern "C" int qt_OCSP_status_server_callback(SSL *ssl, void *); // Defined in qsslsocket_openssl.cpp. #endif // ocsp @@ -269,65 +681,55 @@ void QSslContext::applyBackendConfig(QSslContext *sslContext) } #endif // ocsp -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - if (QSslSocket::sslLibraryVersionNumber() >= 0x10002000L) { - QSharedPointer<SSL_CONF_CTX> cctx(q_SSL_CONF_CTX_new(), &q_SSL_CONF_CTX_free); - if (cctx) { - q_SSL_CONF_CTX_set_ssl_ctx(cctx.data(), sslContext->ctx); - q_SSL_CONF_CTX_set_flags(cctx.data(), SSL_CONF_FLAG_FILE); - - for (auto i = conf.constBegin(); i != conf.constEnd(); ++i) { - if (i.key() == "Qt-OCSP-response") // This never goes to SSL_CONF_cmd(). - continue; - - if (!i.value().canConvert(QMetaType::QByteArray)) { - sslContext->errorCode = QSslError::UnspecifiedError; - sslContext->errorStr = msgErrorSettingBackendConfig( - QSslSocket::tr("Expecting QByteArray for %1").arg( - QString::fromUtf8(i.key()))); - return; - } - - const QByteArray &value = i.value().toByteArray(); - const int result = q_SSL_CONF_cmd(cctx.data(), i.key().constData(), value.constData()); - if (result == 2) - continue; + QSharedPointer<SSL_CONF_CTX> cctx(q_SSL_CONF_CTX_new(), &q_SSL_CONF_CTX_free); + if (cctx) { + q_SSL_CONF_CTX_set_ssl_ctx(cctx.data(), sslContext->ctx); + q_SSL_CONF_CTX_set_flags(cctx.data(), SSL_CONF_FLAG_FILE); + + for (auto i = conf.constBegin(); i != conf.constEnd(); ++i) { + if (i.key() == "Qt-OCSP-response") // This never goes to SSL_CONF_cmd(). + continue; + if (!i.value().canConvert(QMetaType::QByteArray)) { sslContext->errorCode = QSslError::UnspecifiedError; - switch (result) { - case 0: - sslContext->errorStr = msgErrorSettingBackendConfig( - QSslSocket::tr("An error occurred attempting to set %1 to %2").arg( - QString::fromUtf8(i.key()), QString::fromUtf8(value))); - return; - case 1: - sslContext->errorStr = msgErrorSettingBackendConfig( - QSslSocket::tr("Wrong value for %1 (%2)").arg( - QString::fromUtf8(i.key()), QString::fromUtf8(value))); - return; - default: - sslContext->errorStr = msgErrorSettingBackendConfig( - QSslSocket::tr("Unrecognized command %1 = %2").arg( - QString::fromUtf8(i.key()), QString::fromUtf8(value))); - return; - } + sslContext->errorStr = msgErrorSettingBackendConfig( + QSslSocket::tr("Expecting QByteArray for %1").arg( + QString::fromUtf8(i.key()))); + return; } - if (q_SSL_CONF_CTX_finish(cctx.data()) == 0) { - sslContext->errorStr = msgErrorSettingBackendConfig(QSslSocket::tr("SSL_CONF_finish() failed")); - sslContext->errorCode = QSslError::UnspecifiedError; + const QByteArray &value = i.value().toByteArray(); + const int result = q_SSL_CONF_cmd(cctx.data(), i.key().constData(), value.constData()); + if (result == 2) + continue; + + sslContext->errorCode = QSslError::UnspecifiedError; + switch (result) { + case 0: + sslContext->errorStr = msgErrorSettingBackendConfig( + QSslSocket::tr("An error occurred attempting to set %1 to %2").arg( + QString::fromUtf8(i.key()), QString::fromUtf8(value))); + return; + case 1: + sslContext->errorStr = msgErrorSettingBackendConfig( + QSslSocket::tr("Wrong value for %1 (%2)").arg( + QString::fromUtf8(i.key()), QString::fromUtf8(value))); + return; + default: + sslContext->errorStr = msgErrorSettingBackendConfig( + QSslSocket::tr("Unrecognized command %1 = %2").arg( + QString::fromUtf8(i.key()), QString::fromUtf8(value))); + return; } - } else { - sslContext->errorStr = msgErrorSettingBackendConfig(QSslSocket::tr("SSL_CONF_CTX_new() failed")); - sslContext->errorCode = QSslError::UnspecifiedError; - } - } else -#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L - { - // specific algorithms requested, but not possible to set + } + + if (q_SSL_CONF_CTX_finish(cctx.data()) == 0) { + sslContext->errorStr = msgErrorSettingBackendConfig(QSslSocket::tr("SSL_CONF_finish() failed")); + sslContext->errorCode = QSslError::UnspecifiedError; + } + } else { + sslContext->errorStr = msgErrorSettingBackendConfig(QSslSocket::tr("SSL_CONF_CTX_new() failed")); sslContext->errorCode = QSslError::UnspecifiedError; - sslContext->errorStr = msgErrorSettingBackendConfig( - QSslSocket::tr("OpenSSL version too old, need at least v1.0.2")); } } |