From cfbe03a6e035ab3cce5f04962cddd06bd414dcea Mon Sep 17 00:00:00 2001 From: "Richard J. Moore" Date: Thu, 23 Mar 2017 12:43:22 +0100 Subject: QSslSocket: OpenSSL 1.1 backend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch-set implements a new QSslSocket backend based on OpenSSL 1.1. 1. General. The code in this patch was organized to achieve these (somewhat contradicting) objectives: - keep the new code free of #if-ery, as far as possible; - make it easy to clean away dead code when we're eventually able to retire out-dated OpenSSL versions; - reduce the amount of code duplication. If changes in some file/component were insignificant (~5 one-liners per file), we still use pp-checks like: #if QT_CONFIG(opensslv11) ... #else ... #endif - the logic is simple and it's still easy to clean the code if we remove the legacy back-end. Where it saved #if-ery, we also introduced 'forward-compatible' macros implementing equivalents of 1.1 functions using older OpenSSL. In case some class contains a lot of version-specific ifdefs (particularly where nested #if-ery was complex) we choose to split code into: "pre11" h/cpp files, "shared" h/cpp files (they preserve their original names, e.g qsslsocket_openssl.cpp) and "11" h/cpp files. If in future we remove the legacy back-end, "pre11" should be removed; "shared" and "11" parts - merged. 2. Configuration. We introduced a new feature 'opensslv11' which complements the pre-existing 'openssl' and 'openssl-linked' features. The 'opensslv11' feature is enabled by a simple test which either compiles successfully or ends in a compilation error, depending on a value of the OPENSSL_VERSION_NUMBER constant. If the feature was enabled, we also append an additional compilation flag -DOPENSSL_API_COMPAT=0x10100000L to make sure our new code does not contain deprecated structures, function calls, macro-invocations from OpenSSL < 1.1. Change-Id: I2064efbe9685def5d2bb2233a66f7581954fb74a Reviewed-by: AndrĂ© Klitzing Reviewed-by: Allan Sandfeld Jensen --- config.tests/unix/openssl11/openssl.cpp | 48 ++ config.tests/unix/openssl11/openssl.pro | 2 + src/network/configure.json | 11 + src/network/ssl/qsslcertificate_openssl.cpp | 57 +-- src/network/ssl/qsslcontext_openssl.cpp | 309 +------------ src/network/ssl/qsslcontext_openssl11.cpp | 277 +++++++++++ src/network/ssl/qsslcontext_opensslpre11.cpp | 354 ++++++++++++++ .../ssl/qssldiffiehellmanparameters_openssl.cpp | 38 +- src/network/ssl/qsslellipticcurve.h | 1 + src/network/ssl/qsslellipticcurve_openssl.cpp | 8 +- src/network/ssl/qsslkey_openssl.cpp | 79 ++-- src/network/ssl/qsslsocket_openssl.cpp | 412 ++--------------- src/network/ssl/qsslsocket_openssl11.cpp | 285 ++++++++++++ src/network/ssl/qsslsocket_openssl11_symbols_p.h | 132 ++++++ src/network/ssl/qsslsocket_openssl_p.h | 8 +- src/network/ssl/qsslsocket_openssl_symbols.cpp | 509 +++++++++++++-------- src/network/ssl/qsslsocket_openssl_symbols_p.h | 127 +---- src/network/ssl/qsslsocket_opensslpre11.cpp | 424 +++++++++++++++++ .../ssl/qsslsocket_opensslpre11_symbols_p.h | 230 ++++++++++ src/network/ssl/ssl.pri | 18 +- 20 files changed, 2282 insertions(+), 1047 deletions(-) create mode 100644 config.tests/unix/openssl11/openssl.cpp create mode 100644 config.tests/unix/openssl11/openssl.pro create mode 100644 src/network/ssl/qsslcontext_openssl11.cpp create mode 100644 src/network/ssl/qsslcontext_opensslpre11.cpp create mode 100644 src/network/ssl/qsslsocket_openssl11.cpp create mode 100644 src/network/ssl/qsslsocket_openssl11_symbols_p.h create mode 100644 src/network/ssl/qsslsocket_opensslpre11.cpp create mode 100644 src/network/ssl/qsslsocket_opensslpre11_symbols_p.h diff --git a/config.tests/unix/openssl11/openssl.cpp b/config.tests/unix/openssl11/openssl.cpp new file mode 100644 index 0000000000..c20cc59deb --- /dev/null +++ b/config.tests/unix/openssl11/openssl.cpp @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the config.tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER-0 < 0x10100000L +# error "OpenSSL >= 1.1 is required" +#endif + +int main() +{ +} diff --git a/config.tests/unix/openssl11/openssl.pro b/config.tests/unix/openssl11/openssl.pro new file mode 100644 index 0000000000..a023aee4aa --- /dev/null +++ b/config.tests/unix/openssl11/openssl.pro @@ -0,0 +1,2 @@ +SOURCES = openssl.cpp +CONFIG -= x11 qt diff --git a/src/network/configure.json b/src/network/configure.json index efb48f7655..17460285bc 100644 --- a/src/network/configure.json +++ b/src/network/configure.json @@ -105,6 +105,12 @@ "type": "compile", "test": "unix/sctp", "use": "network" + }, + "openssl11": { + "label": "OpenSSL v. 1.1 support", + "type": "compile", + "test": "unix/openssl11", + "use": "network" } }, @@ -172,6 +178,11 @@ "condition": "config.winrt || features.securetransport || features.openssl", "output": [ "publicFeature", "feature" ] }, + "opensslv11": { + "label": "OpenSSL v. 1.1", + "condition": "tests.openssl11", + "output": ["publicFeature", "feature"] + }, "sctp": { "label": "SCTP", "autoDetect": false, diff --git a/src/network/ssl/qsslcertificate_openssl.cpp b/src/network/ssl/qsslcertificate_openssl.cpp index 28b7eda54a..71e514a025 100644 --- a/src/network/ssl/qsslcertificate_openssl.cpp +++ b/src/network/ssl/qsslcertificate_openssl.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2016 Richard J. Moore ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -64,12 +65,14 @@ bool QSslCertificate::operator==(const QSslCertificate &other) const uint qHash(const QSslCertificate &key, uint seed) Q_DECL_NOTHROW { if (X509 * const x509 = key.d->x509) { - (void)q_X509_cmp(x509, x509); // populate x509->sha1_hash - // (if someone knows a better way...) - return qHashBits(x509->sha1_hash, SHA_DIGEST_LENGTH, seed); - } else { - return seed; + const EVP_MD *sha1 = q_EVP_sha1(); + unsigned int len = 0; + unsigned char md[EVP_MAX_MD_SIZE]; + q_X509_digest(x509, sha1, md, &len); + return qHashBits(md, len, seed); } + + return seed; } bool QSslCertificate::isNull() const @@ -89,8 +92,7 @@ QByteArray QSslCertificate::version() const { QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); if (d->versionString.isEmpty() && d->x509) - d->versionString = - QByteArray::number(qlonglong(q_ASN1_INTEGER_get(d->x509->cert_info->version)) + 1); + d->versionString = QByteArray::number(qlonglong(q_X509_get_version(d->x509)) + 1); return d->versionString; } @@ -99,7 +101,7 @@ QByteArray QSslCertificate::serialNumber() const { QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); if (d->serialNumberString.isEmpty() && d->x509) { - ASN1_INTEGER *serialNumber = d->x509->cert_info->serialNumber; + ASN1_INTEGER *serialNumber = q_X509_get_serialNumber(d->x509); QByteArray hexString; hexString.reserve(serialNumber->length * 3); for (int a = 0; a < serialNumber->length; ++a) { @@ -199,14 +201,15 @@ QMultiMap QSslCertificate::subjectAlter continue; } - const char *altNameStr = reinterpret_cast(q_ASN1_STRING_data(genName->d.ia5)); + const char *altNameStr = reinterpret_cast(q_ASN1_STRING_get0_data(genName->d.ia5)); const QString altName = QString::fromLatin1(altNameStr, len); if (genName->type == GEN_DNS) result.insert(QSsl::DnsEntry, altName); else if (genName->type == GEN_EMAIL) result.insert(QSsl::EmailEntry, altName); } - q_sk_pop_free((STACK*)altNames, reinterpret_cast(q_sk_free)); + + q_OPENSSL_sk_pop_free((OPENSSL_STACK*)altNames, reinterpret_cast(q_OPENSSL_sk_free)); } return result; @@ -235,25 +238,26 @@ QSslKey QSslCertificate::publicKey() const QSslKey key; key.d->type = QSsl::PublicKey; - X509_PUBKEY *xkey = d->x509->cert_info->key; - EVP_PKEY *pkey = q_X509_PUBKEY_get(xkey); + + EVP_PKEY *pkey = q_X509_get_pubkey(d->x509); Q_ASSERT(pkey); + const int keyType = q_EVP_PKEY_type(q_EVP_PKEY_base_id(pkey)); - if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_RSA) { + if (keyType == EVP_PKEY_RSA) { key.d->rsa = q_EVP_PKEY_get1_RSA(pkey); key.d->algorithm = QSsl::Rsa; key.d->isNull = false; - } else if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_DSA) { + } else if (keyType == EVP_PKEY_DSA) { key.d->dsa = q_EVP_PKEY_get1_DSA(pkey); key.d->algorithm = QSsl::Dsa; key.d->isNull = false; #ifndef OPENSSL_NO_EC - } else if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_EC) { + } else if (keyType == EVP_PKEY_EC) { key.d->ec = q_EVP_PKEY_get1_EC_KEY(pkey); key.d->algorithm = QSsl::Ec; key.d->isNull = false; #endif - } else if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_DH) { + } else if (keyType == EVP_PKEY_DH) { // DH unsupported } else { // error? @@ -275,7 +279,7 @@ static QVariant x509UnknownExtensionToValue(X509_EXTENSION *ext) X509V3_EXT_METHOD *meth = const_cast(q_X509V3_EXT_get(ext)); if (!meth) { ASN1_OCTET_STRING *value = q_X509_EXTENSION_get_data(ext); - QByteArray result( reinterpret_cast(q_ASN1_STRING_data(value)), + QByteArray result( reinterpret_cast(q_ASN1_STRING_get0_data(value)), q_ASN1_STRING_length(value)); return result; } @@ -371,7 +375,7 @@ static QVariant x509ExtensionToValue(X509_EXTENSION *ext) continue; } - const char *uriStr = reinterpret_cast(q_ASN1_STRING_data(name->d.uniformResourceIdentifier)); + const char *uriStr = reinterpret_cast(q_ASN1_STRING_get0_data(name->d.uniformResourceIdentifier)); const QString uri = QString::fromUtf8(uriStr, len); result[QString::fromUtf8(QSslCertificatePrivate::asn1ObjectName(ad->method))] = uri; @@ -380,11 +384,7 @@ static QVariant x509ExtensionToValue(X509_EXTENSION *ext) } } -#if OPENSSL_VERSION_NUMBER >= 0x10000000L - q_sk_pop_free((_STACK*)info, reinterpret_cast(q_sk_free)); -#else - q_sk_pop_free((STACK*)info, reinterpret_cast(q_sk_free)); -#endif + q_OPENSSL_sk_pop_free((OPENSSL_STACK*)info, reinterpret_cast(q_OPENSSL_sk_free)); return result; } break; @@ -607,7 +607,11 @@ static QMap _q_mapFromX509Name(X509_NAME *name) unsigned char *data = 0; int size = q_ASN1_STRING_to_UTF8(&data, q_X509_NAME_ENTRY_get_data(e)); info.insertMulti(name, QString::fromUtf8((char*)data, size)); +#if QT_CONFIG(opensslv11) + q_CRYPTO_free(data, 0, 0); +#else q_CRYPTO_free(data); +#endif } return info; @@ -619,8 +623,9 @@ QSslCertificate QSslCertificatePrivate::QSslCertificate_from_X509(X509 *x509) if (!x509 || !QSslSocket::supportsSsl()) return certificate; - ASN1_TIME *nbef = q_X509_get_notBefore(x509); - ASN1_TIME *naft = q_X509_get_notAfter(x509); + ASN1_TIME *nbef = q_X509_getm_notBefore(x509); + ASN1_TIME *naft = q_X509_getm_notAfter(x509); + certificate.d->notValidBefore = q_getTimeFromASN1(nbef); certificate.d->notValidAfter = q_getTimeFromASN1(naft); certificate.d->null = false; diff --git a/src/network/ssl/qsslcontext_openssl.cpp b/src/network/ssl/qsslcontext_openssl.cpp index c92d8fc3f8..cef503710c 100644 --- a/src/network/ssl/qsslcontext_openssl.cpp +++ b/src/network/ssl/qsslcontext_openssl.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. ** Copyright (C) 2014 BlackBerry Limited. All rights reserved. ** Copyright (C) 2014 Governikus GmbH & Co. KG. ** Contact: https://www.qt.io/licensing/ @@ -41,22 +41,14 @@ #include -#include -#include #include "private/qssl_p.h" #include "private/qsslcontext_openssl_p.h" -#include "private/qsslsocket_p.h" #include "private/qsslsocket_openssl_p.h" #include "private/qsslsocket_openssl_symbols_p.h" -#include "private/qssldiffiehellmanparameters_p.h" QT_BEGIN_NAMESPACE -// defined in qsslsocket_openssl.cpp: -extern int q_X509Callback(int ok, X509_STORE_CTX *ctx); -extern QString getErrorsFromOpenSsl(); - QSslContext::QSslContext() : ctx(0), pkey(0), @@ -78,301 +70,6 @@ QSslContext::~QSslContext() q_SSL_SESSION_free(session); } -static inline QString msgErrorSettingEllipticCurves(const QString &why) -{ - return QSslSocket::tr("Error when setting the elliptic curves (%1)").arg(why); -} - -// static -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; -init_context: - switch (sslContext->sslConfiguration.protocol()) { - case QSsl::SslV2: -#ifndef OPENSSL_NO_SSL2 - sslContext->ctx = q_SSL_CTX_new(client ? q_SSLv2_client_method() : q_SSLv2_server_method()); -#else - // SSL 2 not supported by the system, but chosen deliberately -> error - sslContext->ctx = 0; - unsupportedProtocol = true; -#endif - break; - case QSsl::SslV3: -#ifndef OPENSSL_NO_SSL3_METHOD - sslContext->ctx = q_SSL_CTX_new(client ? q_SSLv3_client_method() : q_SSLv3_server_method()); -#else - // SSL 3 not supported by the system, but chosen deliberately -> error - sslContext->ctx = 0; - unsupportedProtocol = true; -#endif - break; - case QSsl::SecureProtocols: - // SSLv2 and SSLv3 will be disabled by SSL options - // But we need q_SSLv23_server_method() otherwise AnyProtocol will be unable to connect on Win32. - case QSsl::TlsV1SslV3: - // SSLv2 will will be disabled by SSL options - case QSsl::AnyProtocol: - default: - sslContext->ctx = q_SSL_CTX_new(client ? q_SSLv23_client_method() : q_SSLv23_server_method()); - break; - case QSsl::TlsV1_0: - sslContext->ctx = q_SSL_CTX_new(client ? q_TLSv1_client_method() : q_TLSv1_server_method()); - break; - case QSsl::TlsV1_1: -#if OPENSSL_VERSION_NUMBER >= 0x10001000L - sslContext->ctx = q_SSL_CTX_new(client ? q_TLSv1_1_client_method() : q_TLSv1_1_server_method()); -#else - // TLS 1.1 not supported by the system, but chosen deliberately -> error - sslContext->ctx = 0; - unsupportedProtocol = true; -#endif - break; - case QSsl::TlsV1_2: -#if OPENSSL_VERSION_NUMBER >= 0x10001000L - sslContext->ctx = q_SSL_CTX_new(client ? q_TLSv1_2_client_method() : q_TLSv1_2_server_method()); -#else - // TLS 1.2 not supported by the system, but chosen deliberately -> error - sslContext->ctx = 0; - unsupportedProtocol = true; -#endif - break; - case QSsl::TlsV1_0OrLater: - // Specific protocols will be specified via SSL options. - sslContext->ctx = q_SSL_CTX_new(client ? q_SSLv23_client_method() : q_SSLv23_server_method()); - break; - case QSsl::TlsV1_1OrLater: - case QSsl::TlsV1_2OrLater: -#if OPENSSL_VERSION_NUMBER >= 0x10001000L - // Specific protocols will be specified via SSL options. - sslContext->ctx = q_SSL_CTX_new(client ? q_SSLv23_client_method() : q_SSLv23_server_method()); -#else - // TLS 1.1/1.2 not supported by the system, but chosen deliberately -> error - sslContext->ctx = 0; - unsupportedProtocol = true; -#endif - break; - } - - if (!sslContext->ctx) { - // After stopping Flash 10 the SSL library looses its ciphers. Try re-adding them - // by re-initializing the library. - if (!reinitialized) { - reinitialized = true; - if (q_SSL_library_init() == 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; - } - - // Enable bug workarounds. - long options = QSslSocketBackendPrivate::setupOpenSslOptions(configuration.protocol(), configuration.d->sslOptions); - q_SSL_CTX_set_options(sslContext->ctx, options); - -#if OPENSSL_VERSION_NUMBER >= 0x10000000L - // Tell OpenSSL to release memory early - // http://www.openssl.org/docs/ssl/SSL_CTX_set_mode.html - if (q_SSLeay() >= 0x10000000L) - q_SSL_CTX_set_mode(sslContext->ctx, SSL_MODE_RELEASE_BUFFERS); -#endif - - // Initialize ciphers - QByteArray cipherString; - bool first = true; - QList ciphers = sslContext->sslConfiguration.ciphers(); - if (ciphers.isEmpty()) - ciphers = QSslSocketPrivate::defaultCiphers(); - for (const QSslCipher &cipher : qAsConst(ciphers)) { - if (first) - first = false; - else - cipherString.append(':'); - cipherString.append(cipher.name().toLatin1()); - } - - if (!q_SSL_CTX_set_cipher_list(sslContext->ctx, cipherString.data())) { - sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl()); - 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 unixDirs = QSslSocketPrivate::unixRootCertDirectories(); - for (const QByteArray &unixDir : unixDirs) - q_SSL_CTX_load_verify_locations(sslContext->ctx, 0, 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(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(configuration.d->privateKey.handle())); - else if (configuration.d->privateKey.algorithm() == QSsl::Dsa) - q_EVP_PKEY_set1_DSA(sslContext->pkey, reinterpret_cast(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(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 = 0; // 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(cert.handle()))); - } - } - - // Initialize peer verification. - if (sslContext->sslConfiguration.peerVerifyMode() == QSslSocket::VerifyNone) { - q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_NONE, 0); - } else { - q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_PEER, q_X509Callback); - } - - // 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(NULL, reinterpret_cast(&ptr), params.length()); - if (dh == NULL) - 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_EC -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - if (q_SSLeay() >= 0x10002000L) { - q_SSL_CTX_ctrl(sslContext->ctx, SSL_CTRL_SET_ECDH_AUTO, 1, NULL); - } else -#endif - { - // Set temp ECDH params - EC_KEY *ecdh = 0; - ecdh = q_EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); - q_SSL_CTX_set_tmp_ecdh(sslContext->ctx, ecdh); - q_EC_KEY_free(ecdh); - } -#endif // OPENSSL_NO_EC - -#if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK) - if (!client) - q_SSL_CTX_use_psk_identity_hint(sslContext->ctx, sslContext->sslConfiguration.preSharedKeyIdentityHint().constData()); -#endif // OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK) - - const QVector qcurves = sslContext->sslConfiguration.ellipticCurves(); - if (!qcurves.isEmpty()) { -#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(OPENSSL_NO_EC) - // Set the curves to be used - if (q_SSLeay() >= 0x10002000L) { - // SSL_CTX_ctrl wants a non-const pointer as last argument, - // but let's avoid a copy into a temporary array - if (!q_SSL_CTX_ctrl(sslContext->ctx, - SSL_CTRL_SET_CURVES, - qcurves.size(), - const_cast(reinterpret_cast(qcurves.data())))) { - sslContext->errorStr = msgErrorSettingEllipticCurves(QSslSocketBackendPrivate::getErrorsFromOpenSsl()); - sslContext->errorCode = QSslError::UnspecifiedError; - } - } else -#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(OPENSSL_NO_EC) - { - // specific curves requested, but not possible to set -> error - sslContext->errorStr = msgErrorSettingEllipticCurves(QSslSocket::tr("OpenSSL version too old, need at least v1.0.2")); - sslContext->errorCode = QSslError::UnspecifiedError; - } - } -} - QSslContext* QSslContext::fromConfiguration(QSslSocket::SslMode mode, const QSslConfiguration &configuration, bool allowRootCertOnDemandLoading) { QSslContext *sslContext = new QSslContext(); @@ -463,7 +160,7 @@ SSL* QSslContext::createSsl() m_npnContext.len = m_supportedNPNVersions.count(); m_npnContext.status = QSslConfiguration::NextProtocolNegotiationNone; #if OPENSSL_VERSION_NUMBER >= 0x10002000L - if (q_SSLeay() >= 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'). @@ -508,7 +205,7 @@ bool QSslContext::cacheSession(SSL* ssl) unsigned char *data = reinterpret_cast(m_sessionASN1.data()); if (!q_i2d_SSL_SESSION(session, &data)) qCWarning(lcSsl, "could not store persistent version of SSL session"); - m_sessionTicketLifeTimeHint = session->tlsext_tick_lifetime_hint; + m_sessionTicketLifeTimeHint = q_SSL_SESSION_get_ticket_lifetime_hint(session); } } diff --git a/src/network/ssl/qsslcontext_openssl11.cpp b/src/network/ssl/qsslcontext_openssl11.cpp new file mode 100644 index 0000000000..787b6ae3f5 --- /dev/null +++ b/src/network/ssl/qsslcontext_openssl11.cpp @@ -0,0 +1,277 @@ +/**************************************************************************** +** +** 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 +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include + +#include "private/qssl_p.h" +#include "private/qsslcontext_openssl_p.h" +#include "private/qsslsocket_p.h" +#include "private/qsslsocket_openssl_p.h" +#include "private/qsslsocket_openssl_symbols_p.h" +#include "private/qssldiffiehellmanparameters_p.h" + +#include + +QT_BEGIN_NAMESPACE + +// defined in qsslsocket_openssl.cpp: +extern int q_X509Callback(int ok, X509_STORE_CTX *ctx); +extern QString getErrorsFromOpenSsl(); + +static inline QString msgErrorSettingEllipticCurves(const QString &why) +{ + return QSslSocket::tr("Error when setting the elliptic curves (%1)").arg(why); +} + +// static +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; +init_context: + if (sslContext->sslConfiguration.protocol() == QSsl::SslV2) { + // SSL 2 is no longer supported, but chosen deliberately -> error + sslContext->ctx = nullptr; + unsupportedProtocol = true; + } else { + // 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; + } + + // 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); + + // Initialize ciphers + QByteArray cipherString; + bool first = true; + QList ciphers = sslContext->sslConfiguration.ciphers(); + if (ciphers.isEmpty()) + ciphers = QSslSocketPrivate::defaultCiphers(); + for (const QSslCipher &cipher : qAsConst(ciphers)) { + if (first) + first = false; + else + cipherString.append(':'); + cipherString.append(cipher.name().toLatin1()); + } + + if (!q_SSL_CTX_set_cipher_list(sslContext->ctx, cipherString.data())) { + sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl()); + 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 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(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(configuration.d->privateKey.handle())); + else if (configuration.d->privateKey.algorithm() == QSsl::Dsa) + q_EVP_PKEY_set1_DSA(sslContext->pkey, reinterpret_cast(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(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(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, q_X509Callback); + } + + // 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(NULL, reinterpret_cast(&ptr), params.length()); + if (dh == NULL) + 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 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; +#else + // Set the curves to be used. + std::vector 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; + } +#endif + } +} + +QT_END_NAMESPACE diff --git a/src/network/ssl/qsslcontext_opensslpre11.cpp b/src/network/ssl/qsslcontext_opensslpre11.cpp new file mode 100644 index 0000000000..9c01c2f2dc --- /dev/null +++ b/src/network/ssl/qsslcontext_opensslpre11.cpp @@ -0,0 +1,354 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2014 BlackBerry Limited. All rights reserved. +** Copyright (C) 2014 Governikus GmbH & Co. KG. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include + +#include "private/qssl_p.h" +#include "private/qsslcontext_openssl_p.h" +#include "private/qsslsocket_p.h" +#include "private/qsslsocket_openssl_p.h" +#include "private/qsslsocket_openssl_symbols_p.h" +#include "private/qssldiffiehellmanparameters_p.h" + +QT_BEGIN_NAMESPACE + +// defined in qsslsocket_openssl.cpp: +extern int q_X509Callback(int ok, X509_STORE_CTX *ctx); +extern QString getErrorsFromOpenSsl(); + +static inline QString msgErrorSettingEllipticCurves(const QString &why) +{ + return QSslSocket::tr("Error when setting the elliptic curves (%1)").arg(why); +} + +// static +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; +init_context: + switch (sslContext->sslConfiguration.protocol()) { + case QSsl::SslV2: +#ifndef OPENSSL_NO_SSL2 + sslContext->ctx = q_SSL_CTX_new(client ? q_SSLv2_client_method() : q_SSLv2_server_method()); +#else + // SSL 2 not supported by the system, but chosen deliberately -> error + sslContext->ctx = 0; + unsupportedProtocol = true; +#endif + break; + case QSsl::SslV3: +#ifndef OPENSSL_NO_SSL3_METHOD + sslContext->ctx = q_SSL_CTX_new(client ? q_SSLv3_client_method() : q_SSLv3_server_method()); +#else + // SSL 3 not supported by the system, but chosen deliberately -> error + sslContext->ctx = 0; + unsupportedProtocol = true; +#endif + break; + case QSsl::SecureProtocols: + // SSLv2 and SSLv3 will be disabled by SSL options + // But we need q_SSLv23_server_method() otherwise AnyProtocol will be unable to connect on Win32. + case QSsl::TlsV1SslV3: + // SSLv2 will will be disabled by SSL options + case QSsl::AnyProtocol: + default: + sslContext->ctx = q_SSL_CTX_new(client ? q_SSLv23_client_method() : q_SSLv23_server_method()); + break; + case QSsl::TlsV1_0: + sslContext->ctx = q_SSL_CTX_new(client ? q_TLSv1_client_method() : q_TLSv1_server_method()); + break; + case QSsl::TlsV1_1: +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + sslContext->ctx = q_SSL_CTX_new(client ? q_TLSv1_1_client_method() : q_TLSv1_1_server_method()); +#else + // TLS 1.1 not supported by the system, but chosen deliberately -> error + sslContext->ctx = 0; + unsupportedProtocol = true; +#endif + break; + case QSsl::TlsV1_2: +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + sslContext->ctx = q_SSL_CTX_new(client ? q_TLSv1_2_client_method() : q_TLSv1_2_server_method()); +#else + // TLS 1.2 not supported by the system, but chosen deliberately -> error + sslContext->ctx = 0; + unsupportedProtocol = true; +#endif + break; + case QSsl::TlsV1_0OrLater: + // Specific protocols will be specified via SSL options. + sslContext->ctx = q_SSL_CTX_new(client ? q_SSLv23_client_method() : q_SSLv23_server_method()); + break; + case QSsl::TlsV1_1OrLater: + case QSsl::TlsV1_2OrLater: +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + // Specific protocols will be specified via SSL options. + sslContext->ctx = q_SSL_CTX_new(client ? q_SSLv23_client_method() : q_SSLv23_server_method()); +#else + // TLS 1.1/1.2 not supported by the system, but chosen deliberately -> error + sslContext->ctx = 0; + unsupportedProtocol = true; +#endif + break; + } + + 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_SSL_library_init() == 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; + } + + // Enable bug workarounds. + long options = QSslSocketBackendPrivate::setupOpenSslOptions(configuration.protocol(), configuration.d->sslOptions); + q_SSL_CTX_set_options(sslContext->ctx, options); + +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + // Tell OpenSSL to release memory early + // http://www.openssl.org/docs/ssl/SSL_CTX_set_mode.html + if (q_SSLeay() >= 0x10000000L) + q_SSL_CTX_set_mode(sslContext->ctx, SSL_MODE_RELEASE_BUFFERS); +#endif + + // Initialize ciphers + QByteArray cipherString; + bool first = true; + QList ciphers = sslContext->sslConfiguration.ciphers(); + if (ciphers.isEmpty()) + ciphers = QSslSocketPrivate::defaultCiphers(); + for (const QSslCipher &cipher : qAsConst(ciphers)) { + if (first) + first = false; + else + cipherString.append(':'); + cipherString.append(cipher.name().toLatin1()); + } + + if (!q_SSL_CTX_set_cipher_list(sslContext->ctx, cipherString.data())) { + sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl()); + 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 unixDirs = QSslSocketPrivate::unixRootCertDirectories(); + for (const QByteArray &unixDir : unixDirs) + q_SSL_CTX_load_verify_locations(sslContext->ctx, 0, 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(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(configuration.d->privateKey.handle())); + else if (configuration.d->privateKey.algorithm() == QSsl::Dsa) + q_EVP_PKEY_set1_DSA(sslContext->pkey, reinterpret_cast(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(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 = 0; // 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(cert.handle()))); + } + } + + // Initialize peer verification. + if (sslContext->sslConfiguration.peerVerifyMode() == QSslSocket::VerifyNone) { + q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_NONE, 0); + } else { + q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_PEER, q_X509Callback); + } + + // 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(NULL, reinterpret_cast(&ptr), params.length()); + if (dh == NULL) + 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_EC +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + if (q_SSLeay() >= 0x10002000L) { + q_SSL_CTX_ctrl(sslContext->ctx, SSL_CTRL_SET_ECDH_AUTO, 1, NULL); + } else +#endif + { + // Set temp ECDH params + EC_KEY *ecdh = 0; + ecdh = q_EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + q_SSL_CTX_set_tmp_ecdh(sslContext->ctx, ecdh); + q_EC_KEY_free(ecdh); + } +#endif // OPENSSL_NO_EC + +#if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK) + if (!client) + q_SSL_CTX_use_psk_identity_hint(sslContext->ctx, sslContext->sslConfiguration.preSharedKeyIdentityHint().constData()); +#endif // OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK) + + const QVector qcurves = sslContext->sslConfiguration.ellipticCurves(); + if (!qcurves.isEmpty()) { +#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(OPENSSL_NO_EC) + // Set the curves to be used + if (q_SSLeay() >= 0x10002000L) { + // SSL_CTX_ctrl wants a non-const pointer as last argument, + // but let's avoid a copy into a temporary array + if (!q_SSL_CTX_ctrl(sslContext->ctx, + SSL_CTRL_SET_CURVES, + qcurves.size(), + const_cast(reinterpret_cast(qcurves.data())))) { + sslContext->errorStr = msgErrorSettingEllipticCurves(QSslSocketBackendPrivate::getErrorsFromOpenSsl()); + sslContext->errorCode = QSslError::UnspecifiedError; + } + } else +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(OPENSSL_NO_EC) + { + // specific curves requested, but not possible to set -> error + sslContext->errorStr = msgErrorSettingEllipticCurves(QSslSocket::tr("OpenSSL version too old, need at least v1.0.2")); + sslContext->errorCode = QSslError::UnspecifiedError; + } + } +} + +QT_END_NAMESPACE diff --git a/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp b/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp index 90687b05c5..5ebad822f1 100644 --- a/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp +++ b/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2015 Mikkel Krautz +** Copyright (C) 2016 Richard J. Moore ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -50,8 +51,8 @@ #include #endif -// For q_BN_is_word. #include +#include QT_BEGIN_NAMESPACE @@ -62,13 +63,6 @@ static bool isSafeDH(DH *dh) QSslSocketPrivate::ensureInitialized(); - // Mark p < 1024 bits as unsafe. - if (q_BN_num_bits(dh->p) < 1024) { - return false; - } - - if (q_DH_check(dh, &status) != 1) - return false; // From https://wiki.openssl.org/index.php/Diffie-Hellman_parameters: // @@ -81,11 +75,39 @@ static bool isSafeDH(DH *dh) // Without the test, the IETF parameters would // fail validation. For details, see Diffie-Hellman // Parameter Check (when g = 2, must p mod 24 == 11?). +#if QT_CONFIG(opensslv11) + // Mark p < 1024 bits as unsafe. + if (q_DH_bits(dh) < 1024) + return false; + + if (q_DH_check(dh, &status) != 1) + return false; + + const BIGNUM *p = nullptr; + const BIGNUM *q = nullptr; + const BIGNUM *g = nullptr; + q_DH_get0_pqg(dh, &p, &q, &g); + + if (q_BN_is_word(const_cast(g), DH_GENERATOR_2)) { + long residue = q_BN_mod_word(p, 24); + if (residue == 11 || residue == 23) + status &= ~DH_NOT_SUITABLE_GENERATOR; + } + +#else + // Mark p < 1024 bits as unsafe. + if (q_BN_num_bits(dh->p) < 1024) + return false; + + if (q_DH_check(dh, &status) != 1) + return false; + if (q_BN_is_word(dh->g, DH_GENERATOR_2)) { long residue = q_BN_mod_word(dh->p, 24); if (residue == 11 || residue == 23) status &= ~DH_NOT_SUITABLE_GENERATOR; } +#endif bad |= DH_CHECK_P_NOT_PRIME; bad |= DH_CHECK_P_NOT_SAFE_PRIME; diff --git a/src/network/ssl/qsslellipticcurve.h b/src/network/ssl/qsslellipticcurve.h index 231566063e..57dda19bad 100644 --- a/src/network/ssl/qsslellipticcurve.h +++ b/src/network/ssl/qsslellipticcurve.h @@ -80,6 +80,7 @@ private: friend Q_DECL_CONSTEXPR bool operator==(QSslEllipticCurve lhs, QSslEllipticCurve rhs) Q_DECL_NOTHROW; friend Q_DECL_CONSTEXPR uint qHash(QSslEllipticCurve curve, uint seed) Q_DECL_NOTHROW; + friend class QSslContext; friend class QSslSocketPrivate; friend class QSslSocketBackendPrivate; }; diff --git a/src/network/ssl/qsslellipticcurve_openssl.cpp b/src/network/ssl/qsslellipticcurve_openssl.cpp index e18197b703..8cd14837f0 100644 --- a/src/network/ssl/qsslellipticcurve_openssl.cpp +++ b/src/network/ssl/qsslellipticcurve_openssl.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2014 Governikus GmbH & Co. KG. +** Copyright (C) 2016 Richard J. Moore ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -78,17 +79,18 @@ QSslEllipticCurve QSslEllipticCurve::fromShortName(const QString &name) QSslEllipticCurve result; #ifndef OPENSSL_NO_EC - const QByteArray curveNameLatin1 = name.toLatin1(); + const QByteArray curveNameLatin1 = name.toLatin1(); int nid = q_OBJ_sn2nid(curveNameLatin1.data()); #if OPENSSL_VERSION_NUMBER >= 0x10002000L - if (nid == 0 && q_SSLeay() >= 0x10002000L) + if (nid == 0 && QSslSocket::sslLibraryVersionNumber() >= 0x10002000L) nid = q_EC_curve_nist2nid(curveNameLatin1.data()); #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L result.id = nid; -#endif + +#endif // !OPENSSL_NO_EC return result; } diff --git a/src/network/ssl/qsslkey_openssl.cpp b/src/network/ssl/qsslkey_openssl.cpp index 79df33ecca..be7033c8aa 100644 --- a/src/network/ssl/qsslkey_openssl.cpp +++ b/src/network/ssl/qsslkey_openssl.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2016 Richard J. Moore ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -84,33 +85,30 @@ void QSslKeyPrivate::clear(bool deep) bool QSslKeyPrivate::fromEVP_PKEY(EVP_PKEY *pkey) { - if (pkey->type == EVP_PKEY_RSA) { +#if QT_CONFIG(opensslv11) + const int keyType = q_EVP_PKEY_type(q_EVP_PKEY_base_id(pkey)); +#else + const int keyType = pkey->type; +#endif + if (keyType == EVP_PKEY_RSA) { isNull = false; algorithm = QSsl::Rsa; type = QSsl::PrivateKey; - - rsa = q_RSA_new(); - memcpy(rsa, q_EVP_PKEY_get1_RSA(pkey), sizeof(RSA)); - + rsa = q_EVP_PKEY_get1_RSA(pkey); return true; - } - else if (pkey->type == EVP_PKEY_DSA) { + } else if (keyType == EVP_PKEY_DSA) { isNull = false; algorithm = QSsl::Dsa; type = QSsl::PrivateKey; - - dsa = q_DSA_new(); - memcpy(dsa, q_EVP_PKEY_get1_DSA(pkey), sizeof(DSA)); - + dsa = q_EVP_PKEY_get1_DSA(pkey); return true; } #ifndef OPENSSL_NO_EC - else if (pkey->type == EVP_PKEY_EC) { + else if (keyType == EVP_PKEY_EC) { isNull = false; algorithm = QSsl::Ec; type = QSsl::PrivateKey; - ec = q_EC_KEY_dup(q_EVP_PKEY_get1_EC_KEY(pkey)); - + ec = q_EVP_PKEY_get1_EC_KEY(pkey); return true; } #endif @@ -178,8 +176,8 @@ int QSslKeyPrivate::length() const return -1; switch (algorithm) { - case QSsl::Rsa: return q_BN_num_bits(rsa->n); - case QSsl::Dsa: return q_BN_num_bits(dsa->p); + case QSsl::Rsa: return q_RSA_bits(rsa); + case QSsl::Dsa: return q_DSA_bits(dsa); #ifndef OPENSSL_NO_EC case QSsl::Ec: return q_EC_GROUP_get_degree(q_EC_KEY_get0_group(ec)); #endif @@ -273,7 +271,13 @@ Qt::HANDLE QSslKeyPrivate::handle() const static QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv, int enc) { - EVP_CIPHER_CTX ctx; +#if QT_CONFIG(opensslv11) + EVP_CIPHER_CTX *ctx = q_EVP_CIPHER_CTX_new(); +#else + EVP_CIPHER_CTX evpCipherContext; + EVP_CIPHER_CTX *ctx = &evpCipherContext; +#endif + const EVP_CIPHER* type = 0; int i = 0, len = 0; @@ -291,21 +295,44 @@ static QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data, QByteArray output; output.resize(data.size() + EVP_MAX_BLOCK_LENGTH); - q_EVP_CIPHER_CTX_init(&ctx); - q_EVP_CipherInit(&ctx, type, NULL, NULL, enc); - q_EVP_CIPHER_CTX_set_key_length(&ctx, key.size()); + +#if QT_CONFIG(opensslv11) + q_EVP_CIPHER_CTX_reset(ctx); +#else + q_EVP_CIPHER_CTX_init(ctx); +#endif + + q_EVP_CipherInit(ctx, type, NULL, NULL, enc); + q_EVP_CIPHER_CTX_set_key_length(ctx, key.size()); if (cipher == QSslKeyPrivate::Rc2Cbc) - q_EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_SET_RC2_KEY_BITS, 8 * key.size(), NULL); - q_EVP_CipherInit(&ctx, NULL, + q_EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_SET_RC2_KEY_BITS, 8 * key.size(), NULL); + +#if QT_CONFIG(opensslv11) + // EVP_CipherInit in 1.1 resets the context thus making the calls above useless. + // We call EVP_CipherInit_ex instead. + q_EVP_CipherInit_ex(ctx, nullptr, nullptr, + reinterpret_cast(key.constData()), + reinterpret_cast(iv.constData()), + enc); +#else + q_EVP_CipherInit(ctx, NULL, reinterpret_cast(key.constData()), reinterpret_cast(iv.constData()), enc); - q_EVP_CipherUpdate(&ctx, +#endif // opensslv11 + + q_EVP_CipherUpdate(ctx, reinterpret_cast(output.data()), &len, reinterpret_cast(data.constData()), data.size()); - q_EVP_CipherFinal(&ctx, + q_EVP_CipherFinal(ctx, reinterpret_cast(output.data()) + len, &i); len += i; - q_EVP_CIPHER_CTX_cleanup(&ctx); + +#if QT_CONFIG(opensslv11) + q_EVP_CIPHER_CTX_reset(ctx); + q_EVP_CIPHER_CTX_free(ctx); +#else + q_EVP_CIPHER_CTX_cleanup(ctx); +#endif return output.left(len); } diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index b4bfaca8be..02bd4389af 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. ** Copyright (C) 2014 Governikus GmbH & Co. KG ** Contact: https://www.qt.io/licensing/ ** @@ -97,70 +97,6 @@ bool QSslSocketPrivate::s_loadRootCertsOnDemand = false; int QSslSocketBackendPrivate::s_indexForSSLExtraData = -1; #endif -/* \internal - - From OpenSSL's thread(3) manual page: - - OpenSSL can safely be used in multi-threaded applications provided that at - least two callback functions are set. - - locking_function(int mode, int n, const char *file, int line) is needed to - perform locking on shared data structures. (Note that OpenSSL uses a - number of global data structures that will be implicitly shared - whenever multiple threads use OpenSSL.) Multi-threaded - applications will crash at random if it is not set. ... - ... - id_function(void) is a function that returns a thread ID. It is not - needed on Windows nor on platforms where getpid() returns a different - ID for each thread (most notably Linux) -*/ -class QOpenSslLocks -{ -public: - inline QOpenSslLocks() - : initLocker(QMutex::Recursive), - locksLocker(QMutex::Recursive) - { - QMutexLocker locker(&locksLocker); - int numLocks = q_CRYPTO_num_locks(); - locks = new QMutex *[numLocks]; - memset(locks, 0, numLocks * sizeof(QMutex *)); - } - inline ~QOpenSslLocks() - { - QMutexLocker locker(&locksLocker); - for (int i = 0; i < q_CRYPTO_num_locks(); ++i) - delete locks[i]; - delete [] locks; - - QSslSocketPrivate::deinitialize(); - } - inline QMutex *lock(int num) - { - QMutexLocker locker(&locksLocker); - QMutex *tmp = locks[num]; - if (!tmp) - tmp = locks[num] = new QMutex(QMutex::Recursive); - return tmp; - } - - QMutex *globalLock() - { - return &locksLocker; - } - - QMutex *initLock() - { - return &initLocker; - } - -private: - QMutex initLocker; - QMutex locksLocker; - QMutex **locks; -}; -Q_GLOBAL_STATIC(QOpenSslLocks, openssl_locks) - QString QSslSocketBackendPrivate::getErrorsFromOpenSsl() { QString errorString; @@ -175,20 +111,6 @@ QString QSslSocketBackendPrivate::getErrorsFromOpenSsl() } extern "C" { -static void locking_function(int mode, int lockNumber, const char *, int) -{ - QMutex *mutex = openssl_locks()->lock(lockNumber); - - // Lock or unlock it - if (mode & CRYPTO_LOCK) - mutex->lock(); - else - mutex->unlock(); -} -static unsigned long id_function() -{ - return (quintptr)QThread::currentThreadId(); -} #if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK) static unsigned int q_ssl_psk_client_callback(SSL *ssl, @@ -212,48 +134,6 @@ static unsigned int q_ssl_psk_server_callback(SSL *ssl, #endif } // extern "C" -static void q_OpenSSL_add_all_algorithms_safe() -{ -#ifdef Q_OS_WIN - // Prior to version 1.0.1m an attempt to call OpenSSL_add_all_algorithms on - // Windows could result in 'exit' call from OPENSSL_config (QTBUG-43843). - // We can predict this and avoid OPENSSL_add_all_algorithms call. - // From OpenSSL docs: - // "An application does not need to add algorithms to use them explicitly, - // for example by EVP_sha1(). It just needs to add them if it (or any of - // the functions it calls) needs to lookup algorithms. - // The cipher and digest lookup functions are used in many parts of the - // library. If the table is not initialized several functions will - // misbehave and complain they cannot find algorithms. This includes the - // PEM, PKCS#12, SSL and S/MIME libraries. This is a common query in - // the OpenSSL mailing lists." - // - // Anyway, as a result, we chose not to call this function if it would exit. - - if (q_SSLeay() < 0x100010DFL) - { - // Now, before we try to call it, check if an attempt to open config file - // will result in exit: - if (char *confFileName = q_CONF_get1_default_config_file()) { - BIO *confFile = q_BIO_new_file(confFileName, "r"); - const auto lastError = q_ERR_peek_last_error(); - q_OPENSSL_free(confFileName); - if (confFile) { - q_BIO_free(confFile); - } else { - q_ERR_clear_error(); - if (ERR_GET_REASON(lastError) == ERR_R_SYS_LIB) { - qCWarning(lcSsl, "failed to open openssl.conf file"); - return; - } - } - } - } -#endif // Q_OS_WIN - - q_OpenSSL_add_all_algorithms(); -} - QSslSocketBackendPrivate::QSslSocketBackendPrivate() : ssl(0), readBio(0), @@ -269,7 +149,7 @@ QSslSocketBackendPrivate::~QSslSocketBackendPrivate() destroySslContext(); } -QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(SSL_CIPHER *cipher) +QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(const SSL_CIPHER *cipher) { QSslCipher ciph; @@ -310,7 +190,8 @@ QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(SSL_CIPHER *ciph } // static -inline QSslErrorEntry QSslErrorEntry::fromStoreContext(X509_STORE_CTX *ctx) { +inline QSslErrorEntry QSslErrorEntry::fromStoreContext(X509_STORE_CTX *ctx) +{ return { q_X509_STORE_CTX_get_error(ctx), q_X509_STORE_CTX_get_error_depth(ctx) @@ -324,6 +205,7 @@ struct QSslErrorList QMutex mutex; QVector errors; }; + Q_GLOBAL_STATIC(QSslErrorList, _q_sslErrorList) int q_X509Callback(int ok, X509_STORE_CTX *ctx) @@ -353,7 +235,7 @@ int q_X509Callback(int ok, X509_STORE_CTX *ctx) } #endif } - // Always return OK to allow verification to continue. We're handle the + // Always return OK to allow verification to continue. We handle the // errors gracefully after collecting all errors, after verification has // completed. return 1; @@ -438,7 +320,7 @@ bool QSslSocketBackendPrivate::initSslContext() if (configuration.protocol != QSsl::SslV2 && configuration.protocol != QSsl::SslV3 && configuration.protocol != QSsl::UnknownProtocol && - mode == QSslSocket::SslClientMode && q_SSLeay() >= 0x00090806fL) { + mode == QSslSocket::SslClientMode && QSslSocket::sslLibraryVersionNumber() >= 0x00090806fL) { // Set server hostname on TLS extension. RFC4366 section 3.1 requires it in ACE format. QString tlsHostName = verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName; if (tlsHostName.isEmpty()) @@ -479,13 +361,13 @@ bool QSslSocketBackendPrivate::initSslContext() #if OPENSSL_VERSION_NUMBER >= 0x10001000L // Save a pointer to this object into the SSL structure. - if (q_SSLeay() >= 0x10001000L) + if (QSslSocket::sslLibraryVersionNumber() >= 0x10001000L) q_SSL_set_ex_data(ssl, s_indexForSSLExtraData, this); #endif #if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK) // Set the client callback for PSK - if (q_SSLeay() >= 0x10001000L) { + if (QSslSocket::sslLibraryVersionNumber() >= 0x10001000L) { if (mode == QSslSocket::SslClientMode) q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_client_callback); else if (mode == QSslSocket::SslServerMode) @@ -505,16 +387,6 @@ void QSslSocketBackendPrivate::destroySslContext() sslContextPointer.clear(); } -/*! - \internal -*/ -void QSslSocketPrivate::deinitialize() -{ - q_CRYPTO_set_id_callback(0); - q_CRYPTO_set_locking_callback(0); - q_ERR_free_strings(); -} - /*! \internal @@ -527,91 +399,6 @@ bool QSslSocketPrivate::supportsSsl() return ensureLibraryLoaded(); } -bool QSslSocketPrivate::ensureLibraryLoaded() -{ - if (!q_resolveOpenSslSymbols()) - return false; - - // Check if the library itself needs to be initialized. - QMutexLocker locker(openssl_locks()->initLock()); - - if (!s_libraryLoaded) { - s_libraryLoaded = true; - - // Initialize OpenSSL. - q_CRYPTO_set_id_callback(id_function); - q_CRYPTO_set_locking_callback(locking_function); - if (q_SSL_library_init() != 1) - return false; - q_SSL_load_error_strings(); - q_OpenSSL_add_all_algorithms_safe(); - -#if OPENSSL_VERSION_NUMBER >= 0x10001000L - if (q_SSLeay() >= 0x10001000L) - QSslSocketBackendPrivate::s_indexForSSLExtraData = q_SSL_get_ex_new_index(0L, NULL, NULL, NULL, NULL); -#endif - - // Initialize OpenSSL's random seed. - if (!q_RAND_status()) { - qWarning("Random number generator not seeded, disabling SSL support"); - return false; - } - } - return true; -} - -void QSslSocketPrivate::ensureCiphersAndCertsLoaded() -{ - QMutexLocker locker(openssl_locks()->initLock()); - if (s_loadedCiphersAndCerts) - return; - s_loadedCiphersAndCerts = true; - - resetDefaultCiphers(); - resetDefaultEllipticCurves(); - -#if QT_CONFIG(library) - //load symbols needed to receive certificates from system store -#if defined(Q_OS_WIN) - HINSTANCE hLib = LoadLibraryW(L"Crypt32"); - if (hLib) { - ptrCertOpenSystemStoreW = (PtrCertOpenSystemStoreW)GetProcAddress(hLib, "CertOpenSystemStoreW"); - ptrCertFindCertificateInStore = (PtrCertFindCertificateInStore)GetProcAddress(hLib, "CertFindCertificateInStore"); - ptrCertCloseStore = (PtrCertCloseStore)GetProcAddress(hLib, "CertCloseStore"); - if (!ptrCertOpenSystemStoreW || !ptrCertFindCertificateInStore || !ptrCertCloseStore) - qCWarning(lcSsl, "could not resolve symbols in crypt32 library"); // should never happen - } else { - qCWarning(lcSsl, "could not load crypt32 library"); // should never happen - } -#elif defined(Q_OS_QNX) - s_loadRootCertsOnDemand = true; -#elif defined(Q_OS_UNIX) && !defined(Q_OS_MAC) - // check whether we can enable on-demand root-cert loading (i.e. check whether the sym links are there) - QList dirs = unixRootCertDirectories(); - QStringList symLinkFilter; - symLinkFilter << QLatin1String("[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]"); - for (int a = 0; a < dirs.count(); ++a) { - QDirIterator iterator(QLatin1String(dirs.at(a)), symLinkFilter, QDir::Files); - if (iterator.hasNext()) { - s_loadRootCertsOnDemand = true; - break; - } - } -#endif -#endif // QT_CONFIG(library) - // if on-demand loading was not enabled, load the certs now - if (!s_loadRootCertsOnDemand) - setDefaultCaCertificates(systemCaCertificates()); -#ifdef Q_OS_WIN - //Enabled for fetching additional root certs from windows update on windows 6+ - //This flag is set false by setDefaultCaCertificates() indicating the app uses - //its own cert bundle rather than the system one. - //Same logic that disables the unix on demand cert loading. - //Unlike unix, we do preload the certificates from the cert store. - if ((QSysInfo::windowsVersion() & QSysInfo::WV_NT_based) >= QSysInfo::WV_6_0) - s_loadRootCertsOnDemand = true; -#endif -} /*! \internal @@ -628,26 +415,6 @@ void QSslSocketPrivate::ensureInitialized() ensureCiphersAndCertsLoaded(); } -long QSslSocketPrivate::sslLibraryVersionNumber() -{ - if (!supportsSsl()) - return 0; - - return q_SSLeay(); -} - -QString QSslSocketPrivate::sslLibraryVersionString() -{ - if (!supportsSsl()) - return QString(); - - const char *versionString = q_SSLeay_version(SSLEAY_VERSION); - if (!versionString) - return QString(); - - return QString::fromLatin1(versionString); -} - long QSslSocketPrivate::sslLibraryBuildVersionNumber() { return OPENSSL_VERSION_NUMBER; @@ -669,7 +436,11 @@ QString QSslSocketPrivate::sslLibraryBuildVersionString() */ void QSslSocketPrivate::resetDefaultCiphers() { +#if QT_CONFIG(opensslv11) + SSL_CTX *myCtx = q_SSL_CTX_new(q_TLS_client_method()); +#else SSL_CTX *myCtx = q_SSL_CTX_new(q_SSLv23_client_method()); +#endif SSL *mySsl = q_SSL_new(myCtx); QList ciphers; @@ -705,7 +476,7 @@ void QSslSocketPrivate::resetDefaultEllipticCurves() QVector curves; #ifndef OPENSSL_NO_EC - const size_t curveCount = q_EC_get_builtin_curves(NULL, 0); + const size_t curveCount = q_EC_get_builtin_curves(nullptr, 0); QVarLengthArray builtinCurves(static_cast(curveCount)); @@ -739,13 +510,14 @@ QList QSslSocketPrivate::systemCaCertificates() if (ptrCertOpenSystemStoreW && ptrCertFindCertificateInStore && ptrCertCloseStore) { HCERTSTORE hSystemStore; hSystemStore = ptrCertOpenSystemStoreW(0, L"ROOT"); - if(hSystemStore) { - PCCERT_CONTEXT pc = NULL; - while(1) { - pc = ptrCertFindCertificateInStore( hSystemStore, X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, pc); - if(!pc) + if (hSystemStore) { + PCCERT_CONTEXT pc = nullptr; + while (1) { + pc = ptrCertFindCertificateInStore(hSystemStore, X509_ASN_ENCODING, 0, CERT_FIND_ANY, nullptr, pc); + if (!pc) break; - QByteArray der((const char *)(pc->pbCertEncoded), static_cast(pc->cbCertEncoded)); + QByteArray der(reinterpret_cast(pc->pbCertEncoded), + static_cast(pc->cbCertEncoded)); QSslCertificate cert(der, QSsl::Der); systemCerts.append(cert); } @@ -1543,14 +1315,8 @@ QSslCipher QSslSocketBackendPrivate::sessionCipher() const { if (!ssl) return QSslCipher(); -#if OPENSSL_VERSION_NUMBER >= 0x10000000L - // FIXME This is fairly evil, but needed to keep source level compatibility - // with the OpenSSL 0.9.x implementation at maximum -- some other functions - // don't take a const SSL_CIPHER* when they should - SSL_CIPHER *sessionCipher = const_cast(q_SSL_get_current_cipher(ssl)); -#else - SSL_CIPHER *sessionCipher = q_SSL_get_current_cipher(ssl); -#endif + + const SSL_CIPHER *sessionCipher = q_SSL_get_current_cipher(ssl); return sessionCipher ? QSslCipher_from_SSL_CIPHER(sessionCipher) : QSslCipher(); } @@ -1576,112 +1342,6 @@ QSsl::SslProtocol QSslSocketBackendPrivate::sessionProtocol() const return QSsl::UnknownProtocol; } -void QSslSocketBackendPrivate::continueHandshake() -{ - Q_Q(QSslSocket); - // if we have a max read buffer size, reset the plain socket's to match - if (readBufferMaxSize) - plainSocket->setReadBufferSize(readBufferMaxSize); - - if (q_SSL_ctrl((ssl), SSL_CTRL_GET_SESSION_REUSED, 0, NULL)) - configuration.peerSessionShared = true; - -#ifdef QT_DECRYPT_SSL_TRAFFIC - if (ssl->session && ssl->s3) { - const char *mk = reinterpret_cast(ssl->session->master_key); - QByteArray masterKey(mk, ssl->session->master_key_length); - const char *random = reinterpret_cast(ssl->s3->client_random); - QByteArray clientRandom(random, SSL3_RANDOM_SIZE); - - // different format, needed for e.g. older Wireshark versions: -// const char *sid = reinterpret_cast(ssl->session->session_id); -// QByteArray sessionID(sid, ssl->session->session_id_length); -// QByteArray debugLineRSA("RSA Session-ID:"); -// debugLineRSA.append(sessionID.toHex().toUpper()); -// debugLineRSA.append(" Master-Key:"); -// debugLineRSA.append(masterKey.toHex().toUpper()); -// debugLineRSA.append("\n"); - - QByteArray debugLineClientRandom("CLIENT_RANDOM "); - debugLineClientRandom.append(clientRandom.toHex().toUpper()); - debugLineClientRandom.append(" "); - debugLineClientRandom.append(masterKey.toHex().toUpper()); - debugLineClientRandom.append("\n"); - - QString sslKeyFile = QDir::tempPath() + QLatin1String("/qt-ssl-keys"); - QFile file(sslKeyFile); - if (!file.open(QIODevice::Append)) - qCWarning(lcSsl) << "could not open file" << sslKeyFile << "for appending"; - if (!file.write(debugLineClientRandom)) - qCWarning(lcSsl) << "could not write to file" << sslKeyFile; - file.close(); - } else { - qCWarning(lcSsl, "could not decrypt SSL traffic"); - } -#endif - - // Cache this SSL session inside the QSslContext - if (!(configuration.sslOptions & QSsl::SslOptionDisableSessionSharing)) { - if (!sslContextPointer->cacheSession(ssl)) { - sslContextPointer.clear(); // we could not cache the session - } else { - // Cache the session for permanent usage as well - if (!(configuration.sslOptions & QSsl::SslOptionDisableSessionPersistence)) { - if (!sslContextPointer->sessionASN1().isEmpty()) - configuration.sslSession = sslContextPointer->sessionASN1(); - configuration.sslSessionTicketLifeTimeHint = sslContextPointer->sessionTicketLifeTimeHint(); - } - } - } - -#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG) - - configuration.nextProtocolNegotiationStatus = sslContextPointer->npnContext().status; - if (sslContextPointer->npnContext().status == QSslConfiguration::NextProtocolNegotiationUnsupported) { - // we could not agree -> be conservative and use HTTP/1.1 - configuration.nextNegotiatedProtocol = QByteArrayLiteral("http/1.1"); - } else { - const unsigned char *proto = 0; - unsigned int proto_len = 0; -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - if (q_SSLeay() >= 0x10002000L) { - q_SSL_get0_alpn_selected(ssl, &proto, &proto_len); - if (proto_len && mode == QSslSocket::SslClientMode) { - // Client does not have a callback that sets it ... - configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNegotiated; - } - } - - if (!proto_len) { // Test if NPN was more lucky ... -#else - { -#endif - q_SSL_get0_next_proto_negotiated(ssl, &proto, &proto_len); - } - - if (proto_len) - configuration.nextNegotiatedProtocol = QByteArray(reinterpret_cast(proto), proto_len); - else - configuration.nextNegotiatedProtocol.clear(); - } -#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ... - -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - if (q_SSLeay() >= 0x10002000L && mode == QSslSocket::SslClientMode) { - EVP_PKEY *key; - if (q_SSL_get_server_tmp_key(ssl, &key)) - configuration.ephemeralServerKey = QSslKey(key, QSsl::PublicKey); - } -#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L ... - - connectionEncrypted = true; - emit q->encrypted(); - if (autoStartHandshake && pendingClose) { - pendingClose = false; - q->disconnectFromHost(); - } -} - QList QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509) { ensureInitialized(); @@ -1735,12 +1395,12 @@ QList QSslSocketBackendPrivate::verify(const QList & QMutexLocker sslErrorListMutexLocker(&_q_sslErrorList()->mutex); // Register a custom callback to get all verification errors. - X509_STORE_set_verify_cb_func(certStore, q_X509Callback); + q_X509_STORE_set_verify_cb(certStore, q_X509Callback); // Build the chain of intermediate certificates STACK_OF(X509) *intermediates = 0; if (certificateChain.length() > 1) { - intermediates = (STACK_OF(X509) *) q_sk_new_null(); + intermediates = (STACK_OF(X509) *) q_OPENSSL_sk_new_null(); if (!intermediates) { q_X509_STORE_free(certStore); @@ -1754,11 +1414,8 @@ QList QSslSocketBackendPrivate::verify(const QList & first = false; continue; } -#if OPENSSL_VERSION_NUMBER >= 0x10000000L - q_sk_push( (_STACK *)intermediates, reinterpret_cast(cert.handle())); -#else - q_sk_push( (STACK *)intermediates, reinterpret_cast(cert.handle())); -#endif + + q_OPENSSL_sk_push((OPENSSL_STACK *)intermediates, reinterpret_cast(cert.handle())); } } @@ -1782,11 +1439,7 @@ QList QSslSocketBackendPrivate::verify(const QList & (void) q_X509_verify_cert(storeContext); q_X509_STORE_CTX_free(storeContext); -#if OPENSSL_VERSION_NUMBER >= 0x10000000L - q_sk_free( (_STACK *) intermediates); -#else - q_sk_free( (STACK *) intermediates); -#endif + q_OPENSSL_sk_free((OPENSSL_STACK *)intermediates); // Now process the errors const auto errorList = std::move(_q_sslErrorList()->errors); @@ -1860,7 +1513,8 @@ bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device, // Convert to Qt types if (!key->d->fromEVP_PKEY(pkey)) { qCWarning(lcSsl, "Unable to convert private key"); - q_sk_pop_free(reinterpret_cast(ca), reinterpret_cast(q_sk_free)); + q_OPENSSL_sk_pop_free(reinterpret_cast(ca), + reinterpret_cast(q_OPENSSL_sk_free)); q_X509_free(x509); q_EVP_PKEY_free(pkey); q_PKCS12_free(p12); @@ -1875,7 +1529,11 @@ bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device, *caCertificates = QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates(ca); // Clean up - q_sk_pop_free(reinterpret_cast(ca), reinterpret_cast(q_sk_free)); + // TODO: verify ASAP, in the past we had sk_pop_free with q_OPENSSL_sk_free + // which seems to be blatantly wrong and even crashes with 1.1. + q_OPENSSL_sk_pop_free(reinterpret_cast(ca), + reinterpret_cast(q_X509_free)); + q_X509_free(x509); q_EVP_PKEY_free(pkey); q_PKCS12_free(p12); diff --git a/src/network/ssl/qsslsocket_openssl11.cpp b/src/network/ssl/qsslsocket_openssl11.cpp new file mode 100644 index 0000000000..b6d18943a5 --- /dev/null +++ b/src/network/ssl/qsslsocket_openssl11.cpp @@ -0,0 +1,285 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2014 Governikus GmbH & Co. KG +** Copyright (C) 2016 Richard J. Moore +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** In addition, as a special exception, the copyright holders listed above give +** permission to link the code of its release of Qt with the OpenSSL project's +** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the +** same license as the original version), and distribute the linked executables. +** +** You must comply with the GNU General Public License version 2 in all +** respects for all of the code used other than the "OpenSSL" code. If you +** modify this file, you may extend this exception to your version of the file, +** but you are not obligated to do so. If you do not wish to do so, delete +** this exception statement from your version of this file. +** +****************************************************************************/ + +//#define QT_DECRYPT_SSL_TRAFFIC + +#include "qssl_p.h" +#include "qsslsocket_openssl_p.h" +#include "qsslsocket_openssl_symbols_p.h" +#include "qsslsocket.h" +#include "qsslkey.h" + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC_WITH_ARGS(QMutex, qt_opensslInitMutex, (QMutex::Recursive)) + +/*! + \internal +*/ +void QSslSocketPrivate::deinitialize() +{ + // This function exists only for compatibility with the pre-11 code, + // where deinitialize() actually does some cleanup. To be discarded + // once we retire < 1.1. +} + +bool QSslSocketPrivate::ensureLibraryLoaded() +{ + if (!q_resolveOpenSslSymbols()) + return false; + + const QMutexLocker locker(qt_opensslInitMutex); + + if (!s_libraryLoaded) { + s_libraryLoaded = true; + + // Initialize OpenSSL. + if (q_OPENSSL_init_ssl(0, nullptr) != 1) + return false; + q_SSL_load_error_strings(); + q_OpenSSL_add_all_algorithms(); + + QSslSocketBackendPrivate::s_indexForSSLExtraData + = q_CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL, 0L, nullptr, nullptr, + nullptr, nullptr); + + // Initialize OpenSSL's random seed. + if (!q_RAND_status()) { + qWarning("Random number generator not seeded, disabling SSL support"); + return false; + } + } + return true; +} + +void QSslSocketPrivate::ensureCiphersAndCertsLoaded() +{ + const QMutexLocker locker(qt_opensslInitMutex); + + if (s_loadedCiphersAndCerts) + return; + s_loadedCiphersAndCerts = true; + + resetDefaultCiphers(); + resetDefaultEllipticCurves(); + +#if QT_CONFIG(library) + //load symbols needed to receive certificates from system store +#if defined(Q_OS_WIN) + HINSTANCE hLib = LoadLibraryW(L"Crypt32"); + if (hLib) { + ptrCertOpenSystemStoreW = (PtrCertOpenSystemStoreW)GetProcAddress(hLib, "CertOpenSystemStoreW"); + ptrCertFindCertificateInStore = (PtrCertFindCertificateInStore)GetProcAddress(hLib, "CertFindCertificateInStore"); + ptrCertCloseStore = (PtrCertCloseStore)GetProcAddress(hLib, "CertCloseStore"); + if (!ptrCertOpenSystemStoreW || !ptrCertFindCertificateInStore || !ptrCertCloseStore) + qCWarning(lcSsl, "could not resolve symbols in crypt32 library"); // should never happen + } else { + qCWarning(lcSsl, "could not load crypt32 library"); // should never happen + } +#elif defined(Q_OS_QNX) + s_loadRootCertsOnDemand = true; +#elif defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) + // check whether we can enable on-demand root-cert loading (i.e. check whether the sym links are there) + QList dirs = unixRootCertDirectories(); + QStringList symLinkFilter; + symLinkFilter << QLatin1String("[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]"); + for (int a = 0; a < dirs.count(); ++a) { + QDirIterator iterator(QLatin1String(dirs.at(a)), symLinkFilter, QDir::Files); + if (iterator.hasNext()) { + s_loadRootCertsOnDemand = true; + break; + } + } +#endif +#endif // QT_CONFIG(library) + // if on-demand loading was not enabled, load the certs now + if (!s_loadRootCertsOnDemand) + setDefaultCaCertificates(systemCaCertificates()); +#ifdef Q_OS_WIN + //Enabled for fetching additional root certs from windows update on windows 6+ + //This flag is set false by setDefaultCaCertificates() indicating the app uses + //its own cert bundle rather than the system one. + //Same logic that disables the unix on demand cert loading. + //Unlike unix, we do preload the certificates from the cert store. + if ((QSysInfo::windowsVersion() & QSysInfo::WV_NT_based) >= QSysInfo::WV_6_0) + s_loadRootCertsOnDemand = true; +#endif +} + +long QSslSocketPrivate::sslLibraryVersionNumber() +{ + if (!supportsSsl()) + return 0; + + return q_OpenSSL_version_num(); +} + +QString QSslSocketPrivate::sslLibraryVersionString() +{ + if (!supportsSsl()) + return QString(); + + const char *versionString = q_OpenSSL_version(OPENSSL_VERSION); + if (!versionString) + return QString(); + + return QString::fromLatin1(versionString); +} + +void QSslSocketBackendPrivate::continueHandshake() +{ + Q_Q(QSslSocket); + // if we have a max read buffer size, reset the plain socket's to match + if (readBufferMaxSize) + plainSocket->setReadBufferSize(readBufferMaxSize); + + if (q_SSL_session_reused(ssl)) + configuration.peerSessionShared = true; + +#ifdef QT_DECRYPT_SSL_TRAFFIC + if (q_SSL_get_session(ssl)) { + size_t master_key_len = q_SSL_SESSION_get_master_key(q_SSL_get_session(ssl), 0, 0); + size_t client_random_len = q_SSL_get_client_random(ssl, 0, 0); + QByteArray masterKey(int(master_key_len), 0); // Will not overflow + QByteArray clientRandom(int(client_random_len), 0); // Will not overflow + + q_SSL_SESSION_get_master_key(q_SSL_get_session(ssl), + reinterpret_cast(masterKey.data()), + masterKey.size()); + q_SSL_get_client_random(ssl, reinterpret_cast(clientRandom.data()), + clientRandom.size()); + + QByteArray debugLineClientRandom("CLIENT_RANDOM "); + debugLineClientRandom.append(clientRandom.toHex().toUpper()); + debugLineClientRandom.append(" "); + debugLineClientRandom.append(masterKey.toHex().toUpper()); + debugLineClientRandom.append("\n"); + + QString sslKeyFile = QDir::tempPath() + QLatin1String("/qt-ssl-keys"); + QFile file(sslKeyFile); + if (!file.open(QIODevice::Append)) + qCWarning(lcSsl) << "could not open file" << sslKeyFile << "for appending"; + if (!file.write(debugLineClientRandom)) + qCWarning(lcSsl) << "could not write to file" << sslKeyFile; + file.close(); + } else { + qCWarning(lcSsl, "could not decrypt SSL traffic"); + } +#endif + + // Cache this SSL session inside the QSslContext + if (!(configuration.sslOptions & QSsl::SslOptionDisableSessionSharing)) { + if (!sslContextPointer->cacheSession(ssl)) { + sslContextPointer.clear(); // we could not cache the session + } else { + // Cache the session for permanent usage as well + if (!(configuration.sslOptions & QSsl::SslOptionDisableSessionPersistence)) { + if (!sslContextPointer->sessionASN1().isEmpty()) + configuration.sslSession = sslContextPointer->sessionASN1(); + configuration.sslSessionTicketLifeTimeHint = sslContextPointer->sessionTicketLifeTimeHint(); + } + } + } + +#if !defined(OPENSSL_NO_NEXTPROTONEG) + + configuration.nextProtocolNegotiationStatus = sslContextPointer->npnContext().status; + if (sslContextPointer->npnContext().status == QSslConfiguration::NextProtocolNegotiationUnsupported) { + // we could not agree -> be conservative and use HTTP/1.1 + configuration.nextNegotiatedProtocol = QByteArrayLiteral("http/1.1"); + } else { + const unsigned char *proto = 0; + unsigned int proto_len = 0; + + q_SSL_get0_alpn_selected(ssl, &proto, &proto_len); + if (proto_len && mode == QSslSocket::SslClientMode) { + // Client does not have a callback that sets it ... + configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNegotiated; + } + + if (!proto_len) { // Test if NPN was more lucky ... + q_SSL_get0_next_proto_negotiated(ssl, &proto, &proto_len); + } + + if (proto_len) + configuration.nextNegotiatedProtocol = QByteArray(reinterpret_cast(proto), proto_len); + else + configuration.nextNegotiatedProtocol.clear(); + } +#endif // !defined(OPENSSL_NO_NEXTPROTONEG) + + if (mode == QSslSocket::SslClientMode) { + EVP_PKEY *key; + if (q_SSL_get_server_tmp_key(ssl, &key)) + configuration.ephemeralServerKey = QSslKey(key, QSsl::PublicKey); + } + + connectionEncrypted = true; + emit q->encrypted(); + if (autoStartHandshake && pendingClose) { + pendingClose = false; + q->disconnectFromHost(); + } +} + +QT_END_NAMESPACE diff --git a/src/network/ssl/qsslsocket_openssl11_symbols_p.h b/src/network/ssl/qsslsocket_openssl11_symbols_p.h new file mode 100644 index 0000000000..2980b3d23e --- /dev/null +++ b/src/network/ssl/qsslsocket_openssl11_symbols_p.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2014 BlackBerry Limited. All rights reserved. +** Copyright (C) 2016 Richard J. Moore +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** In addition, as a special exception, the copyright holders listed above give +** permission to link the code of its release of Qt with the OpenSSL project's +** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the +** same license as the original version), and distribute the linked executables. +** +** You must comply with the GNU General Public License version 2 in all +** respects for all of the code used other than the "OpenSSL" code. If you +** modify this file, you may extend this exception to your version of the file, +** but you are not obligated to do so. If you do not wish to do so, delete +** this exception statement from your version of this file. +** +****************************************************************************/ + +#ifndef QSSLSOCKET_OPENSSL11_SYMBOLS_P_H +#define QSSLSOCKET_OPENSSL11_SYMBOLS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +// Note: this file does not have QT_BEGIN_NAMESPACE/QT_END_NAMESPACE, it's done +// in qsslsocket_openssl_symbols_p.h. + +#ifndef QSSLSOCKET_OPENSSL_SYMBOLS_P_H +#error "You are not supposed to use this header file, include qsslsocket_openssl_symbols_p.h instead" +#endif + +const unsigned char * q_ASN1_STRING_get0_data(const ASN1_STRING *x); + +Q_AUTOTEST_EXPORT BIO *q_BIO_new(const BIO_METHOD *a); +Q_AUTOTEST_EXPORT const BIO_METHOD *q_BIO_s_mem(); + +int q_DSA_bits(DSA *a); +int q_EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX *c); +int q_EVP_PKEY_base_id(EVP_PKEY *a); +int q_RSA_bits(RSA *a); +int q_OPENSSL_sk_num(OPENSSL_STACK *a); +void q_OPENSSL_sk_pop_free(OPENSSL_STACK *a, void (*b)(void *)); +OPENSSL_STACK *q_OPENSSL_sk_new_null(); +void q_OPENSSL_sk_push(OPENSSL_STACK *st, void *data); +void q_OPENSSL_sk_free(OPENSSL_STACK *a); +void * q_OPENSSL_sk_value(OPENSSL_STACK *a, int b); +int q_SSL_session_reused(SSL *a); +unsigned long q_SSL_CTX_set_options(SSL_CTX *ctx, unsigned long op); +int q_OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings); +size_t q_SSL_get_client_random(SSL *a, unsigned char *out, size_t outlen); +size_t q_SSL_SESSION_get_master_key(const SSL_SESSION *session, unsigned char *out, size_t outlen); +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); +const SSL_METHOD *q_TLS_method(); +const SSL_METHOD *q_TLS_client_method(); +const SSL_METHOD *q_TLS_server_method(); +ASN1_TIME *q_X509_getm_notBefore(X509 *a); +ASN1_TIME *q_X509_getm_notAfter(X509 *a); + +long q_X509_get_version(X509 *a); +EVP_PKEY *q_X509_get_pubkey(X509 *a); +void q_X509_STORE_set_verify_cb(X509_STORE *ctx, X509_STORE_CTX_verify_cb verify_cb); +STACK_OF(X509) *q_X509_STORE_CTX_get0_chain(X509_STORE_CTX *ctx); +void q_DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g); +int q_DH_bits(DH *dh); + +# define q_SSL_load_error_strings() q_OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS \ + | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL) + +#define q_SKM_sk_num(type, st) ((int (*)(const STACK_OF(type) *))q_OPENSSL_sk_num)(st) +#define q_SKM_sk_value(type, st,i) ((type * (*)(const STACK_OF(type) *, int))q_OPENSSL_sk_value)(st, i) + +#define q_OPENSSL_add_all_algorithms_conf() q_OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \ + | OPENSSL_INIT_ADD_ALL_DIGESTS \ + | OPENSSL_INIT_LOAD_CONFIG, NULL) +#define q_OPENSSL_add_all_algorithms_noconf() q_OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \ + | OPENSSL_INIT_ADD_ALL_DIGESTS, NULL) + +int q_OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings); +void q_CRYPTO_free(void *str, const char *file, int line); + +long q_OpenSSL_version_num(); +const char *q_OpenSSL_version(int type); + +unsigned long q_SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *session); + +#endif diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h index b2adb3e547..7f9e884045 100644 --- a/src/network/ssl/qsslsocket_openssl_p.h +++ b/src/network/ssl/qsslsocket_openssl_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -98,8 +98,8 @@ #include #include -#if OPENSSL_VERSION_NUMBER >= 0x10000000L -typedef _STACK STACK; +#if QT_CONFIG(opensslv11) +#include #endif QT_BEGIN_NAMESPACE @@ -151,7 +151,7 @@ public: #endif Q_AUTOTEST_EXPORT static long setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions); - static QSslCipher QSslCipher_from_SSL_CIPHER(SSL_CIPHER *cipher); + static QSslCipher QSslCipher_from_SSL_CIPHER(const SSL_CIPHER *cipher); static QList STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509); static QList verify(const QList &certificateChain, const QString &hostName); static QString getErrorsFromOpenSsl(); diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp index 12d52fc082..0ef8bf6b5e 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -1,7 +1,8 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. ** Copyright (C) 2014 BlackBerry Limited. All rights reserved. +** Copyright (C) 2016 Richard J. Moore ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -136,53 +137,195 @@ void qsslSocketCannotResolveSymbolWarning(const char *functionName) #endif // QT_LINKED_OPENSSL +#if QT_CONFIG(opensslv11) + +// Below are the functions first introduced in version 1.1: + +DEFINEFUNC(const unsigned char *, ASN1_STRING_get0_data, const ASN1_STRING *a, a, return 0, return) +DEFINEFUNC2(int, OPENSSL_init_ssl, uint64_t opts, opts, const OPENSSL_INIT_SETTINGS *settings, settings, return 0, return) +DEFINEFUNC2(int, OPENSSL_init_crypto, uint64_t opts, opts, const OPENSSL_INIT_SETTINGS *settings, settings, return 0, return) +DEFINEFUNC(BIO *, BIO_new, const BIO_METHOD *a, a, return 0, return) +DEFINEFUNC(const BIO_METHOD *, BIO_s_mem, void, DUMMYARG, return 0, return) +DEFINEFUNC2(int, BN_is_word, BIGNUM *a, a, BN_ULONG w, w, return 0, return) +DEFINEFUNC(int, EVP_CIPHER_CTX_reset, EVP_CIPHER_CTX *c, c, return 0, return) +DEFINEFUNC(int, EVP_PKEY_base_id, EVP_PKEY *a, a, return NID_undef, return) +DEFINEFUNC(int, RSA_bits, RSA *a, a, return 0, return) +DEFINEFUNC(int, DSA_bits, DSA *a, a, return 0, return) +DEFINEFUNC(int, OPENSSL_sk_num, OPENSSL_STACK *a, a, return -1, return) +DEFINEFUNC2(void, OPENSSL_sk_pop_free, OPENSSL_STACK *a, a, void (*b)(void*), b, return, DUMMYARG) +DEFINEFUNC(OPENSSL_STACK *, OPENSSL_sk_new_null, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC2(void, OPENSSL_sk_push, OPENSSL_STACK *a, a, void *b, b, return, DUMMYARG) +DEFINEFUNC(void, OPENSSL_sk_free, OPENSSL_STACK *a, a, return, DUMMYARG) +DEFINEFUNC2(void *, OPENSSL_sk_value, OPENSSL_STACK *a, a, int b, b, return 0, return) +DEFINEFUNC(int, SSL_session_reused, SSL *a, a, return 0, return) +DEFINEFUNC2(unsigned long, SSL_CTX_set_options, SSL_CTX *ctx, ctx, unsigned long op, op, return 0, return) +DEFINEFUNC3(size_t, SSL_get_client_random, SSL *a, a, unsigned char *out, out, size_t outlen, outlen, return 0, return) +DEFINEFUNC3(size_t, SSL_SESSION_get_master_key, const SSL_SESSION *ses, ses, unsigned char *out, out, size_t outlen, outlen, return 0, return) +DEFINEFUNC6(int, CRYPTO_get_ex_new_index, int class_index, class_index, long argl, argl, void *argp, argp, CRYPTO_EX_new *new_func, new_func, CRYPTO_EX_dup *dup_func, dup_func, CRYPTO_EX_free *free_func, free_func, return -1, return) + +DEFINEFUNC(const SSL_METHOD *, TLS_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(const SSL_METHOD *, TLS_client_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(const SSL_METHOD *, TLS_server_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(ASN1_TIME *, X509_getm_notBefore, X509 *a, a, return 0, return) +DEFINEFUNC(ASN1_TIME *, X509_getm_notAfter, X509 *a, a, return 0, return) +DEFINEFUNC(long, X509_get_version, X509 *a, a, return -1, return) +DEFINEFUNC(EVP_PKEY *, X509_get_pubkey, X509 *a, a, return 0, return) +DEFINEFUNC2(void, X509_STORE_set_verify_cb, X509_STORE *a, a, X509_STORE_CTX_verify_cb verify_cb, verify_cb, return, DUMMYARG) +DEFINEFUNC(STACK_OF(X509) *, X509_STORE_CTX_get0_chain, X509_STORE_CTX *a, a, return 0, return) +DEFINEFUNC3(void, CRYPTO_free, void *str, str, const char *file, file, int line, line, return, DUMMYARG) +DEFINEFUNC(long, OpenSSL_version_num, void, DUMMYARG, return 0, return) +DEFINEFUNC(const char *, OpenSSL_version, int a, a, return 0, return) +DEFINEFUNC(unsigned long, SSL_SESSION_get_ticket_lifetime_hint, const SSL_SESSION *session, session, return 0, return) +DEFINEFUNC4(void, DH_get0_pqg, const DH *dh, dh, const BIGNUM **p, p, const BIGNUM **q, q, const BIGNUM **g, g, return, DUMMYARG) +DEFINEFUNC(int, DH_bits, DH *dh, dh, return 0, return) + +#else // QT_CONFIG(opensslv11) + +// Functions below are either deprecated or removed in OpenSSL >= 1.1: + +DEFINEFUNC(unsigned char *, ASN1_STRING_data, ASN1_STRING *a, a, return 0, return) + #ifdef SSLEAY_MACROS DEFINEFUNC3(void *, ASN1_dup, i2d_of_void *a, a, d2i_of_void *b, b, char *c, c, return 0, return) #endif +DEFINEFUNC2(BIO *, BIO_new_file, const char *filename, filename, const char *mode, mode, return 0, return) +DEFINEFUNC(void, ERR_clear_error, DUMMYARG, DUMMYARG, return, DUMMYARG) +DEFINEFUNC(BIO *, BIO_new, BIO_METHOD *a, a, return 0, return) +DEFINEFUNC(BIO_METHOD *, BIO_s_mem, void, DUMMYARG, return 0, return) +DEFINEFUNC(int, CRYPTO_num_locks, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(void, CRYPTO_set_locking_callback, void (*a)(int, int, const char *, int), a, return, DUMMYARG) +DEFINEFUNC(void, CRYPTO_set_id_callback, unsigned long (*a)(), a, return, DUMMYARG) +DEFINEFUNC(void, CRYPTO_free, void *a, a, return, DUMMYARG) +DEFINEFUNC(unsigned long, ERR_peek_last_error, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(void, ERR_free_strings, void, DUMMYARG, return, DUMMYARG) +DEFINEFUNC(void, EVP_CIPHER_CTX_cleanup, EVP_CIPHER_CTX *a, a, return, DUMMYARG) +DEFINEFUNC(void, EVP_CIPHER_CTX_init, EVP_CIPHER_CTX *a, a, return, DUMMYARG) + +#ifdef SSLEAY_MACROS +DEFINEFUNC6(void *, PEM_ASN1_read_bio, d2i_of_void *a, a, const char *b, b, BIO *c, c, void **d, d, pem_password_cb *e, e, void *f, f, return 0, return) +DEFINEFUNC6(void *, PEM_ASN1_write_bio, d2i_of_void *a, a, const char *b, b, BIO *c, c, void **d, d, pem_password_cb *e, e, void *f, f, return 0, return) +#endif // SSLEAY_MACROS + +DEFINEFUNC(int, sk_num, STACK *a, a, return -1, return) +DEFINEFUNC2(void, sk_pop_free, STACK *a, a, void (*b)(void*), b, return, DUMMYARG) + +#if OPENSSL_VERSION_NUMBER >= 0x10000000L +DEFINEFUNC(_STACK *, sk_new_null, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC2(void, sk_push, _STACK *a, a, void *b, b, return, DUMMYARG) +DEFINEFUNC(void, sk_free, _STACK *a, a, return, DUMMYARG) +DEFINEFUNC2(void *, sk_value, STACK *a, a, int b, b, return 0, return) +#else +DEFINEFUNC(STACK *, sk_new_null, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC2(void, sk_push, STACK *a, a, char *b, b, return, DUMMYARG) +DEFINEFUNC(void, sk_free, STACK *a, a, return, DUMMYARG) +DEFINEFUNC2(char *, sk_value, STACK *a, a, int b, b, return 0, return) +#endif // OPENSSL_VERSION_NUMBER >= 0x10000000L + +DEFINEFUNC(int, SSL_library_init, void, DUMMYARG, return -1, return) +DEFINEFUNC(void, SSL_load_error_strings, void, DUMMYARG, return, DUMMYARG) + +#if OPENSSL_VERSION_NUMBER >= 0x10001000L +DEFINEFUNC5(int, SSL_get_ex_new_index, long argl, argl, void *argp, argp, CRYPTO_EX_new *new_func, new_func, CRYPTO_EX_dup *dup_func, dup_func, CRYPTO_EX_free *free_func, free_func, return -1, return) +#endif // OPENSSL_VERSION_NUMBER >= 0x10001000L + +#if OPENSSL_VERSION_NUMBER >= 0x10000000L +#ifndef OPENSSL_NO_SSL2 +DEFINEFUNC(const SSL_METHOD *, SSLv2_client_method, DUMMYARG, DUMMYARG, return 0, return) +#endif +#ifndef OPENSSL_NO_SSL3_METHOD +DEFINEFUNC(const SSL_METHOD *, SSLv3_client_method, DUMMYARG, DUMMYARG, return 0, return) +#endif +DEFINEFUNC(const SSL_METHOD *, SSLv23_client_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(const SSL_METHOD *, TLSv1_client_method, DUMMYARG, DUMMYARG, return 0, return) +#if OPENSSL_VERSION_NUMBER >= 0x10001000L +DEFINEFUNC(const SSL_METHOD *, TLSv1_1_client_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(const SSL_METHOD *, TLSv1_2_client_method, DUMMYARG, DUMMYARG, return 0, return) +#endif +#ifndef OPENSSL_NO_SSL2 +DEFINEFUNC(const SSL_METHOD *, SSLv2_server_method, DUMMYARG, DUMMYARG, return 0, return) +#endif +#ifndef OPENSSL_NO_SSL3_METHOD +DEFINEFUNC(const SSL_METHOD *, SSLv3_server_method, DUMMYARG, DUMMYARG, return 0, return) +#endif +DEFINEFUNC(const SSL_METHOD *, SSLv23_server_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(const SSL_METHOD *, TLSv1_server_method, DUMMYARG, DUMMYARG, return 0, return) +#if OPENSSL_VERSION_NUMBER >= 0x10001000L +DEFINEFUNC(const SSL_METHOD *, TLSv1_1_server_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(const SSL_METHOD *, TLSv1_2_server_method, DUMMYARG, DUMMYARG, return 0, return) +#endif +#else +#ifndef OPENSSL_NO_SSL2 +DEFINEFUNC(SSL_METHOD *, SSLv2_client_method, DUMMYARG, DUMMYARG, return 0, return) +#endif +#ifndef OPENSSL_NO_SSL3_METHOD +DEFINEFUNC(SSL_METHOD *, SSLv3_client_method, DUMMYARG, DUMMYARG, return 0, return) +#endif +DEFINEFUNC(SSL_METHOD *, SSLv23_client_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(SSL_METHOD *, TLSv1_client_method, DUMMYARG, DUMMYARG, return 0, return) +#ifndef OPENSSL_NO_SSL2 +DEFINEFUNC(SSL_METHOD *, SSLv2_server_method, DUMMYARG, DUMMYARG, return 0, return) +#endif +#ifndef OPENSSL_NO_SSL3_METHOD +DEFINEFUNC(SSL_METHOD *, SSLv3_server_method, DUMMYARG, DUMMYARG, return 0, return) +#endif +DEFINEFUNC(SSL_METHOD *, SSLv23_server_method, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(SSL_METHOD *, TLSv1_server_method, DUMMYARG, DUMMYARG, return 0, return) +#endif + +DEFINEFUNC(STACK_OF(X509) *, X509_STORE_CTX_get_chain, X509_STORE_CTX *a, a, return 0, return) + +#ifdef SSLEAY_MACROS +DEFINEFUNC2(int, i2d_DSAPrivateKey, const DSA *a, a, unsigned char **b, b, return -1, return) +DEFINEFUNC2(int, i2d_RSAPrivateKey, const RSA *a, a, unsigned char **b, b, return -1, return) +#ifndef OPENSSL_NO_EC +DEFINEFUNC2(int, i2d_ECPrivateKey, const EC_KEY *a, a, unsigned char **b, b, return -1, return) +#endif +DEFINEFUNC3(RSA *, d2i_RSAPrivateKey, RSA **a, a, unsigned char **b, b, long c, c, return 0, return) +DEFINEFUNC3(DSA *, d2i_DSAPrivateKey, DSA **a, a, unsigned char **b, b, long c, c, return 0, return) +#ifndef OPENSSL_NO_EC +DEFINEFUNC3(EC_KEY *, d2i_ECPrivateKey, EC_KEY **a, a, unsigned char **b, b, long c, c, return 0, return) +#endif +#endif +DEFINEFUNC(char *, CONF_get1_default_config_file, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(void, OPENSSL_add_all_algorithms_noconf, void, DUMMYARG, return, DUMMYARG) +DEFINEFUNC(void, OPENSSL_add_all_algorithms_conf, void, DUMMYARG, return, DUMMYARG) +DEFINEFUNC(long, SSLeay, void, DUMMYARG, return 0, return) +DEFINEFUNC(const char *, SSLeay_version, int a, a, return 0, return) + +#endif // QT_CONFIG(opensslv11) + DEFINEFUNC(long, ASN1_INTEGER_get, ASN1_INTEGER *a, a, return 0, return) -DEFINEFUNC(unsigned char *, ASN1_STRING_data, ASN1_STRING *a, a, return 0, return) DEFINEFUNC(int, ASN1_STRING_length, ASN1_STRING *a, a, return 0, return) -DEFINEFUNC2(int, ASN1_STRING_to_UTF8, unsigned char **a, a, ASN1_STRING *b, b, return 0, return); +DEFINEFUNC2(int, ASN1_STRING_to_UTF8, unsigned char **a, a, ASN1_STRING *b, b, return 0, return) DEFINEFUNC4(long, BIO_ctrl, BIO *a, a, int b, b, long c, c, void *d, d, return -1, return) -DEFINEFUNC2(BIO *, BIO_new_file, const char *filename, filename, const char *mode, mode, return 0, return) -DEFINEFUNC(void, ERR_clear_error, DUMMYARG, DUMMYARG, return, DUMMYARG) -DEFINEFUNC(void, OPENSSL_free, void *ptr, ptr, return, DUMMYARG) DEFINEFUNC(int, BIO_free, BIO *a, a, return 0, return) -DEFINEFUNC(BIO *, BIO_new, BIO_METHOD *a, a, return 0, return) DEFINEFUNC2(BIO *, BIO_new_mem_buf, void *a, a, int b, b, return 0, return) DEFINEFUNC3(int, BIO_read, BIO *a, a, void *b, b, int c, c, return -1, return) -DEFINEFUNC(BIO_METHOD *, BIO_s_mem, void, DUMMYARG, return 0, return) + DEFINEFUNC3(int, BIO_write, BIO *a, a, const void *b, b, int c, c, return -1, return) DEFINEFUNC(int, BN_num_bits, const BIGNUM *a, a, return 0, return) -#if OPENSSL_VERSION_NUMBER >= 0x10100000L -DEFINEFUNC2(int, BN_is_word, BIGNUM *a, a, BN_ULONG w, w, return 0, return) -#endif DEFINEFUNC2(BN_ULONG, BN_mod_word, const BIGNUM *a, a, BN_ULONG w, w, return static_cast(-1), return) #ifndef OPENSSL_NO_EC DEFINEFUNC(const EC_GROUP*, EC_KEY_get0_group, const EC_KEY* k, k, return 0, return) DEFINEFUNC(int, EC_GROUP_get_degree, const EC_GROUP* g, g, return 0, return) #endif -DEFINEFUNC(int, CRYPTO_num_locks, DUMMYARG, DUMMYARG, return 0, return) -DEFINEFUNC(void, CRYPTO_set_locking_callback, void (*a)(int, int, const char *, int), a, return, DUMMYARG) -DEFINEFUNC(void, CRYPTO_set_id_callback, unsigned long (*a)(), a, return, DUMMYARG) -DEFINEFUNC(void, CRYPTO_free, void *a, a, return, DUMMYARG) DEFINEFUNC(DSA *, DSA_new, DUMMYARG, DUMMYARG, return 0, return) DEFINEFUNC(void, DSA_free, DSA *a, a, return, DUMMYARG) DEFINEFUNC3(X509 *, d2i_X509, X509 **a, a, const unsigned char **b, b, long c, c, return 0, return) DEFINEFUNC2(char *, ERR_error_string, unsigned long a, a, char *b, b, return 0, return) DEFINEFUNC(unsigned long, ERR_get_error, DUMMYARG, DUMMYARG, return 0, return) -DEFINEFUNC(unsigned long, ERR_peek_last_error, DUMMYARG, DUMMYARG, return 0, return) -DEFINEFUNC(void, ERR_free_strings, void, DUMMYARG, return, DUMMYARG) -DEFINEFUNC(void, EVP_CIPHER_CTX_cleanup, EVP_CIPHER_CTX *a, a, return, DUMMYARG) -DEFINEFUNC(void, EVP_CIPHER_CTX_init, EVP_CIPHER_CTX *a, a, return, DUMMYARG) -DEFINEFUNC4(int, EVP_CIPHER_CTX_ctrl, EVP_CIPHER_CTX *ctx, ctx, int type, type, int arg, arg, void *ptr, ptr, return 0, return); +DEFINEFUNC(EVP_CIPHER_CTX *, EVP_CIPHER_CTX_new, void, DUMMYARG, return 0, return) +DEFINEFUNC(void, EVP_CIPHER_CTX_free, EVP_CIPHER_CTX *a, a, return, DUMMYARG) +DEFINEFUNC4(int, EVP_CIPHER_CTX_ctrl, EVP_CIPHER_CTX *ctx, ctx, int type, type, int arg, arg, void *ptr, ptr, return 0, return) DEFINEFUNC2(int, EVP_CIPHER_CTX_set_key_length, EVP_CIPHER_CTX *ctx, ctx, int keylen, keylen, return 0, return) -DEFINEFUNC5(int, EVP_CipherInit, EVP_CIPHER_CTX *ctx, ctx, const EVP_CIPHER *type, type, const unsigned char *key, key, const unsigned char *iv, iv, int enc, enc, return 0, return); -DEFINEFUNC5(int, EVP_CipherUpdate, EVP_CIPHER_CTX *ctx, ctx, unsigned char *out, out, int *outl, outl, const unsigned char *in, in, int inl, inl, return 0, return); -DEFINEFUNC3(int, EVP_CipherFinal, EVP_CIPHER_CTX *ctx, ctx, unsigned char *out, out, int *outl, outl, return 0, return); +DEFINEFUNC5(int, EVP_CipherInit, EVP_CIPHER_CTX *ctx, ctx, const EVP_CIPHER *type, type, const unsigned char *key, key, const unsigned char *iv, iv, int enc, enc, return 0, return) +DEFINEFUNC6(int, EVP_CipherInit_ex, EVP_CIPHER_CTX *ctx, ctx, const EVP_CIPHER *cipher, cipher, ENGINE *impl, impl, const unsigned char *key, key, const unsigned char *iv, iv, int enc, enc, return 0, return) +DEFINEFUNC5(int, EVP_CipherUpdate, EVP_CIPHER_CTX *ctx, ctx, unsigned char *out, out, int *outl, outl, const unsigned char *in, in, int inl, inl, return 0, return) +DEFINEFUNC3(int, EVP_CipherFinal, EVP_CIPHER_CTX *ctx, ctx, unsigned char *out, out, int *outl, outl, return 0, return) DEFINEFUNC(const EVP_CIPHER *, EVP_des_cbc, DUMMYARG, DUMMYARG, return 0, return) DEFINEFUNC(const EVP_CIPHER *, EVP_des_ede3_cbc, DUMMYARG, DUMMYARG, return 0, return) DEFINEFUNC(const EVP_CIPHER *, EVP_rc2_cbc, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC(const EVP_MD *, EVP_sha1, DUMMYARG, DUMMYARG, return 0, return) DEFINEFUNC3(int, EVP_PKEY_assign, EVP_PKEY *a, a, int b, b, char *c, c, return -1, return) DEFINEFUNC2(int, EVP_PKEY_set1_RSA, EVP_PKEY *a, a, RSA *b, b, return -1, return) DEFINEFUNC2(int, EVP_PKEY_set1_DSA, EVP_PKEY *a, a, DSA *b, b, return -1, return) @@ -206,10 +349,8 @@ DEFINEFUNC3(int, i2t_ASN1_OBJECT, char *a, a, int b, b, ASN1_OBJECT *c, c, retur DEFINEFUNC4(int, OBJ_obj2txt, char *a, a, int b, b, ASN1_OBJECT *c, c, int d, d, return -1, return) DEFINEFUNC(int, OBJ_obj2nid, const ASN1_OBJECT *a, a, return NID_undef, return) -#ifdef SSLEAY_MACROS -DEFINEFUNC6(void *, PEM_ASN1_read_bio, d2i_of_void *a, a, const char *b, b, BIO *c, c, void **d, d, pem_password_cb *e, e, void *f, f, return 0, return) -DEFINEFUNC6(void *, PEM_ASN1_write_bio, d2i_of_void *a, a, const char *b, b, BIO *c, c, void **d, d, pem_password_cb *e, e, void *f, f, return 0, return) -#else + +#ifndef SSLEAY_MACROS DEFINEFUNC4(EVP_PKEY *, PEM_read_bio_PrivateKey, BIO *a, a, EVP_PKEY **b, b, pem_password_cb *c, c, void *d, d, return 0, return) DEFINEFUNC4(DSA *, PEM_read_bio_DSAPrivateKey, BIO *a, a, DSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return) DEFINEFUNC4(RSA *, PEM_read_bio_RSAPrivateKey, BIO *a, a, RSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return) @@ -222,7 +363,7 @@ DEFINEFUNC7(int, PEM_write_bio_RSAPrivateKey, BIO *a, a, RSA *b, b, const EVP_CI #ifndef OPENSSL_NO_EC DEFINEFUNC7(int, PEM_write_bio_ECPrivateKey, BIO *a, a, EC_KEY *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return) #endif -#endif +#endif // !SSLEAY_MACROS DEFINEFUNC4(EVP_PKEY *, PEM_read_bio_PUBKEY, BIO *a, a, EVP_PKEY **b, b, pem_password_cb *c, c, void *d, d, return 0, return) DEFINEFUNC4(DSA *, PEM_read_bio_DSA_PUBKEY, BIO *a, a, DSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return) DEFINEFUNC4(RSA *, PEM_read_bio_RSA_PUBKEY, BIO *a, a, RSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return) @@ -238,23 +379,10 @@ DEFINEFUNC2(void, RAND_seed, const void *a, a, int b, b, return, DUMMYARG) DEFINEFUNC(int, RAND_status, void, DUMMYARG, return -1, return) DEFINEFUNC(RSA *, RSA_new, DUMMYARG, DUMMYARG, return 0, return) DEFINEFUNC(void, RSA_free, RSA *a, a, return, DUMMYARG) -DEFINEFUNC(int, sk_num, STACK *a, a, return -1, return) -DEFINEFUNC2(void, sk_pop_free, STACK *a, a, void (*b)(void*), b, return, DUMMYARG) -#if OPENSSL_VERSION_NUMBER >= 0x10000000L -DEFINEFUNC(_STACK *, sk_new_null, DUMMYARG, DUMMYARG, return 0, return) -DEFINEFUNC2(void, sk_push, _STACK *a, a, void *b, b, return, DUMMYARG) -DEFINEFUNC(void, sk_free, _STACK *a, a, return, DUMMYARG) -DEFINEFUNC2(void *, sk_value, STACK *a, a, int b, b, return 0, return) -#else -DEFINEFUNC(STACK *, sk_new_null, DUMMYARG, DUMMYARG, return 0, return) -DEFINEFUNC2(void, sk_push, STACK *a, a, char *b, b, return, DUMMYARG) -DEFINEFUNC(void, sk_free, STACK *a, a, return, DUMMYARG) -DEFINEFUNC2(char *, sk_value, STACK *a, a, int b, b, return 0, return) -#endif DEFINEFUNC(int, SSL_accept, SSL *a, a, return -1, return) DEFINEFUNC(int, SSL_clear, SSL *a, a, return -1, return) -DEFINEFUNC3(char *, SSL_CIPHER_description, SSL_CIPHER *a, a, char *b, b, int c, c, return 0, return) -DEFINEFUNC2(int, SSL_CIPHER_get_bits, SSL_CIPHER *a, a, int *b, b, return 0, return) +DEFINEFUNC3(char *, SSL_CIPHER_description, const SSL_CIPHER *a, a, char *b, b, int c, c, return 0, return) +DEFINEFUNC2(int, SSL_CIPHER_get_bits, const SSL_CIPHER *a, a, int *b, b, return 0, return) DEFINEFUNC(int, SSL_connect, SSL *a, a, return -1, return) DEFINEFUNC(int, SSL_CTX_check_private_key, const SSL_CTX *a, a, return -1, return) DEFINEFUNC4(long, SSL_CTX_ctrl, SSL_CTX *a, a, int b, b, long c, c, void *d, d, return -1, return) @@ -291,8 +419,6 @@ DEFINEFUNC(long, SSL_get_verify_result, const SSL *a, a, return -1, return) #else DEFINEFUNC(long, SSL_get_verify_result, SSL *a, a, return -1, return) #endif -DEFINEFUNC(int, SSL_library_init, void, DUMMYARG, return -1, return) -DEFINEFUNC(void, SSL_load_error_strings, void, DUMMYARG, return, DUMMYARG) DEFINEFUNC(SSL *, SSL_new, SSL_CTX *a, a, return 0, return) DEFINEFUNC4(long, SSL_ctrl, SSL *a, a, int cmd, cmd, long larg, larg, void *parg, parg, return -1, return) DEFINEFUNC3(int, SSL_read, SSL *a, a, void *b, b, int c, c, return -1, return) @@ -305,7 +431,6 @@ DEFINEFUNC(void, SSL_SESSION_free, SSL_SESSION *ses, ses, return, DUMMYARG) DEFINEFUNC(SSL_SESSION*, SSL_get1_session, SSL *ssl, ssl, return 0, return) DEFINEFUNC(SSL_SESSION*, SSL_get_session, const SSL *ssl, ssl, return 0, return) #if OPENSSL_VERSION_NUMBER >= 0x10001000L -DEFINEFUNC5(int, SSL_get_ex_new_index, long argl, argl, void *argp, argp, CRYPTO_EX_new *new_func, new_func, CRYPTO_EX_dup *dup_func, dup_func, CRYPTO_EX_free *free_func, free_func, return -1, return) DEFINEFUNC3(int, SSL_set_ex_data, SSL *ssl, ssl, int idx, idx, void *arg, arg, return 0, return) DEFINEFUNC2(void *, SSL_get_ex_data, const SSL *ssl, ssl, int idx, idx, return NULL, return) #endif @@ -314,51 +439,9 @@ DEFINEFUNC2(void, SSL_set_psk_client_callback, SSL* ssl, ssl, q_psk_client_callb DEFINEFUNC2(void, SSL_set_psk_server_callback, SSL* ssl, ssl, q_psk_server_callback_t callback, callback, return, DUMMYARG) DEFINEFUNC2(int, SSL_CTX_use_psk_identity_hint, SSL_CTX* ctx, ctx, const char *hint, hint, return 0, return) #endif -#if OPENSSL_VERSION_NUMBER >= 0x10000000L -#ifndef OPENSSL_NO_SSL2 -DEFINEFUNC(const SSL_METHOD *, SSLv2_client_method, DUMMYARG, DUMMYARG, return 0, return) -#endif -#ifndef OPENSSL_NO_SSL3_METHOD -DEFINEFUNC(const SSL_METHOD *, SSLv3_client_method, DUMMYARG, DUMMYARG, return 0, return) -#endif -DEFINEFUNC(const SSL_METHOD *, SSLv23_client_method, DUMMYARG, DUMMYARG, return 0, return) -DEFINEFUNC(const SSL_METHOD *, TLSv1_client_method, DUMMYARG, DUMMYARG, return 0, return) -#if OPENSSL_VERSION_NUMBER >= 0x10001000L -DEFINEFUNC(const SSL_METHOD *, TLSv1_1_client_method, DUMMYARG, DUMMYARG, return 0, return) -DEFINEFUNC(const SSL_METHOD *, TLSv1_2_client_method, DUMMYARG, DUMMYARG, return 0, return) -#endif -#ifndef OPENSSL_NO_SSL2 -DEFINEFUNC(const SSL_METHOD *, SSLv2_server_method, DUMMYARG, DUMMYARG, return 0, return) -#endif -#ifndef OPENSSL_NO_SSL3_METHOD -DEFINEFUNC(const SSL_METHOD *, SSLv3_server_method, DUMMYARG, DUMMYARG, return 0, return) -#endif -DEFINEFUNC(const SSL_METHOD *, SSLv23_server_method, DUMMYARG, DUMMYARG, return 0, return) -DEFINEFUNC(const SSL_METHOD *, TLSv1_server_method, DUMMYARG, DUMMYARG, return 0, return) -#if OPENSSL_VERSION_NUMBER >= 0x10001000L -DEFINEFUNC(const SSL_METHOD *, TLSv1_1_server_method, DUMMYARG, DUMMYARG, return 0, return) -DEFINEFUNC(const SSL_METHOD *, TLSv1_2_server_method, DUMMYARG, DUMMYARG, return 0, return) -#endif -#else -#ifndef OPENSSL_NO_SSL2 -DEFINEFUNC(SSL_METHOD *, SSLv2_client_method, DUMMYARG, DUMMYARG, return 0, return) -#endif -#ifndef OPENSSL_NO_SSL3_METHOD -DEFINEFUNC(SSL_METHOD *, SSLv3_client_method, DUMMYARG, DUMMYARG, return 0, return) -#endif -DEFINEFUNC(SSL_METHOD *, SSLv23_client_method, DUMMYARG, DUMMYARG, return 0, return) -DEFINEFUNC(SSL_METHOD *, TLSv1_client_method, DUMMYARG, DUMMYARG, return 0, return) -#ifndef OPENSSL_NO_SSL2 -DEFINEFUNC(SSL_METHOD *, SSLv2_server_method, DUMMYARG, DUMMYARG, return 0, return) -#endif -#ifndef OPENSSL_NO_SSL3_METHOD -DEFINEFUNC(SSL_METHOD *, SSLv3_server_method, DUMMYARG, DUMMYARG, return 0, return) -#endif -DEFINEFUNC(SSL_METHOD *, SSLv23_server_method, DUMMYARG, DUMMYARG, return 0, return) -DEFINEFUNC(SSL_METHOD *, TLSv1_server_method, DUMMYARG, DUMMYARG, return 0, return) -#endif DEFINEFUNC3(int, SSL_write, SSL *a, a, const void *b, b, int c, c, return -1, return) DEFINEFUNC2(int, X509_cmp, X509 *a, a, X509 *b, b, return -1, return) +DEFINEFUNC4(int, X509_digest, const X509 *x509, x509, const EVP_MD *type, type, unsigned char *md, md, unsigned int *len, len, return -1, return) #ifndef SSLEAY_MACROS DEFINEFUNC(X509 *, X509_dup, X509 *a, a, return 0, return) #endif @@ -382,6 +465,7 @@ DEFINEFUNC2(int, ASN1_STRING_print, BIO *a, a, ASN1_STRING *b, b, return 0, retu DEFINEFUNC2(int, X509_check_issued, X509 *a, a, X509 *b, b, return -1, return) DEFINEFUNC(X509_NAME *, X509_get_issuer_name, X509 *a, a, return 0, return) DEFINEFUNC(X509_NAME *, X509_get_subject_name, X509 *a, a, return 0, return) +DEFINEFUNC(ASN1_INTEGER *, X509_get_serialNumber, X509 *a, a, return 0, return) DEFINEFUNC(int, X509_verify_cert, X509_STORE_CTX *a, a, return -1, return) DEFINEFUNC(int, X509_NAME_entry_count, X509_NAME *a, a, return 0, return) DEFINEFUNC2(X509_NAME_ENTRY *, X509_NAME_get_entry, X509_NAME *a, a, int b, b, return 0, return) @@ -397,26 +481,8 @@ DEFINEFUNC2(int, X509_STORE_CTX_set_purpose, X509_STORE_CTX *a, a, int b, b, ret DEFINEFUNC(int, X509_STORE_CTX_get_error, X509_STORE_CTX *a, a, return -1, return) DEFINEFUNC(int, X509_STORE_CTX_get_error_depth, X509_STORE_CTX *a, a, return -1, return) DEFINEFUNC(X509 *, X509_STORE_CTX_get_current_cert, X509_STORE_CTX *a, a, return 0, return) -DEFINEFUNC(STACK_OF(X509) *, X509_STORE_CTX_get_chain, X509_STORE_CTX *a, a, return 0, return) DEFINEFUNC(X509_STORE_CTX *, X509_STORE_CTX_new, DUMMYARG, DUMMYARG, return 0, return) -#ifdef SSLEAY_MACROS -DEFINEFUNC2(int, i2d_DSAPrivateKey, const DSA *a, a, unsigned char **b, b, return -1, return) -DEFINEFUNC2(int, i2d_RSAPrivateKey, const RSA *a, a, unsigned char **b, b, return -1, return) -#ifndef OPENSSL_NO_EC -DEFINEFUNC2(int, i2d_ECPrivateKey, const EC_KEY *a, a, unsigned char **b, b, return -1, return) -#endif -DEFINEFUNC3(RSA *, d2i_RSAPrivateKey, RSA **a, a, unsigned char **b, b, long c, c, return 0, return) -DEFINEFUNC3(DSA *, d2i_DSAPrivateKey, DSA **a, a, unsigned char **b, b, long c, c, return 0, return) -#ifndef OPENSSL_NO_EC -DEFINEFUNC3(EC_KEY *, d2i_ECPrivateKey, EC_KEY **a, a, unsigned char **b, b, long c, c, return 0, return) -#endif -#endif -DEFINEFUNC(char *, CONF_get1_default_config_file, DUMMYARG, DUMMYARG, return 0, return) -DEFINEFUNC(void, OPENSSL_add_all_algorithms_noconf, void, DUMMYARG, return, DUMMYARG) -DEFINEFUNC(void, OPENSSL_add_all_algorithms_conf, void, DUMMYARG, return, DUMMYARG) DEFINEFUNC3(int, SSL_CTX_load_verify_locations, SSL_CTX *ctx, ctx, const char *CAfile, CAfile, const char *CApath, CApath, return 0, return) -DEFINEFUNC(long, SSLeay, void, DUMMYARG, return 0, return) -DEFINEFUNC(const char *, SSLeay_version, int a, a, return 0, return) DEFINEFUNC2(int, i2d_SSL_SESSION, SSL_SESSION *in, in, unsigned char **pp, pp, return 0, return) DEFINEFUNC3(SSL_SESSION *, d2i_SSL_SESSION, SSL_SESSION **a, a, const unsigned char **pp, pp, long length, length, return 0, return) #if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG) @@ -699,8 +765,8 @@ static QPair loadOpenSsl() #ifndef Q_OS_DARWIN // second attempt: find the development files libssl.so and libcrypto.so // - // disabled on OS X/iOS: - // OS X's /usr/lib/libssl.dylib, /usr/lib/libcrypto.dylib will be picked up in the third + // disabled on macOS/iOS: + // macOS's /usr/lib/libssl.dylib, /usr/lib/libcrypto.dylib will be picked up in the third // attempt, _after_ /Contents/Frameworks has been searched. // iOS does not ship a system libssl.dylib, libcrypto.dylib in the first place. libssl->setFileNameAndVersion(QLatin1String("ssl"), -1); @@ -759,7 +825,11 @@ bool q_resolveOpenSslSymbols() static bool symbolsResolved = false; static bool triedToResolveSymbols = false; #ifndef QT_NO_THREAD +#if QT_CONFIG(opensslv11) + QMutexLocker locker(QMutexPool::globalInstanceGet((void *)&q_OPENSSL_init_ssl)); +#else QMutexLocker locker(QMutexPool::globalInstanceGet((void *)&q_SSL_library_init)); +#endif #endif if (symbolsResolved) return true; @@ -776,17 +846,148 @@ bool q_resolveOpenSslSymbols() // failed to load them return false; +#if QT_CONFIG(opensslv11) + + RESOLVEFUNC(OPENSSL_init_ssl) + RESOLVEFUNC(OPENSSL_init_crypto) + RESOLVEFUNC(ASN1_STRING_get0_data) + RESOLVEFUNC(EVP_CIPHER_CTX_reset) + RESOLVEFUNC(EVP_PKEY_base_id) + RESOLVEFUNC(RSA_bits) + RESOLVEFUNC(OPENSSL_sk_new_null) + RESOLVEFUNC(OPENSSL_sk_push) + RESOLVEFUNC(OPENSSL_sk_free) + RESOLVEFUNC(OPENSSL_sk_num) + RESOLVEFUNC(OPENSSL_sk_pop_free) + RESOLVEFUNC(OPENSSL_sk_value) + RESOLVEFUNC(DH_get0_pqg) + RESOLVEFUNC(SSL_CTX_set_options) + RESOLVEFUNC(SSL_get_client_random) + RESOLVEFUNC(SSL_SESSION_get_master_key) + RESOLVEFUNC(SSL_session_reused) + RESOLVEFUNC(SSL_get_session) + RESOLVEFUNC(CRYPTO_get_ex_new_index) + RESOLVEFUNC(TLS_method) + RESOLVEFUNC(TLS_client_method) + RESOLVEFUNC(TLS_server_method) + RESOLVEFUNC(X509_STORE_CTX_get0_chain) + RESOLVEFUNC(X509_getm_notBefore) + RESOLVEFUNC(X509_getm_notAfter) + RESOLVEFUNC(X509_get_version) + RESOLVEFUNC(X509_get_pubkey) + RESOLVEFUNC(X509_STORE_set_verify_cb) + RESOLVEFUNC(CRYPTO_free) + RESOLVEFUNC(OpenSSL_version_num) + RESOLVEFUNC(OpenSSL_version) + if (!_q_OpenSSL_version) { + // Apparently, we were built with OpenSSL 1.1 enabled but are now using + // a wrong library. + delete libs.first; + delete libs.second; + qCWarning(lcSsl, "Incompatible version of OpenSSL"); + return false; + } + + RESOLVEFUNC(SSL_SESSION_get_ticket_lifetime_hint) + RESOLVEFUNC(DH_bits) + RESOLVEFUNC(DSA_bits) + +#else // !opensslv11 + + RESOLVEFUNC(ASN1_STRING_data) + #ifdef SSLEAY_MACROS RESOLVEFUNC(ASN1_dup) +#endif // SSLEAY_MACROS + RESOLVEFUNC(BIO_new_file) + RESOLVEFUNC(ERR_clear_error) + RESOLVEFUNC(CRYPTO_free) + RESOLVEFUNC(CRYPTO_num_locks) + RESOLVEFUNC(CRYPTO_set_id_callback) + RESOLVEFUNC(CRYPTO_set_locking_callback) + RESOLVEFUNC(ERR_peek_last_error) + RESOLVEFUNC(ERR_free_strings) + RESOLVEFUNC(EVP_CIPHER_CTX_cleanup) + RESOLVEFUNC(EVP_CIPHER_CTX_init) + +#ifdef SSLEAY_MACROS // ### verify + RESOLVEFUNC(PEM_ASN1_read_bio) +#endif // SSLEAY_MACROS + + RESOLVEFUNC(sk_new_null) + RESOLVEFUNC(sk_push) + RESOLVEFUNC(sk_free) + RESOLVEFUNC(sk_num) + RESOLVEFUNC(sk_pop_free) + RESOLVEFUNC(sk_value) + RESOLVEFUNC(SSL_library_init) + RESOLVEFUNC(SSL_load_error_strings) +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + RESOLVEFUNC(SSL_get_ex_new_index) +#endif +#ifndef OPENSSL_NO_SSL2 + RESOLVEFUNC(SSLv2_client_method) +#endif +#ifndef OPENSSL_NO_SSL3_METHOD + RESOLVEFUNC(SSLv3_client_method) +#endif + RESOLVEFUNC(SSLv23_client_method) + RESOLVEFUNC(TLSv1_client_method) +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + RESOLVEFUNC(TLSv1_1_client_method) + RESOLVEFUNC(TLSv1_2_client_method) #endif +#ifndef OPENSSL_NO_SSL2 + RESOLVEFUNC(SSLv2_server_method) +#endif +#ifndef OPENSSL_NO_SSL3_METHOD + RESOLVEFUNC(SSLv3_server_method) +#endif + RESOLVEFUNC(SSLv23_server_method) + RESOLVEFUNC(TLSv1_server_method) +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + RESOLVEFUNC(TLSv1_1_server_method) + RESOLVEFUNC(TLSv1_2_server_method) +#endif + RESOLVEFUNC(X509_STORE_CTX_get_chain) +#ifdef SSLEAY_MACROS + RESOLVEFUNC(i2d_DSAPrivateKey) + RESOLVEFUNC(i2d_RSAPrivateKey) + RESOLVEFUNC(d2i_DSAPrivateKey) + RESOLVEFUNC(d2i_RSAPrivateKey) +#endif + RESOLVEFUNC(CONF_get1_default_config_file) + RESOLVEFUNC(OPENSSL_add_all_algorithms_noconf) + RESOLVEFUNC(OPENSSL_add_all_algorithms_conf) + RESOLVEFUNC(SSLeay) + + if (!_q_SSLeay || q_SSLeay() >= 0x10100000L) { + // OpenSSL 1.1 has deprecated and removed SSLeay. We consider a failure to + // resolve this symbol as a failure to resolve symbols. + // The right operand of '||' above is ... a bit of paranoia. + delete libs.first; + delete libs.second; + qCWarning(lcSsl, "Incompatible version of OpenSSL"); + return false; + } + + + RESOLVEFUNC(SSLeay_version) + +#ifndef OPENSSL_NO_EC +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + if (q_SSLeay() >= 0x10002000L) + RESOLVEFUNC(EC_curve_nist2nid) +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L +#endif // OPENSSL_NO_EC + + +#endif // !opensslv11 + RESOLVEFUNC(ASN1_INTEGER_get) - RESOLVEFUNC(ASN1_STRING_data) RESOLVEFUNC(ASN1_STRING_length) RESOLVEFUNC(ASN1_STRING_to_UTF8) RESOLVEFUNC(BIO_ctrl) - RESOLVEFUNC(BIO_new_file) - RESOLVEFUNC(ERR_clear_error) - RESOLVEFUNC(OPENSSL_free) RESOLVEFUNC(BIO_free) RESOLVEFUNC(BIO_new) RESOLVEFUNC(BIO_new_mem_buf) @@ -802,26 +1003,22 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(BN_is_word) #endif RESOLVEFUNC(BN_mod_word) - RESOLVEFUNC(CRYPTO_free) - RESOLVEFUNC(CRYPTO_num_locks) - RESOLVEFUNC(CRYPTO_set_id_callback) - RESOLVEFUNC(CRYPTO_set_locking_callback) RESOLVEFUNC(DSA_new) RESOLVEFUNC(DSA_free) RESOLVEFUNC(ERR_error_string) RESOLVEFUNC(ERR_get_error) - RESOLVEFUNC(ERR_peek_last_error) - RESOLVEFUNC(ERR_free_strings) - RESOLVEFUNC(EVP_CIPHER_CTX_cleanup) - RESOLVEFUNC(EVP_CIPHER_CTX_init) + RESOLVEFUNC(EVP_CIPHER_CTX_new) + RESOLVEFUNC(EVP_CIPHER_CTX_free) RESOLVEFUNC(EVP_CIPHER_CTX_ctrl) RESOLVEFUNC(EVP_CIPHER_CTX_set_key_length) RESOLVEFUNC(EVP_CipherInit) + RESOLVEFUNC(EVP_CipherInit_ex) RESOLVEFUNC(EVP_CipherUpdate) RESOLVEFUNC(EVP_CipherFinal) RESOLVEFUNC(EVP_des_cbc) RESOLVEFUNC(EVP_des_ede3_cbc) RESOLVEFUNC(EVP_rc2_cbc) + RESOLVEFUNC(EVP_sha1) RESOLVEFUNC(EVP_PKEY_assign) RESOLVEFUNC(EVP_PKEY_set1_RSA) RESOLVEFUNC(EVP_PKEY_set1_DSA) @@ -843,9 +1040,8 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(i2t_ASN1_OBJECT) RESOLVEFUNC(OBJ_obj2txt) RESOLVEFUNC(OBJ_obj2nid) -#ifdef SSLEAY_MACROS // ### verify - RESOLVEFUNC(PEM_ASN1_read_bio) -#else + +#ifndef SSLEAY_MACROS RESOLVEFUNC(PEM_read_bio_PrivateKey) RESOLVEFUNC(PEM_read_bio_DSAPrivateKey) RESOLVEFUNC(PEM_read_bio_RSAPrivateKey) @@ -858,7 +1054,8 @@ bool q_resolveOpenSslSymbols() #ifndef OPENSSL_NO_EC RESOLVEFUNC(PEM_write_bio_ECPrivateKey) #endif -#endif +#endif // !SSLEAY_MACROS + RESOLVEFUNC(PEM_read_bio_PUBKEY) RESOLVEFUNC(PEM_read_bio_DSA_PUBKEY) RESOLVEFUNC(PEM_read_bio_RSA_PUBKEY) @@ -874,12 +1071,6 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(RAND_status) RESOLVEFUNC(RSA_new) RESOLVEFUNC(RSA_free) - RESOLVEFUNC(sk_new_null) - RESOLVEFUNC(sk_push) - RESOLVEFUNC(sk_free) - RESOLVEFUNC(sk_num) - RESOLVEFUNC(sk_pop_free) - RESOLVEFUNC(sk_value) RESOLVEFUNC(SSL_CIPHER_description) RESOLVEFUNC(SSL_CIPHER_get_bits) RESOLVEFUNC(SSL_CTX_check_private_key) @@ -907,8 +1098,6 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(SSL_get_peer_cert_chain) RESOLVEFUNC(SSL_get_peer_certificate) RESOLVEFUNC(SSL_get_verify_result) - RESOLVEFUNC(SSL_library_init) - RESOLVEFUNC(SSL_load_error_strings) RESOLVEFUNC(SSL_new) RESOLVEFUNC(SSL_ctrl) RESOLVEFUNC(SSL_read) @@ -921,7 +1110,6 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(SSL_get1_session) RESOLVEFUNC(SSL_get_session) #if OPENSSL_VERSION_NUMBER >= 0x10001000L - RESOLVEFUNC(SSL_get_ex_new_index) RESOLVEFUNC(SSL_set_ex_data) RESOLVEFUNC(SSL_get_ex_data) #endif @@ -931,30 +1119,6 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(SSL_CTX_use_psk_identity_hint) #endif RESOLVEFUNC(SSL_write) -#ifndef OPENSSL_NO_SSL2 - RESOLVEFUNC(SSLv2_client_method) -#endif -#ifndef OPENSSL_NO_SSL3_METHOD - RESOLVEFUNC(SSLv3_client_method) -#endif - RESOLVEFUNC(SSLv23_client_method) - RESOLVEFUNC(TLSv1_client_method) -#if OPENSSL_VERSION_NUMBER >= 0x10001000L - RESOLVEFUNC(TLSv1_1_client_method) - RESOLVEFUNC(TLSv1_2_client_method) -#endif -#ifndef OPENSSL_NO_SSL2 - RESOLVEFUNC(SSLv2_server_method) -#endif -#ifndef OPENSSL_NO_SSL3_METHOD - RESOLVEFUNC(SSLv3_server_method) -#endif - RESOLVEFUNC(SSLv23_server_method) - RESOLVEFUNC(TLSv1_server_method) -#if OPENSSL_VERSION_NUMBER >= 0x10001000L - RESOLVEFUNC(TLSv1_1_server_method) - RESOLVEFUNC(TLSv1_2_server_method) -#endif RESOLVEFUNC(X509_NAME_entry_count) RESOLVEFUNC(X509_NAME_get_entry) RESOLVEFUNC(X509_NAME_ENTRY_get_data) @@ -970,12 +1134,12 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(X509_STORE_CTX_get_error) RESOLVEFUNC(X509_STORE_CTX_get_error_depth) RESOLVEFUNC(X509_STORE_CTX_get_current_cert) - RESOLVEFUNC(X509_STORE_CTX_get_chain) RESOLVEFUNC(X509_cmp) #ifndef SSLEAY_MACROS RESOLVEFUNC(X509_dup) #endif RESOLVEFUNC(X509_print) + RESOLVEFUNC(X509_digest) RESOLVEFUNC(X509_EXTENSION_get_object) RESOLVEFUNC(X509_free) RESOLVEFUNC(X509_get_ext) @@ -991,21 +1155,11 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(X509_check_issued) RESOLVEFUNC(X509_get_issuer_name) RESOLVEFUNC(X509_get_subject_name) + RESOLVEFUNC(X509_get_serialNumber) RESOLVEFUNC(X509_verify_cert) RESOLVEFUNC(d2i_X509) RESOLVEFUNC(i2d_X509) -#ifdef SSLEAY_MACROS - RESOLVEFUNC(i2d_DSAPrivateKey) - RESOLVEFUNC(i2d_RSAPrivateKey) - RESOLVEFUNC(d2i_DSAPrivateKey) - RESOLVEFUNC(d2i_RSAPrivateKey) -#endif - RESOLVEFUNC(CONF_get1_default_config_file) - RESOLVEFUNC(OPENSSL_add_all_algorithms_noconf) - RESOLVEFUNC(OPENSSL_add_all_algorithms_conf) RESOLVEFUNC(SSL_CTX_load_verify_locations) - RESOLVEFUNC(SSLeay) - RESOLVEFUNC(SSLeay_version) RESOLVEFUNC(i2d_SSL_SESSION) RESOLVEFUNC(d2i_SSL_SESSION) #if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG) @@ -1029,27 +1183,14 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(EC_KEY_new_by_curve_name) RESOLVEFUNC(EC_KEY_free) RESOLVEFUNC(EC_get_builtin_curves) -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - if (q_SSLeay() >= 0x10002000L) - RESOLVEFUNC(EC_curve_nist2nid) -#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L #endif // OPENSSL_NO_EC RESOLVEFUNC(PKCS12_parse) RESOLVEFUNC(d2i_PKCS12_bio) RESOLVEFUNC(PKCS12_free) + symbolsResolved = true; delete libs.first; delete libs.second; - if (!_q_SSLeay || q_SSLeay() >= 0x10100000L) { - // OpenSSL 1.1 deprecated and removed SSLeay. We consider a failure to - // resolve this symbol as a failure to resolve symbols. - // The right operand of '||' above ... a bit of paranoia. - qCWarning(lcSsl, "Incompatible version of OpenSSL"); - return false; - } - - symbolsResolved = true; - return true; } #endif // QT_CONFIG(library) diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h index 68dc6da811..796bf2d4f5 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. ** Copyright (C) 2014 BlackBerry Limited. All rights reserved. ** Contact: https://www.qt.io/licensing/ ** @@ -56,6 +56,7 @@ #ifndef QSSLSOCKET_OPENSSL_SYMBOLS_P_H #define QSSLSOCKET_OPENSSL_SYMBOLS_P_H + // // W A R N I N G // ------------- @@ -215,20 +216,20 @@ QT_BEGIN_NAMESPACE #endif // !defined QT_LINKED_OPENSSL +#if QT_CONFIG(opensslv11) +#include "qsslsocket_openssl11_symbols_p.h" +#else +#include "qsslsocket_opensslpre11_symbols_p.h" +#endif // QT_CONFIG + bool q_resolveOpenSslSymbols(); long q_ASN1_INTEGER_get(ASN1_INTEGER *a); -unsigned char * q_ASN1_STRING_data(ASN1_STRING *a); int q_ASN1_STRING_length(ASN1_STRING *a); int q_ASN1_STRING_to_UTF8(unsigned char **a, ASN1_STRING *b); long q_BIO_ctrl(BIO *a, int b, long c, void *d); -BIO *q_BIO_new_file(const char *filename, const char *mode); -void q_ERR_clear_error(); -void q_OPENSSL_free(void *ptr); Q_AUTOTEST_EXPORT int q_BIO_free(BIO *a); -Q_AUTOTEST_EXPORT BIO *q_BIO_new(BIO_METHOD *a); BIO *q_BIO_new_mem_buf(void *a, int b); int q_BIO_read(BIO *a, void *b, int c); -Q_AUTOTEST_EXPORT BIO_METHOD *q_BIO_s_mem(); Q_AUTOTEST_EXPORT int q_BIO_write(BIO *a, const void *b, int c); int q_BN_num_bits(const BIGNUM *a); #if OPENSSL_VERSION_NUMBER >= 0x10100000L @@ -250,27 +251,23 @@ BN_ULONG q_BN_mod_word(const BIGNUM *a, BN_ULONG w); const EC_GROUP* q_EC_KEY_get0_group(const EC_KEY* k); int q_EC_GROUP_get_degree(const EC_GROUP* g); #endif -int q_CRYPTO_num_locks(); -void q_CRYPTO_set_locking_callback(void (*a)(int, int, const char *, int)); -void q_CRYPTO_set_id_callback(unsigned long (*a)()); -void q_CRYPTO_free(void *a); DSA *q_DSA_new(); void q_DSA_free(DSA *a); X509 *q_d2i_X509(X509 **a, const unsigned char **b, long c); char *q_ERR_error_string(unsigned long a, char *b); unsigned long q_ERR_get_error(); -unsigned long q_ERR_peek_last_error(); -void q_ERR_free_strings(); -void q_EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *a); -void q_EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *a); +EVP_CIPHER_CTX *q_EVP_CIPHER_CTX_new(); +void q_EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *a); int q_EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr); int q_EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *x, int keylen); int q_EVP_CipherInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, const unsigned char *key, const unsigned char *iv, int enc); +int q_EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, ENGINE *impl, const unsigned char *key, const unsigned char *iv, int enc); int q_EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl); int q_EVP_CipherFinal(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl); const EVP_CIPHER *q_EVP_des_cbc(); const EVP_CIPHER *q_EVP_des_ede3_cbc(); const EVP_CIPHER *q_EVP_rc2_cbc(); +const EVP_MD *q_EVP_sha1(); int q_EVP_PKEY_assign(EVP_PKEY *a, int b, char *c); Q_AUTOTEST_EXPORT int q_EVP_PKEY_set1_RSA(EVP_PKEY *a, RSA *b); int q_EVP_PKEY_set1_DSA(EVP_PKEY *a, DSA *b); @@ -314,7 +311,7 @@ int q_PEM_write_bio_RSAPrivateKey(BIO *a, RSA *b, const EVP_CIPHER *c, unsigned int q_PEM_write_bio_ECPrivateKey(BIO *a, EC_KEY *b, const EVP_CIPHER *c, unsigned char *d, int e, pem_password_cb *f, void *g); #endif -#endif +#endif // SSLEAY_MACROS Q_AUTOTEST_EXPORT EVP_PKEY *q_PEM_read_bio_PUBKEY(BIO *a, EVP_PKEY **b, pem_password_cb *c, void *d); DSA *q_PEM_read_bio_DSA_PUBKEY(BIO *a, DSA **b, pem_password_cb *c, void *d); RSA *q_PEM_read_bio_RSA_PUBKEY(BIO *a, RSA **b, pem_password_cb *c, void *d); @@ -330,23 +327,10 @@ void q_RAND_seed(const void *a, int b); int q_RAND_status(); RSA *q_RSA_new(); void q_RSA_free(RSA *a); -int q_sk_num(STACK *a); -void q_sk_pop_free(STACK *a, void (*b)(void *)); -#if OPENSSL_VERSION_NUMBER >= 0x10000000L -_STACK *q_sk_new_null(); -void q_sk_push(_STACK *st, void *data); -void q_sk_free(_STACK *a); -void * q_sk_value(STACK *a, int b); -#else -STACK *q_sk_new_null(); -void q_sk_push(STACK *st, char *data); -void q_sk_free(STACK *a); -char * q_sk_value(STACK *a, int b); -#endif int q_SSL_accept(SSL *a); int q_SSL_clear(SSL *a); -char *q_SSL_CIPHER_description(SSL_CIPHER *a, char *b, int c); -int q_SSL_CIPHER_get_bits(SSL_CIPHER *a, int *b); +char *q_SSL_CIPHER_description(const SSL_CIPHER *a, char *b, int c); +int q_SSL_CIPHER_get_bits(const SSL_CIPHER *a, int *b); int q_SSL_connect(SSL *a); int q_SSL_CTX_check_private_key(const SSL_CTX *a); long q_SSL_CTX_ctrl(SSL_CTX *a, int b, long c, void *d); @@ -378,8 +362,6 @@ int q_SSL_get_error(SSL *a, int b); STACK_OF(X509) *q_SSL_get_peer_cert_chain(SSL *a); X509 *q_SSL_get_peer_certificate(SSL *a); long q_SSL_get_verify_result(const SSL *a); -int q_SSL_library_init(); -void q_SSL_load_error_strings(); SSL *q_SSL_new(SSL_CTX *a); long q_SSL_ctrl(SSL *ssl,int cmd, long larg, void *parg); int q_SSL_read(SSL *a, void *b, int c); @@ -392,7 +374,6 @@ void q_SSL_SESSION_free(SSL_SESSION *ses); SSL_SESSION *q_SSL_get1_session(SSL *ssl); SSL_SESSION *q_SSL_get_session(const SSL *ssl); #if OPENSSL_VERSION_NUMBER >= 0x10001000L -int q_SSL_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func); int q_SSL_set_ex_data(SSL *ssl, int idx, void *arg); void *q_SSL_get_ex_data(const SSL *ssl, int idx); #endif @@ -403,49 +384,6 @@ typedef unsigned int (*q_psk_server_callback_t)(SSL *ssl, const char *identity, void q_SSL_set_psk_server_callback(SSL *ssl, q_psk_server_callback_t callback); int q_SSL_CTX_use_psk_identity_hint(SSL_CTX *ctx, const char *hint); #endif // OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK) -#if OPENSSL_VERSION_NUMBER >= 0x10000000L -#ifndef OPENSSL_NO_SSL2 -const SSL_METHOD *q_SSLv2_client_method(); -#endif -#ifndef OPENSSL_NO_SSL3_METHOD -const SSL_METHOD *q_SSLv3_client_method(); -#endif -const SSL_METHOD *q_SSLv23_client_method(); -const SSL_METHOD *q_TLSv1_client_method(); -const SSL_METHOD *q_TLSv1_1_client_method(); -const SSL_METHOD *q_TLSv1_2_client_method(); -#ifndef OPENSSL_NO_SSL2 -const SSL_METHOD *q_SSLv2_server_method(); -#endif -#ifndef OPENSSL_NO_SSL3_METHOD -const SSL_METHOD *q_SSLv3_server_method(); -#endif -const SSL_METHOD *q_SSLv23_server_method(); -const SSL_METHOD *q_TLSv1_server_method(); -const SSL_METHOD *q_TLSv1_1_server_method(); -const SSL_METHOD *q_TLSv1_2_server_method(); -#else -#ifndef OPENSSL_NO_SSL2 -SSL_METHOD *q_SSLv2_client_method(); -#endif -#ifndef OPENSSL_NO_SSL3_METHOD -SSL_METHOD *q_SSLv3_client_method(); -#endif -SSL_METHOD *q_SSLv23_client_method(); -SSL_METHOD *q_TLSv1_client_method(); -SSL_METHOD *q_TLSv1_1_client_method(); -SSL_METHOD *q_TLSv1_2_client_method(); -#ifndef OPENSSL_NO_SSL2 -SSL_METHOD *q_SSLv2_server_method(); -#endif -#ifndef OPENSSL_NO_SSL3_METHOD -SSL_METHOD *q_SSLv3_server_method(); -#endif -SSL_METHOD *q_SSLv23_server_method(); -SSL_METHOD *q_TLSv1_server_method(); -SSL_METHOD *q_TLSv1_1_server_method(); -SSL_METHOD *q_TLSv1_2_server_method(); -#endif int q_SSL_write(SSL *a, const void *b, int c); int q_X509_cmp(X509 *a, X509 *b); #ifdef SSLEAY_MACROS @@ -456,6 +394,7 @@ void *q_ASN1_dup(i2d_of_void *i2d, d2i_of_void *d2i, char *x); X509 *q_X509_dup(X509 *a); #endif void q_X509_print(BIO *a, X509*b); +int q_X509_digest(const X509 *x509, const EVP_MD *type, unsigned char *md, unsigned int *len); ASN1_OBJECT *q_X509_EXTENSION_get_object(X509_EXTENSION *a); void q_X509_free(X509 *a); X509_EXTENSION *q_X509_get_ext(X509 *a, int b); @@ -475,6 +414,7 @@ int q_ASN1_STRING_print(BIO *a, ASN1_STRING *b); int q_X509_check_issued(X509 *a, X509 *b); X509_NAME *q_X509_get_issuer_name(X509 *a); X509_NAME *q_X509_get_subject_name(X509 *a); +ASN1_INTEGER *q_X509_get_serialNumber(X509 *a); int q_X509_verify_cert(X509_STORE_CTX *ctx); int q_X509_NAME_entry_count(X509_NAME *a); X509_NAME_ENTRY *q_X509_NAME_get_entry(X509_NAME *a,int b); @@ -492,7 +432,6 @@ int q_X509_STORE_CTX_set_purpose(X509_STORE_CTX *ctx, int purpose); int q_X509_STORE_CTX_get_error(X509_STORE_CTX *ctx); int q_X509_STORE_CTX_get_error_depth(X509_STORE_CTX *ctx); X509 *q_X509_STORE_CTX_get_current_cert(X509_STORE_CTX *ctx); -STACK_OF(X509) *q_X509_STORE_CTX_get_chain(X509_STORE_CTX *ctx); // Diffie-Hellman support DH *q_DH_new(); @@ -526,34 +465,9 @@ int q_PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert, PKCS12 *q_d2i_PKCS12_bio(BIO *bio, PKCS12 **pkcs12); void q_PKCS12_free(PKCS12 *pkcs12); - #define q_BIO_get_mem_data(b, pp) (int)q_BIO_ctrl(b,BIO_CTRL_INFO,0,(char *)pp) #define q_BIO_pending(b) (int)q_BIO_ctrl(b,BIO_CTRL_PENDING,0,NULL) -#ifdef SSLEAY_MACROS -int q_i2d_DSAPrivateKey(const DSA *a, unsigned char **pp); -int q_i2d_RSAPrivateKey(const RSA *a, unsigned char **pp); -RSA *q_d2i_RSAPrivateKey(RSA **a, unsigned char **pp, long length); -DSA *q_d2i_DSAPrivateKey(DSA **a, unsigned char **pp, long length); -#define q_PEM_read_bio_RSAPrivateKey(bp, x, cb, u) \ - (RSA *)q_PEM_ASN1_read_bio( \ - (void *(*)(void**, const unsigned char**, long int))q_d2i_RSAPrivateKey, PEM_STRING_RSA, bp, (void **)x, cb, u) -#define q_PEM_read_bio_DSAPrivateKey(bp, x, cb, u) \ - (DSA *)q_PEM_ASN1_read_bio( \ - (void *(*)(void**, const unsigned char**, long int))q_d2i_DSAPrivateKey, PEM_STRING_DSA, bp, (void **)x, cb, u) -#define q_PEM_write_bio_RSAPrivateKey(bp,x,enc,kstr,klen,cb,u) \ - PEM_ASN1_write_bio((int (*)(void*, unsigned char**))q_i2d_RSAPrivateKey,PEM_STRING_RSA,\ - bp,(char *)x,enc,kstr,klen,cb,u) -#define q_PEM_write_bio_DSAPrivateKey(bp,x,enc,kstr,klen,cb,u) \ - PEM_ASN1_write_bio((int (*)(void*, unsigned char**))q_i2d_DSAPrivateKey,PEM_STRING_DSA,\ - bp,(char *)x,enc,kstr,klen,cb,u) -#define q_PEM_read_bio_DHparams(bp, dh, cb, u) \ - (DH *)q_PEM_ASN1_read_bio( \ - (void *(*)(void**, const unsigned char**, long int))q_d2i_DHparams, PEM_STRING_DHPARAMS, bp, (void **)x, cb, u) -#endif -#define q_SSL_CTX_set_options(ctx,op) q_SSL_CTX_ctrl((ctx),SSL_CTRL_OPTIONS,(op),NULL) #define q_SSL_CTX_set_mode(ctx,op) q_SSL_CTX_ctrl((ctx),SSL_CTRL_MODE,(op),NULL) -#define q_SKM_sk_num(type, st) ((int (*)(const STACK_OF(type) *))q_sk_num)(st) -#define q_SKM_sk_value(type, st,i) ((type * (*)(const STACK_OF(type) *, int))q_sk_value)(st, i) #define q_sk_GENERAL_NAME_num(st) q_SKM_sk_num(GENERAL_NAME, (st)) #define q_sk_GENERAL_NAME_value(st, i) q_SKM_sk_value(GENERAL_NAME, (st), (i)) #define q_sk_X509_num(st) q_SKM_sk_num(X509, (st)) @@ -562,19 +476,12 @@ DSA *q_d2i_DSAPrivateKey(DSA **a, unsigned char **pp, long length); #define q_sk_SSL_CIPHER_value(st, i) q_SKM_sk_value(SSL_CIPHER, (st), (i)) #define q_SSL_CTX_add_extra_chain_cert(ctx,x509) \ q_SSL_CTX_ctrl(ctx,SSL_CTRL_EXTRA_CHAIN_CERT,0,(char *)x509) -#define q_X509_get_notAfter(x) X509_get_notAfter(x) -#define q_X509_get_notBefore(x) X509_get_notBefore(x) #define q_EVP_PKEY_assign_RSA(pkey,rsa) q_EVP_PKEY_assign((pkey),EVP_PKEY_RSA,\ (char *)(rsa)) #define q_EVP_PKEY_assign_DSA(pkey,dsa) q_EVP_PKEY_assign((pkey),EVP_PKEY_DSA,\ (char *)(dsa)) #define q_OpenSSL_add_all_algorithms() q_OPENSSL_add_all_algorithms_conf() -char *q_CONF_get1_default_config_file(); -void q_OPENSSL_add_all_algorithms_noconf(); -void q_OPENSSL_add_all_algorithms_conf(); int q_SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, const char *CApath); -long q_SSLeay(); -const char *q_SSLeay_version(int type); int q_i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp); SSL_SESSION *q_d2i_SSL_SESSION(SSL_SESSION **a, const unsigned char **pp, long length); diff --git a/src/network/ssl/qsslsocket_opensslpre11.cpp b/src/network/ssl/qsslsocket_opensslpre11.cpp new file mode 100644 index 0000000000..e51888c5f2 --- /dev/null +++ b/src/network/ssl/qsslsocket_opensslpre11.cpp @@ -0,0 +1,424 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2014 Governikus GmbH & Co. KG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** In addition, as a special exception, the copyright holders listed above give +** permission to link the code of its release of Qt with the OpenSSL project's +** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the +** same license as the original version), and distribute the linked executables. +** +** You must comply with the GNU General Public License version 2 in all +** respects for all of the code used other than the "OpenSSL" code. If you +** modify this file, you may extend this exception to your version of the file, +** but you are not obligated to do so. If you do not wish to do so, delete +** this exception statement from your version of this file. +** +****************************************************************************/ + +//#define QT_DECRYPT_SSL_TRAFFIC + +#include "qssl_p.h" +#include "qsslsocket_openssl_p.h" +#include "qsslsocket_openssl_symbols_p.h" +#include "qsslsocket.h" +#include "qsslkey.h" + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/* \internal + + From OpenSSL's thread(3) manual page: + + OpenSSL can safely be used in multi-threaded applications provided that at + least two callback functions are set. + + locking_function(int mode, int n, const char *file, int line) is needed to + perform locking on shared data structures. (Note that OpenSSL uses a + number of global data structures that will be implicitly shared + whenever multiple threads use OpenSSL.) Multi-threaded + applications will crash at random if it is not set. ... + ... + id_function(void) is a function that returns a thread ID. It is not + needed on Windows nor on platforms where getpid() returns a different + ID for each thread (most notably Linux) +*/ + +class QOpenSslLocks +{ +public: + QOpenSslLocks() + : initLocker(QMutex::Recursive), + locksLocker(QMutex::Recursive) + { + QMutexLocker locker(&locksLocker); + int numLocks = q_CRYPTO_num_locks(); + locks = new QMutex *[numLocks]; + memset(locks, 0, numLocks * sizeof(QMutex *)); + } + ~QOpenSslLocks() + { + QMutexLocker locker(&locksLocker); + for (int i = 0; i < q_CRYPTO_num_locks(); ++i) + delete locks[i]; + delete [] locks; + + QSslSocketPrivate::deinitialize(); + } + QMutex *lock(int num) + { + QMutexLocker locker(&locksLocker); + QMutex *tmp = locks[num]; + if (!tmp) + tmp = locks[num] = new QMutex(QMutex::Recursive); + return tmp; + } + + QMutex *globalLock() + { + return &locksLocker; + } + + QMutex *initLock() + { + return &initLocker; + } + +private: + QMutex initLocker; + QMutex locksLocker; + QMutex **locks; +}; + +Q_GLOBAL_STATIC(QOpenSslLocks, openssl_locks) + +extern "C" { +static void locking_function(int mode, int lockNumber, const char *, int) +{ + QMutex *mutex = openssl_locks()->lock(lockNumber); + + // Lock or unlock it + if (mode & CRYPTO_LOCK) + mutex->lock(); + else + mutex->unlock(); +} +static unsigned long id_function() +{ + return (quintptr)QThread::currentThreadId(); +} + +} // extern "C" + +static void q_OpenSSL_add_all_algorithms_safe() +{ +#ifdef Q_OS_WIN + // Prior to version 1.0.1m an attempt to call OpenSSL_add_all_algorithms on + // Windows could result in 'exit' call from OPENSSL_config (QTBUG-43843). + // We can predict this and avoid OPENSSL_add_all_algorithms call. + // From OpenSSL docs: + // "An application does not need to add algorithms to use them explicitly, + // for example by EVP_sha1(). It just needs to add them if it (or any of + // the functions it calls) needs to lookup algorithms. + // The cipher and digest lookup functions are used in many parts of the + // library. If the table is not initialized several functions will + // misbehave and complain they cannot find algorithms. This includes the + // PEM, PKCS#12, SSL and S/MIME libraries. This is a common query in + // the OpenSSL mailing lists." + // + // Anyway, as a result, we chose not to call this function if it would exit. + + if (q_SSLeay() < 0x100010DFL) + { + // Now, before we try to call it, check if an attempt to open config file + // will result in exit: + if (char *confFileName = q_CONF_get1_default_config_file()) { + BIO *confFile = q_BIO_new_file(confFileName, "r"); + const auto lastError = q_ERR_peek_last_error(); + q_CRYPTO_free(confFileName); + if (confFile) { + q_BIO_free(confFile); + } else { + q_ERR_clear_error(); + if (ERR_GET_REASON(lastError) == ERR_R_SYS_LIB) { + qCWarning(lcSsl, "failed to open openssl.conf file"); + return; + } + } + } + } +#endif // Q_OS_WIN + + q_OpenSSL_add_all_algorithms(); +} + + +/*! + \internal +*/ +void QSslSocketPrivate::deinitialize() +{ + q_CRYPTO_set_id_callback(0); + q_CRYPTO_set_locking_callback(0); + q_ERR_free_strings(); +} + + +bool QSslSocketPrivate::ensureLibraryLoaded() +{ + if (!q_resolveOpenSslSymbols()) + return false; + + // Check if the library itself needs to be initialized. + QMutexLocker locker(openssl_locks()->initLock()); + + if (!s_libraryLoaded) { + s_libraryLoaded = true; + + // Initialize OpenSSL. + q_CRYPTO_set_id_callback(id_function); + q_CRYPTO_set_locking_callback(locking_function); + if (q_SSL_library_init() != 1) + return false; + q_SSL_load_error_strings(); + q_OpenSSL_add_all_algorithms_safe(); + +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + if (q_SSLeay() >= 0x10001000L) + QSslSocketBackendPrivate::s_indexForSSLExtraData = q_SSL_get_ex_new_index(0L, NULL, NULL, NULL, NULL); +#endif + + // Initialize OpenSSL's random seed. + if (!q_RAND_status()) { + qWarning("Random number generator not seeded, disabling SSL support"); + return false; + } + } + return true; +} + +void QSslSocketPrivate::ensureCiphersAndCertsLoaded() +{ + QMutexLocker locker(openssl_locks()->initLock()); + if (s_loadedCiphersAndCerts) + return; + s_loadedCiphersAndCerts = true; + + resetDefaultCiphers(); + resetDefaultEllipticCurves(); + +#if QT_CONFIG(library) + //load symbols needed to receive certificates from system store +#if defined(Q_OS_WIN) + HINSTANCE hLib = LoadLibraryW(L"Crypt32"); + if (hLib) { + ptrCertOpenSystemStoreW = (PtrCertOpenSystemStoreW)GetProcAddress(hLib, "CertOpenSystemStoreW"); + ptrCertFindCertificateInStore = (PtrCertFindCertificateInStore)GetProcAddress(hLib, "CertFindCertificateInStore"); + ptrCertCloseStore = (PtrCertCloseStore)GetProcAddress(hLib, "CertCloseStore"); + if (!ptrCertOpenSystemStoreW || !ptrCertFindCertificateInStore || !ptrCertCloseStore) + qCWarning(lcSsl, "could not resolve symbols in crypt32 library"); // should never happen + } else { + qCWarning(lcSsl, "could not load crypt32 library"); // should never happen + } +#elif defined(Q_OS_QNX) + s_loadRootCertsOnDemand = true; +#elif defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) + // check whether we can enable on-demand root-cert loading (i.e. check whether the sym links are there) + QList dirs = unixRootCertDirectories(); + QStringList symLinkFilter; + symLinkFilter << QLatin1String("[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]"); + for (int a = 0; a < dirs.count(); ++a) { + QDirIterator iterator(QLatin1String(dirs.at(a)), symLinkFilter, QDir::Files); + if (iterator.hasNext()) { + s_loadRootCertsOnDemand = true; + break; + } + } +#endif +#endif // QT_CONFIG(library) + // if on-demand loading was not enabled, load the certs now + if (!s_loadRootCertsOnDemand) + setDefaultCaCertificates(systemCaCertificates()); +#ifdef Q_OS_WIN + //Enabled for fetching additional root certs from windows update on windows 6+ + //This flag is set false by setDefaultCaCertificates() indicating the app uses + //its own cert bundle rather than the system one. + //Same logic that disables the unix on demand cert loading. + //Unlike unix, we do preload the certificates from the cert store. + if ((QSysInfo::windowsVersion() & QSysInfo::WV_NT_based) >= QSysInfo::WV_6_0) + s_loadRootCertsOnDemand = true; +#endif +} + +long QSslSocketPrivate::sslLibraryVersionNumber() +{ + if (!supportsSsl()) + return 0; + + return q_SSLeay(); +} + +QString QSslSocketPrivate::sslLibraryVersionString() +{ + if (!supportsSsl()) + return QString(); + + const char *versionString = q_SSLeay_version(SSLEAY_VERSION); + if (!versionString) + return QString(); + + return QString::fromLatin1(versionString); +} + +void QSslSocketBackendPrivate::continueHandshake() +{ + Q_Q(QSslSocket); + // if we have a max read buffer size, reset the plain socket's to match + if (readBufferMaxSize) + plainSocket->setReadBufferSize(readBufferMaxSize); + + if (q_SSL_ctrl((ssl), SSL_CTRL_GET_SESSION_REUSED, 0, NULL)) + configuration.peerSessionShared = true; + +#ifdef QT_DECRYPT_SSL_TRAFFIC + if (ssl->session && ssl->s3) { + const char *mk = reinterpret_cast(ssl->session->master_key); + QByteArray masterKey(mk, ssl->session->master_key_length); + const char *random = reinterpret_cast(ssl->s3->client_random); + QByteArray clientRandom(random, SSL3_RANDOM_SIZE); + + // different format, needed for e.g. older Wireshark versions: +// const char *sid = reinterpret_cast(ssl->session->session_id); +// QByteArray sessionID(sid, ssl->session->session_id_length); +// QByteArray debugLineRSA("RSA Session-ID:"); +// debugLineRSA.append(sessionID.toHex().toUpper()); +// debugLineRSA.append(" Master-Key:"); +// debugLineRSA.append(masterKey.toHex().toUpper()); +// debugLineRSA.append("\n"); + + QByteArray debugLineClientRandom("CLIENT_RANDOM "); + debugLineClientRandom.append(clientRandom.toHex().toUpper()); + debugLineClientRandom.append(" "); + debugLineClientRandom.append(masterKey.toHex().toUpper()); + debugLineClientRandom.append("\n"); + + QString sslKeyFile = QDir::tempPath() + QLatin1String("/qt-ssl-keys"); + QFile file(sslKeyFile); + if (!file.open(QIODevice::Append)) + qCWarning(lcSsl) << "could not open file" << sslKeyFile << "for appending"; + if (!file.write(debugLineClientRandom)) + qCWarning(lcSsl) << "could not write to file" << sslKeyFile; + file.close(); + } else { + qCWarning(lcSsl, "could not decrypt SSL traffic"); + } +#endif + + // Cache this SSL session inside the QSslContext + if (!(configuration.sslOptions & QSsl::SslOptionDisableSessionSharing)) { + if (!sslContextPointer->cacheSession(ssl)) { + sslContextPointer.clear(); // we could not cache the session + } else { + // Cache the session for permanent usage as well + if (!(configuration.sslOptions & QSsl::SslOptionDisableSessionPersistence)) { + if (!sslContextPointer->sessionASN1().isEmpty()) + configuration.sslSession = sslContextPointer->sessionASN1(); + configuration.sslSessionTicketLifeTimeHint = sslContextPointer->sessionTicketLifeTimeHint(); + } + } + } + +#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG) + + configuration.nextProtocolNegotiationStatus = sslContextPointer->npnContext().status; + if (sslContextPointer->npnContext().status == QSslConfiguration::NextProtocolNegotiationUnsupported) { + // we could not agree -> be conservative and use HTTP/1.1 + configuration.nextNegotiatedProtocol = QByteArrayLiteral("http/1.1"); + } else { + const unsigned char *proto = 0; + unsigned int proto_len = 0; +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + if (q_SSLeay() >= 0x10002000L) { + q_SSL_get0_alpn_selected(ssl, &proto, &proto_len); + if (proto_len && mode == QSslSocket::SslClientMode) { + // Client does not have a callback that sets it ... + configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNegotiated; + } + } + + if (!proto_len) { // Test if NPN was more lucky ... +#else + { +#endif + q_SSL_get0_next_proto_negotiated(ssl, &proto, &proto_len); + } + + if (proto_len) + configuration.nextNegotiatedProtocol = QByteArray(reinterpret_cast(proto), proto_len); + else + configuration.nextNegotiatedProtocol.clear(); + } +#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ... + +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + if (q_SSLeay() >= 0x10002000L && mode == QSslSocket::SslClientMode) { + EVP_PKEY *key; + if (q_SSL_get_server_tmp_key(ssl, &key)) + configuration.ephemeralServerKey = QSslKey(key, QSsl::PublicKey); + } +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L ... + + connectionEncrypted = true; + emit q->encrypted(); + if (autoStartHandshake && pendingClose) { + pendingClose = false; + q->disconnectFromHost(); + } +} + +QT_END_NAMESPACE diff --git a/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h b/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h new file mode 100644 index 0000000000..9686d22b98 --- /dev/null +++ b/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h @@ -0,0 +1,230 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2014 BlackBerry Limited. All rights reserved. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** In addition, as a special exception, the copyright holders listed above give +** permission to link the code of its release of Qt with the OpenSSL project's +** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the +** same license as the original version), and distribute the linked executables. +** +** You must comply with the GNU General Public License version 2 in all +** respects for all of the code used other than the "OpenSSL" code. If you +** modify this file, you may extend this exception to your version of the file, +** but you are not obligated to do so. If you do not wish to do so, delete +** this exception statement from your version of this file. +** +****************************************************************************/ + + +#ifndef QSSLSOCKET_OPENSSLPRE11_SYMBOLS_P_H +#define QSSLSOCKET_OPENSSLPRE11_SYMBOLS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +// Note: this file does not have QT_BEGIN_NAMESPACE/QT_END_NAMESPACE, it's done +// in qsslsocket_openssl_symbols_p.h. + +#ifndef QSSLSOCKET_OPENSSL_SYMBOLS_P_H +#error "You are not supposed to use this header file, include qsslsocket_openssl_symbols_p.h instead" +#endif + +unsigned char * q_ASN1_STRING_data(ASN1_STRING *a); +BIO *q_BIO_new_file(const char *filename, const char *mode); +void q_ERR_clear_error(); +Q_AUTOTEST_EXPORT BIO *q_BIO_new(BIO_METHOD *a); +Q_AUTOTEST_EXPORT BIO_METHOD *q_BIO_s_mem(); +int q_CRYPTO_num_locks(); +void q_CRYPTO_set_locking_callback(void (*a)(int, int, const char *, int)); +void q_CRYPTO_set_id_callback(unsigned long (*a)()); +void q_CRYPTO_free(void *a); +unsigned long q_ERR_peek_last_error(); +void q_ERR_free_strings(); +void q_EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *a); +void q_EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *a); + +#if OPENSSL_VERSION_NUMBER >= 0x10000000L +typedef _STACK STACK; +#endif + +// The typedef we use to make our pre 1.1 code look more like 1.1 (less ifdefs). +typedef STACK OPENSSL_STACK; + +// We resolve q_sk_ functions, but use q_OPENSSL_sk_ macros in code to reduce +// the amount of #ifdefs. +int q_sk_num(STACK *a); +#define q_OPENSSL_sk_num(a) q_sk_num(a) +void q_sk_pop_free(STACK *a, void (*b)(void *)); +#define q_OPENSSL_sk_pop_free(a, b) q_sk_pop_free(a, b) +STACK *q_sk_new_null(); +#define q_OPENSSL_sk_new_null() q_sk_new_null() + +void q_sk_free(STACK *a); + +// Just a name alias (not a function call expression) since in code we take an +// address of this: +#define q_OPENSSL_sk_free q_sk_free + +#if OPENSSL_VERSION_NUMBER >= 0x10000000L +void *q_sk_value(STACK *a, int b); +void q_sk_push(STACK *st, void *data); +#else +char *q_sk_value(STACK *a, int b); +void q_sk_push(STACK *st, char *data); +#endif // OPENSSL_VERSION_NUMBER >= 0x10000000L + +#define q_OPENSSL_sk_value(a, b) q_sk_value(a, b) +#define q_OPENSSL_sk_push(st, data) q_sk_push(st, data) + +#if OPENSSL_VERSION_NUMBER >= 0x10000000L +SSL_CTX *q_SSL_CTX_new(const SSL_METHOD *a); +#else +SSL_CTX *q_SSL_CTX_new(SSL_METHOD *a); +#endif + +int q_SSL_library_init(); +void q_SSL_load_error_strings(); + +#if OPENSSL_VERSION_NUMBER >= 0x10001000L +int q_SSL_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func); +#endif + +#if OPENSSL_VERSION_NUMBER >= 0x10000000L +#ifndef OPENSSL_NO_SSL2 +const SSL_METHOD *q_SSLv2_client_method(); +#endif +#ifndef OPENSSL_NO_SSL3_METHOD +const SSL_METHOD *q_SSLv3_client_method(); +#endif +const SSL_METHOD *q_SSLv23_client_method(); +const SSL_METHOD *q_TLSv1_client_method(); +const SSL_METHOD *q_TLSv1_1_client_method(); +const SSL_METHOD *q_TLSv1_2_client_method(); +#ifndef OPENSSL_NO_SSL2 +const SSL_METHOD *q_SSLv2_server_method(); +#endif +#ifndef OPENSSL_NO_SSL3_METHOD +const SSL_METHOD *q_SSLv3_server_method(); +#endif +const SSL_METHOD *q_SSLv23_server_method(); +const SSL_METHOD *q_TLSv1_server_method(); +const SSL_METHOD *q_TLSv1_1_server_method(); +const SSL_METHOD *q_TLSv1_2_server_method(); +#else +#ifndef OPENSSL_NO_SSL2 +SSL_METHOD *q_SSLv2_client_method(); +#endif +#ifndef OPENSSL_NO_SSL3_METHOD +SSL_METHOD *q_SSLv3_client_method(); +#endif +SSL_METHOD *q_SSLv23_client_method(); +SSL_METHOD *q_TLSv1_client_method(); +SSL_METHOD *q_TLSv1_1_client_method(); +SSL_METHOD *q_TLSv1_2_client_method(); +#ifndef OPENSSL_NO_SSL2 +SSL_METHOD *q_SSLv2_server_method(); +#endif +#ifndef OPENSSL_NO_SSL3_METHOD +SSL_METHOD *q_SSLv3_server_method(); +#endif +SSL_METHOD *q_SSLv23_server_method(); +SSL_METHOD *q_TLSv1_server_method(); +SSL_METHOD *q_TLSv1_1_server_method(); +SSL_METHOD *q_TLSv1_2_server_method(); +#endif + +STACK_OF(X509) *q_X509_STORE_CTX_get_chain(X509_STORE_CTX *ctx); + +#ifdef SSLEAY_MACROS +int q_i2d_DSAPrivateKey(const DSA *a, unsigned char **pp); +int q_i2d_RSAPrivateKey(const RSA *a, unsigned char **pp); +RSA *q_d2i_RSAPrivateKey(RSA **a, unsigned char **pp, long length); +DSA *q_d2i_DSAPrivateKey(DSA **a, unsigned char **pp, long length); +#define q_PEM_read_bio_RSAPrivateKey(bp, x, cb, u) \ + (RSA *)q_PEM_ASN1_read_bio( \ + (void *(*)(void**, const unsigned char**, long int))q_d2i_RSAPrivateKey, PEM_STRING_RSA, bp, (void **)x, cb, u) +#define q_PEM_read_bio_DSAPrivateKey(bp, x, cb, u) \ + (DSA *)q_PEM_ASN1_read_bio( \ + (void *(*)(void**, const unsigned char**, long int))q_d2i_DSAPrivateKey, PEM_STRING_DSA, bp, (void **)x, cb, u) +#define q_PEM_write_bio_RSAPrivateKey(bp,x,enc,kstr,klen,cb,u) \ + PEM_ASN1_write_bio((int (*)(void*, unsigned char**))q_i2d_RSAPrivateKey,PEM_STRING_RSA,\ + bp,(char *)x,enc,kstr,klen,cb,u) +#define q_PEM_write_bio_DSAPrivateKey(bp,x,enc,kstr,klen,cb,u) \ + PEM_ASN1_write_bio((int (*)(void*, unsigned char**))q_i2d_DSAPrivateKey,PEM_STRING_DSA,\ + bp,(char *)x,enc,kstr,klen,cb,u) +#define q_PEM_read_bio_DHparams(bp, dh, cb, u) \ + (DH *)q_PEM_ASN1_read_bio( \ + (void *(*)(void**, const unsigned char**, long int))q_d2i_DHparams, PEM_STRING_DHPARAMS, bp, (void **)x, cb, u) +#endif // SSLEAY_MACROS + +#define q_SSL_CTX_set_options(ctx,op) q_SSL_CTX_ctrl((ctx),SSL_CTRL_OPTIONS,(op),NULL) +#define q_SKM_sk_num(type, st) ((int (*)(const STACK_OF(type) *))q_sk_num)(st) +#define q_SKM_sk_value(type, st,i) ((type * (*)(const STACK_OF(type) *, int))q_sk_value)(st, i) +#define q_X509_getm_notAfter(x) X509_get_notAfter(x) +#define q_X509_getm_notBefore(x) X509_get_notBefore(x) + +// "Forward compatibility" with OpenSSL 1.1 (to save on #if-ery elsewhere): +#define q_X509_get_version(x509) q_ASN1_INTEGER_get((x509)->cert_info->version) +#define q_ASN1_STRING_get0_data(x) q_ASN1_STRING_data(x) +#define q_EVP_PKEY_base_id(pkey) ((pkey)->type) +#define q_X509_get_pubkey(x509) q_X509_PUBKEY_get((x509)->cert_info->key) +#define q_SSL_SESSION_get_ticket_lifetime_hint(s) ((s)->tlsext_tick_lifetime_hint) +#define q_RSA_bits(rsa) q_BN_num_bits((rsa)->n) +#define q_DSA_bits(dsa) q_BN_num_bits((dsa)->p) +#define q_X509_STORE_set_verify_cb(s,c) X509_STORE_set_verify_cb_func((s),(c)) + +char *q_CONF_get1_default_config_file(); +void q_OPENSSL_add_all_algorithms_noconf(); +void q_OPENSSL_add_all_algorithms_conf(); + +long q_SSLeay(); +const char *q_SSLeay_version(int type); + + +#endif // QSSLSOCKET_OPENSSL_PRE11_SYMBOLS_P_H diff --git a/src/network/ssl/ssl.pri b/src/network/ssl/ssl.pri index 52ce2eeade..949ebc3d2a 100644 --- a/src/network/ssl/ssl.pri +++ b/src/network/ssl/ssl.pri @@ -60,13 +60,25 @@ qtConfig(ssl) { HEADERS += ssl/qsslcontext_openssl_p.h \ ssl/qsslsocket_openssl_p.h \ ssl/qsslsocket_openssl_symbols_p.h - SOURCES += ssl/qsslcertificate_openssl.cpp \ - ssl/qsslcontext_openssl.cpp \ + SOURCES += ssl/qsslsocket_openssl_symbols.cpp \ ssl/qssldiffiehellmanparameters_openssl.cpp \ + ssl/qsslcertificate_openssl.cpp \ ssl/qsslellipticcurve_openssl.cpp \ ssl/qsslkey_openssl.cpp \ ssl/qsslsocket_openssl.cpp \ - ssl/qsslsocket_openssl_symbols.cpp + ssl/qsslcontext_openssl.cpp + + qtConfig(opensslv11) { + HEADERS += ssl/qsslsocket_openssl11_symbols_p.h + SOURCES += ssl/qsslsocket_openssl11.cpp \ + ssl/qsslcontext_openssl11.cpp + + QMAKE_CXXFLAGS += -DOPENSSL_API_COMPAT=0x10100000L + } else { + HEADERS += ssl/qsslsocket_opensslpre11_symbols_p.h + SOURCES += ssl/qsslsocket_opensslpre11.cpp \ + ssl/qsslcontext_opensslpre11.cpp + } darwin:SOURCES += ssl/qsslsocket_mac_shared.cpp -- cgit v1.2.3