From 2cf63c71ebe139890526057dcc51b24ea6df6c30 Mon Sep 17 00:00:00 2001 From: Mikkel Krautz Date: Wed, 25 May 2016 21:24:25 +0200 Subject: Add settable QSslDiffieHellmanParameters for QSslSocket-based servers. Only the OpenSSL backend is supported right now. [ChangeLog][QtNetwork][SSL/TLS support] It is now possible to set custom Diffie-Hellman parameters for QSslSocket-based servers. Change-Id: I50148873132cd0ec7e414250b107b6b4cbde02ea Reviewed-by: Timur Pocheptsov --- src/network/ssl/qsslconfiguration.cpp | 29 ++ src/network/ssl/qsslconfiguration.h | 4 + src/network/ssl/qsslconfiguration_p.h | 4 + src/network/ssl/qsslcontext_openssl.cpp | 39 ++- src/network/ssl/qssldiffiehellmanparameters.cpp | 313 +++++++++++++++++++++ src/network/ssl/qssldiffiehellmanparameters.h | 117 ++++++++ .../ssl/qssldiffiehellmanparameters_dummy.cpp | 59 ++++ .../ssl/qssldiffiehellmanparameters_openssl.cpp | 164 +++++++++++ src/network/ssl/qssldiffiehellmanparameters_p.h | 78 +++++ src/network/ssl/qsslsocket.cpp | 1 + src/network/ssl/qsslsocket_openssl_symbols.cpp | 14 + src/network/ssl/qsslsocket_openssl_symbols_p.h | 21 ++ src/network/ssl/ssl.pri | 6 + 13 files changed, 829 insertions(+), 20 deletions(-) create mode 100644 src/network/ssl/qssldiffiehellmanparameters.cpp create mode 100644 src/network/ssl/qssldiffiehellmanparameters.h create mode 100644 src/network/ssl/qssldiffiehellmanparameters_dummy.cpp create mode 100644 src/network/ssl/qssldiffiehellmanparameters_openssl.cpp create mode 100644 src/network/ssl/qssldiffiehellmanparameters_p.h (limited to 'src/network') diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp index edcaef7b0c..e009824a69 100644 --- a/src/network/ssl/qsslconfiguration.cpp +++ b/src/network/ssl/qsslconfiguration.cpp @@ -214,6 +214,7 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const d->ciphers == other.d->ciphers && d->ellipticCurves == other.d->ellipticCurves && d->ephemeralServerKey == other.d->ephemeralServerKey && + d->dhParams == other.d->dhParams && d->caCertificates == other.d->caCertificates && d->protocol == other.d->protocol && d->peerVerifyMode == other.d->peerVerifyMode && @@ -256,6 +257,7 @@ bool QSslConfiguration::isNull() const d->ciphers.count() == 0 && d->ellipticCurves.isEmpty() && d->ephemeralServerKey.isNull() && + d->dhParams == QSslDiffieHellmanParameters::defaultParameters() && d->localCertificateChain.isEmpty() && d->privateKey.isNull() && d->peerCertificate.isNull() && @@ -839,6 +841,33 @@ void QSslConfiguration::setPreSharedKeyIdentityHint(const QByteArray &hint) d->preSharedKeyIdentityHint = hint; } +/*! + \since 5.8 + + Retrieves the current set of Diffie-Hellman parameters. + + If no Diffie-Hellman parameters have been set, the QSslConfiguration object + defaults to using the 1024-bit MODP group from RFC 2409. + */ +QSslDiffieHellmanParameters QSslConfiguration::diffieHellmanParameters() const +{ + return d->dhParams; +} + +/*! + \since 5.8 + + Sets a custom set of Diffie-Hellman parameters to be used by this socket when functioning as + a server. + + If no Diffie-Hellman parameters have been set, the QSslConfiguration object + defaults to using the 1024-bit MODP group from RFC 2409. + */ +void QSslConfiguration::setDiffieHellmanParameters(const QSslDiffieHellmanParameters &dhparams) +{ + d->dhParams = dhparams; +} + /*! \since 5.3 diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h index 1f39e1a06f..61246e1009 100644 --- a/src/network/ssl/qsslconfiguration.h +++ b/src/network/ssl/qsslconfiguration.h @@ -69,6 +69,7 @@ class QSslCertificate; class QSslCipher; class QSslKey; class QSslEllipticCurve; +class QSslDiffieHellmanParameters; class QSslConfigurationPrivate; class Q_NETWORK_EXPORT QSslConfiguration @@ -144,6 +145,9 @@ public: QByteArray preSharedKeyIdentityHint() const; void setPreSharedKeyIdentityHint(const QByteArray &hint); + QSslDiffieHellmanParameters diffieHellmanParameters() const; + void setDiffieHellmanParameters(const QSslDiffieHellmanParameters &dhparams); + static QSslConfiguration defaultConfiguration(); static void setDefaultConfiguration(const QSslConfiguration &configuration); diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h index 5ab0ef36e4..139a9fc32b 100644 --- a/src/network/ssl/qsslconfiguration_p.h +++ b/src/network/ssl/qsslconfiguration_p.h @@ -73,6 +73,7 @@ #include "qsslcipher.h" #include "qsslkey.h" #include "qsslellipticcurve.h" +#include "qssldiffiehellmanparameters.h" QT_BEGIN_NAMESPACE @@ -87,6 +88,7 @@ public: allowRootCertOnDemandLoading(true), peerSessionShared(false), sslOptions(QSslConfigurationPrivate::defaultSslOptions), + dhParams(QSslDiffieHellmanParameters::defaultParameters()), sslSessionTicketLifeTimeHint(-1), ephemeralServerKey(), preSharedKeyIdentityHint(), @@ -118,6 +120,8 @@ public: QVector ellipticCurves; + QSslDiffieHellmanParameters dhParams; + QByteArray sslSession; int sslSessionTicketLifeTimeHint; diff --git a/src/network/ssl/qsslcontext_openssl.cpp b/src/network/ssl/qsslcontext_openssl.cpp index cbf290a05c..5a80d08e24 100644 --- a/src/network/ssl/qsslcontext_openssl.cpp +++ b/src/network/ssl/qsslcontext_openssl.cpp @@ -41,6 +41,7 @@ #include +#include #include #include "private/qssl_p.h" @@ -48,6 +49,7 @@ #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 @@ -55,22 +57,6 @@ QT_BEGIN_NAMESPACE extern int q_X509Callback(int ok, X509_STORE_CTX *ctx); extern QString getErrorsFromOpenSsl(); -static DH *get_dh1024() -{ - // Default DH params - // 1024-bit MODP Group - // From RFC 2409 - QByteArray params = QByteArray::fromBase64( - QByteArrayLiteral("MIGHAoGBAP//////////yQ/aoiFowjTExmKLgNwc0SkCTgiKZ8x0Agu+pjsTmyJR" \ - "Sgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHCReSFtXZiXn7G9ExC6aY37WsL" \ - "/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR7OZTgf//////////AgEC")); - - const char *ptr = params.constData(); - DH *dh = q_d2i_DHparams(NULL, reinterpret_cast(&ptr), params.length()); - - return dh; -} - QSslContext::QSslContext() : ctx(0), pkey(0), @@ -325,10 +311,23 @@ init_context: sslContext->setSessionASN1(configuration.sessionTicket()); // Set temp DH params - DH *dh = 0; - dh = get_dh1024(); - q_SSL_CTX_set_tmp_dh(sslContext->ctx, dh); - q_DH_free(dh); + 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.data()->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 diff --git a/src/network/ssl/qssldiffiehellmanparameters.cpp b/src/network/ssl/qssldiffiehellmanparameters.cpp new file mode 100644 index 0000000000..74424117dc --- /dev/null +++ b/src/network/ssl/qssldiffiehellmanparameters.cpp @@ -0,0 +1,313 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Mikkel Krautz +** Contact: http://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$ +** +****************************************************************************/ + + +/*! + \class QSslDiffieHellmanParameters + \brief The QSslDiffieHellmanParameters class provides an interface for Diffie-Hellman parameters for servers. + \since 5.8 + + \reentrant + \ingroup network + \ingroup ssl + \ingroup shared + \inmodule QtNetwork + + QSslDiffieHellmanParameters provides an interface for setting Diffie-Hellman parameters to servers based on QSslSocket. + + \sa QSslSocket, QSslCipher, QSslConfiguration +*/ + +#include "qssldiffiehellmanparameters.h" +#include "qssldiffiehellmanparameters_p.h" +#include "qsslsocket.h" +#include "qsslsocket_p.h" + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + Returns the default QSslDiffieHellmanParameters used by QSslSocket. + + This is currently the 1024-bit MODP group from RFC 2459, also + known as the Second Oakley Group. +*/ +QSslDiffieHellmanParameters QSslDiffieHellmanParameters::defaultParameters() +{ + // The 1024-bit MODP group from RFC 2459 (Second Oakley Group) + return QSslDiffieHellmanParameters( + QByteArray::fromBase64(QByteArrayLiteral( + "MIGHAoGBAP//////////yQ/aoiFowjTExmKLgNwc0SkCTgiKZ8x0Agu+pjsTmyJR" + "Sgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHCReSFtXZiXn7G9ExC6aY37WsL" + "/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR7OZTgf//////////AgEC" + )), + QSsl::Der + ); +} + +/*! + Constructs an empty QSslDiffieHellmanParameters instance. + + If an empty QSslDiffieHellmanParameters instance is set on a + QSslConfiguration object, Diffie-Hellman negotiation will + be disabled. + + \sa isValid() + \sa QSslConfiguration +*/ +QSslDiffieHellmanParameters::QSslDiffieHellmanParameters() + : d(new QSslDiffieHellmanParametersPrivate) +{ +} + +/*! + Constructs a QSslDiffieHellmanParameters object using + the byte array \encoded in either PEM or DER form. + + After construction, the isValid() method should be used to + check whether the Diffie-Hellman parameters were valid and + loaded correctly. + + \sa isValid() + \sa QSslConfiguration +*/ +QSslDiffieHellmanParameters::QSslDiffieHellmanParameters(const QByteArray &encoded, QSsl::EncodingFormat encoding) + : d(new QSslDiffieHellmanParametersPrivate) +{ + switch (encoding) { + case QSsl::Der: + d->decodeDer(encoded); + break; + case QSsl::Pem: + d->decodePem(encoded); + break; + } +} + +/*! + Constructs a QSslDiffieHellmanParameters object by + reading from \device in either PEM or DER form. + + After construction, the isValid() method should be used + to check whether the Diffie-Hellman parameters were valid + and loaded correctly. + + \sa isValid() + \sa QSslConfiguration +*/ +QSslDiffieHellmanParameters::QSslDiffieHellmanParameters(QIODevice *device, QSsl::EncodingFormat encoding) + : d(new QSslDiffieHellmanParametersPrivate) +{ + if (!device) + return; + + const QByteArray encoded = device->readAll(); + + switch (encoding) { + case QSsl::Der: + d->decodeDer(encoded); + break; + case QSsl::Pem: + d->decodePem(encoded); + break; + } +} + +/*! + Constructs an identical copy of \a other. +*/ +QSslDiffieHellmanParameters::QSslDiffieHellmanParameters(const QSslDiffieHellmanParameters &other) : d(other.d) +{ +} + +/*! + Destroys the QSslDiffieHellmanParameters object. +*/ +QSslDiffieHellmanParameters::~QSslDiffieHellmanParameters() +{ +} + +/*! + Copies the contents of \a other into this QSslDiffieHellmanParameters, making the two QSslDiffieHellmanParameters + identical. + + Returns a reference to this QSslDiffieHellmanParameters. +*/ +QSslDiffieHellmanParameters &QSslDiffieHellmanParameters::operator=(const QSslDiffieHellmanParameters &other) +{ + d = other.d; + return *this; +} + +/*! + \fn QSslDiffieHellmanParameters &QSslDiffieHellmanParameters::operator=(QSslDiffieHellmanParameters &&other) + + Move-assigns \a other to this QSslDiffieHellmanParameters instance. +*/ + +/*! + \fn void QSslDiffieHellmanParameters::swap(QSslDiffieHellmanParameters &other) + + Swaps this QSslDiffieHellmanParameters with \a other. This function is very fast and + never fails. +*/ + +/*! + Returns \c true if this is a an empty QSslDiffieHellmanParameters instance. + + Setting an empty QSslDiffieHellmanParameters instance on a QSslSocket-based + server will disable Diffie-Hellman key exchange. +*/ +bool QSslDiffieHellmanParameters::isEmpty() const Q_DECL_NOTHROW +{ + return d->derData.isNull() && d->error == QSslDiffieHellmanParameters::NoError; +} + +/*! + Returns \c true if this is a valid QSslDiffieHellmanParameters; otherwise false. + + This method should be used after constructing a QSslDiffieHellmanParameters + object to determine its validity. + + If a QSslDiffieHellmanParameters object is not valid, you can use the error() + method to determine what error prevented the object from being constructed. + + \sa clear() + \sa error() +*/ +bool QSslDiffieHellmanParameters::isValid() const Q_DECL_NOTHROW +{ + return d->error == QSslDiffieHellmanParameters::NoError; +} + +/*! + \enum QSslDiffieHellmanParameters::Error + + Describes a QSslDiffieHellmanParameters error. + + \value ErrorInvalidInputData The given input data could not be used to + construct a QSslDiffieHellmanParameters + object. + + \value ErrorUnsafeParameters The Diffie-Hellman parameters are unsafe + and should not be used. +*/ + +/*! + Returns the error that caused the QSslDiffieHellmanParameters object + to be invalid. +*/ +QSslDiffieHellmanParameters::Error QSslDiffieHellmanParameters::error() const Q_DECL_NOTHROW +{ + return d->error; +} + +/*! + Returns a human-readable description of the error that caused the + QSslDiffieHellmanParameters object to be invalid. +*/ +QString QSslDiffieHellmanParameters::errorString() const Q_DECL_NOTHROW +{ + switch (d->error) { + case QSslDiffieHellmanParameters::NoError: + return QCoreApplication::translate("QSslDiffieHellmanParameter", "No error"); + case QSslDiffieHellmanParameters::InvalidInputDataError: + return QCoreApplication::translate("QSslDiffieHellmanParameter", "Invalid input data"); + case QSslDiffieHellmanParameters::UnsafeParametersError: + return QCoreApplication::translate("QSslDiffieHellmanParameter", "The given Diffie-Hellman parameters are deemed unsafe"); + } + + Q_UNREACHABLE(); + return QString(); +} + +/*! + \relates QSslDiffieHellmanParameters + + Returns \c true if \a lhs is equal to \a rhs; otherwise returns \c false. +*/ +bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) Q_DECL_NOTHROW +{ + return lhs.d->derData == rhs.d->derData; +} + +/*! \fn bool QSslDiffieHellmanParameters::operator!=(const QSslDiffieHellmanParameters &other) const + + Returns \c true if this QSslDiffieHellmanParameters is not equal to \a other; otherwise + returns \c false. +*/ + +#ifndef QT_NO_DEBUG_STREAM +/*! + \relates QSslDiffieHellmanParameters + + Writes the set of Diffie-Hellman parameters in \a dhparm into the debug object \a debug for + debugging purposes. + + The Diffie-Hellman parameters will be represented in Base64-encoded DER form. + + \sa {Debugging Techniques} +*/ +QDebug operator<<(QDebug debug, const QSslDiffieHellmanParameters &dhparam) +{ + QDebugStateSaver saver(debug); + debug.resetFormat().nospace(); + debug << "QSslDiffieHellmanParameters(" << dhparam.d->derData.toBase64() << ')'; + return debug; +} +#endif + +/*! + \relates QHash + + Returns an hash value for \a dhparam, using \a seed to seed + the calculation. +*/ +uint qHash(const QSslDiffieHellmanParameters &dhparam, uint seed) Q_DECL_NOTHROW +{ + return qHash(dhparam.d->derData, seed); +} + +QT_END_NAMESPACE diff --git a/src/network/ssl/qssldiffiehellmanparameters.h b/src/network/ssl/qssldiffiehellmanparameters.h new file mode 100644 index 0000000000..aa40be83a6 --- /dev/null +++ b/src/network/ssl/qssldiffiehellmanparameters.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Mikkel Krautz +** Contact: http://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$ +** +****************************************************************************/ + + +#ifndef QSSLDIFFIEHELLMANPARAMETERS_H +#define QSSLDIFFIEHELLMANPARAMETERS_H + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_SSL + +class QIODevice; +class QSslContext; +class QSslDiffieHellmanParametersPrivate; + +class QSslDiffieHellmanParameters; +// qHash is a friend, but we can't use default arguments for friends (ยง8.3.6.4) +Q_NETWORK_EXPORT uint qHash(const QSslDiffieHellmanParameters &dhparam, uint seed = 0) Q_DECL_NOTHROW; + +#ifndef QT_NO_DEBUG_STREAM +class QDebug; +Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslDiffieHellmanParameters &dhparams); +#endif + +Q_NETWORK_EXPORT bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) Q_DECL_NOTHROW; + +inline bool operator!=(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) Q_DECL_NOTHROW +{ + return !operator==(lhs, rhs); +} + +class QSslDiffieHellmanParameters +{ +public: + enum Error { + NoError, + InvalidInputDataError, + UnsafeParametersError + }; + + Q_NETWORK_EXPORT static QSslDiffieHellmanParameters defaultParameters(); + + Q_NETWORK_EXPORT QSslDiffieHellmanParameters(); + Q_NETWORK_EXPORT explicit QSslDiffieHellmanParameters(const QByteArray &encoded, QSsl::EncodingFormat format = QSsl::Pem); + Q_NETWORK_EXPORT explicit QSslDiffieHellmanParameters(QIODevice *device, QSsl::EncodingFormat format = QSsl::Pem); + Q_NETWORK_EXPORT QSslDiffieHellmanParameters(const QSslDiffieHellmanParameters &other); + Q_NETWORK_EXPORT ~QSslDiffieHellmanParameters(); + Q_NETWORK_EXPORT QSslDiffieHellmanParameters &operator=(const QSslDiffieHellmanParameters &other); +#ifdef Q_COMPILER_RVALUE_REFS + QSslDiffieHellmanParameters &operator=(QSslDiffieHellmanParameters &&other) Q_DECL_NOTHROW { swap(other); return *this; } +#endif + + void swap(QSslDiffieHellmanParameters &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + + Q_NETWORK_EXPORT bool isEmpty() const Q_DECL_NOTHROW; + Q_NETWORK_EXPORT bool isValid() const Q_DECL_NOTHROW; + Q_NETWORK_EXPORT QSslDiffieHellmanParameters::Error error() const Q_DECL_NOTHROW; + Q_NETWORK_EXPORT QString errorString() const Q_DECL_NOTHROW; + +private: + QExplicitlySharedDataPointer d; + friend class QSslContext; + friend Q_NETWORK_EXPORT bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) Q_DECL_NOTHROW; +#ifndef QT_NO_DEBUG_STREAM + friend Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslDiffieHellmanParameters &dhparam); +#endif + friend Q_NETWORK_EXPORT uint qHash(const QSslDiffieHellmanParameters &dhparam, uint seed) Q_DECL_NOTHROW; +}; + +Q_DECLARE_SHARED(QSslDiffieHellmanParameters) + +#endif // QT_NO_SSL + +QT_END_NAMESPACE + +#endif diff --git a/src/network/ssl/qssldiffiehellmanparameters_dummy.cpp b/src/network/ssl/qssldiffiehellmanparameters_dummy.cpp new file mode 100644 index 0000000000..42222f3f38 --- /dev/null +++ b/src/network/ssl/qssldiffiehellmanparameters_dummy.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Mikkel Krautz +** Contact: http://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 "qssldiffiehellmanparameters.h" +#include "qssldiffiehellmanparameters_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +void QSslDiffieHellmanParametersPrivate::decodeDer(const QByteArray &) +{ + Q_UNIMPLEMENTED(); +} + +void QSslDiffieHellmanParametersPrivate::decodePem(const QByteArray &) +{ + Q_UNIMPLEMENTED(); +} + +QT_END_NAMESPACE diff --git a/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp b/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp new file mode 100644 index 0000000000..949da1b7df --- /dev/null +++ b/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Mikkel Krautz +** Contact: http://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 "qssldiffiehellmanparameters.h" +#include "qssldiffiehellmanparameters_p.h" +#include "qsslsocket_openssl_symbols_p.h" +#include "qsslsocket.h" +#include "qsslsocket_p.h" + +#include +#include +#include +#ifndef QT_NO_DEBUG_STREAM +#include +#endif + +// For q_BN_is_word. +#include + +QT_BEGIN_NAMESPACE + +static bool isSafeDH(DH *dh) +{ + int status = 0; + int bad = 0; + + 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: + // + // The additional call to BN_mod_word(dh->p, 24) + // (and unmasking of DH_NOT_SUITABLE_GENERATOR) + // is performed to ensure your program accepts + // IETF group parameters. OpenSSL checks the prime + // is congruent to 11 when g = 2; while the IETF's + // primes are congruent to 23 when g = 2. + // 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 (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; + } + + bad |= DH_CHECK_P_NOT_PRIME; + bad |= DH_CHECK_P_NOT_SAFE_PRIME; + bad |= DH_NOT_SUITABLE_GENERATOR; + + return !(status & bad); +} + +void QSslDiffieHellmanParametersPrivate::decodeDer(const QByteArray &der) +{ + if (der.isEmpty()) { + error = QSslDiffieHellmanParameters::InvalidInputDataError; + return; + } + + const unsigned char *data = reinterpret_cast(der.data()); + int len = der.size(); + + QSslSocketPrivate::ensureInitialized(); + + DH *dh = q_d2i_DHparams(NULL, &data, len); + if (dh) { + if (isSafeDH(dh)) + derData = der; + else + error = QSslDiffieHellmanParameters::UnsafeParametersError; + } else { + error = QSslDiffieHellmanParameters::InvalidInputDataError; + } + + q_DH_free(dh); +} + +void QSslDiffieHellmanParametersPrivate::decodePem(const QByteArray &pem) +{ + if (pem.isEmpty()) { + error = QSslDiffieHellmanParameters::InvalidInputDataError; + return; + } + + if (!QSslSocket::supportsSsl()) { + error = QSslDiffieHellmanParameters::InvalidInputDataError; + return; + } + + QSslSocketPrivate::ensureInitialized(); + + BIO *bio = q_BIO_new_mem_buf(const_cast(pem.data()), pem.size()); + if (!bio) { + error = QSslDiffieHellmanParameters::InvalidInputDataError; + return; + } + + DH *dh = Q_NULLPTR; + q_PEM_read_bio_DHparams(bio, &dh, 0, 0); + + if (dh) { + if (isSafeDH(dh)) { + char *buf = Q_NULLPTR; + int len = q_i2d_DHparams(dh, reinterpret_cast(&buf)); + if (len > 0) + derData = QByteArray(buf, len); + else + error = QSslDiffieHellmanParameters::InvalidInputDataError; + } else { + error = QSslDiffieHellmanParameters::UnsafeParametersError; + } + } else { + error = QSslDiffieHellmanParameters::InvalidInputDataError; + } + + q_DH_free(dh); + q_BIO_free(bio); +} + +QT_END_NAMESPACE diff --git a/src/network/ssl/qssldiffiehellmanparameters_p.h b/src/network/ssl/qssldiffiehellmanparameters_p.h new file mode 100644 index 0000000000..a5da4e51fc --- /dev/null +++ b/src/network/ssl/qssldiffiehellmanparameters_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Mikkel Krautz +** Contact: http://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$ +** +****************************************************************************/ + + +#ifndef QSSLDIFFIEHELLMANPARAMETERS_P_H +#define QSSLDIFFIEHELLMANPARAMETERS_P_H + +#include "qsslkey.h" + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qssldiffiehellmanparameters.cpp. This header file may change from version to version +// without notice, or even be removed. +// +// We mean it. +// + +#include + +#include "qssldiffiehellmanparameters.h" +#include "qsslsocket_p.h" // includes wincrypt.h + +QT_BEGIN_NAMESPACE + +class QSslDiffieHellmanParametersPrivate : public QSharedData +{ +public: + QSslDiffieHellmanParametersPrivate() : error(QSslDiffieHellmanParameters::NoError) {}; + + void decodeDer(const QByteArray &der); + void decodePem(const QByteArray &pem); + + QSslDiffieHellmanParameters::Error error; + QByteArray derData; +}; + +QT_END_NAMESPACE + +#endif // QSSLDIFFIEHELLMANPARAMETERS_P_H diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index 7fd2a361e3..75a38db5f9 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -916,6 +916,7 @@ void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration) d->configuration.ciphers = configuration.ciphers(); d->configuration.ellipticCurves = configuration.ellipticCurves(); d->configuration.preSharedKeyIdentityHint = configuration.preSharedKeyIdentityHint(); + d->configuration.dhParams = configuration.diffieHellmanParameters(); d->configuration.caCertificates = configuration.caCertificates(); d->configuration.peerVerifyDepth = configuration.peerVerifyDepth(); d->configuration.peerVerifyMode = configuration.peerVerifyMode(); diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp index 66654e2a0d..0f9241e470 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -151,6 +151,10 @@ 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 -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) @@ -207,6 +211,7 @@ DEFINEFUNC4(RSA *, PEM_read_bio_RSAPrivateKey, BIO *a, a, RSA **b, b, pem_passwo #ifndef OPENSSL_NO_EC DEFINEFUNC4(EC_KEY *, PEM_read_bio_ECPrivateKey, BIO *a, a, EC_KEY **b, b, pem_password_cb *c, c, void *d, d, return 0, return) #endif +DEFINEFUNC4(DH *, PEM_read_bio_DHparams, BIO *a, a, DH **b, b, pem_password_cb *c, c, void *d, d, return 0, return) DEFINEFUNC7(int, PEM_write_bio_DSAPrivateKey, BIO *a, a, DSA *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) DEFINEFUNC7(int, PEM_write_bio_RSAPrivateKey, BIO *a, a, RSA *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) #ifndef OPENSSL_NO_EC @@ -436,6 +441,8 @@ DEFINEFUNC3(void, SSL_get0_alpn_selected, const SSL *s, s, const unsigned char * DEFINEFUNC(DH *, DH_new, DUMMYARG, DUMMYARG, return 0, return) DEFINEFUNC(void, DH_free, DH *dh, dh, return, DUMMYARG) DEFINEFUNC3(DH *, d2i_DHparams, DH**a, a, const unsigned char **pp, pp, long length, length, return 0, return) +DEFINEFUNC2(int, i2d_DHparams, DH *a, a, unsigned char **p, p, return -1, return) +DEFINEFUNC2(int, DH_check, DH *dh, dh, int *codes, codes, return 0, return) DEFINEFUNC3(BIGNUM *, BN_bin2bn, const unsigned char *s, s, int len, len, BIGNUM *ret, ret, return 0, return) #ifndef OPENSSL_NO_EC DEFINEFUNC(EC_KEY *, EC_KEY_dup, const EC_KEY *ec, ec, return 0, return) @@ -782,6 +789,10 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(EC_GROUP_get_degree) #endif RESOLVEFUNC(BN_num_bits) +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + RESOLVEFUNC(BN_is_word) +#endif + RESOLVEFUNC(BN_mod_word) RESOLVEFUNC(CRYPTO_free) RESOLVEFUNC(CRYPTO_num_locks) RESOLVEFUNC(CRYPTO_set_id_callback) @@ -830,6 +841,7 @@ bool q_resolveOpenSslSymbols() #ifndef OPENSSL_NO_EC RESOLVEFUNC(PEM_read_bio_ECPrivateKey) #endif + RESOLVEFUNC(PEM_read_bio_DHparams) RESOLVEFUNC(PEM_write_bio_DSAPrivateKey) RESOLVEFUNC(PEM_write_bio_RSAPrivateKey) #ifndef OPENSSL_NO_EC @@ -991,6 +1003,8 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(DH_new) RESOLVEFUNC(DH_free) RESOLVEFUNC(d2i_DHparams) + RESOLVEFUNC(i2d_DHparams) + RESOLVEFUNC(DH_check) RESOLVEFUNC(BN_bin2bn) #ifndef OPENSSL_NO_EC RESOLVEFUNC(EC_KEY_dup) diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h index 45e4380580..c0ddb8e888 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -227,6 +227,21 @@ int q_BIO_read(BIO *a, void *b, int c); BIO_METHOD *q_BIO_s_mem(); int q_BIO_write(BIO *a, const void *b, int c); int q_BN_num_bits(const BIGNUM *a); +#if OPENSSL_VERSION_NUMBER >= 0x10100000L +int q_BN_is_word(BIGNUM *a, BN_ULONG w); +#else +// BN_is_word is implemented purely as a +// macro in OpenSSL < 1.1. It doesn't +// call any functions. +// +// The implementation of BN_is_word is +// 100% the same between 1.0.0, 1.0.1 +// and 1.0.2. +// +// Users are required to include . +#define q_BN_is_word BN_is_word +#endif // OPENSSL_VERSION_NUMBER >= 0x10100000L +BN_ULONG q_BN_mod_word(const BIGNUM *a, BN_ULONG w); #ifndef OPENSSL_NO_EC const EC_GROUP* q_EC_KEY_get0_group(const EC_KEY* k); int q_EC_GROUP_get_degree(const EC_GROUP* g); @@ -284,6 +299,7 @@ RSA *q_PEM_read_bio_RSAPrivateKey(BIO *a, RSA **b, pem_password_cb *c, void *d); #ifndef OPENSSL_NO_EC EC_KEY *q_PEM_read_bio_ECPrivateKey(BIO *a, EC_KEY **b, pem_password_cb *c, void *d); #endif +DH *q_PEM_read_bio_DHparams(BIO *a, DH **b, pem_password_cb *c, void *d); int q_PEM_write_bio_DSAPrivateKey(BIO *a, DSA *b, const EVP_CIPHER *c, unsigned char *d, int e, pem_password_cb *f, void *g); int q_PEM_write_bio_RSAPrivateKey(BIO *a, RSA *b, const EVP_CIPHER *c, unsigned char *d, @@ -475,6 +491,8 @@ STACK_OF(X509) *q_X509_STORE_CTX_get_chain(X509_STORE_CTX *ctx); DH *q_DH_new(); void q_DH_free(DH *dh); DH *q_d2i_DHparams(DH **a, const unsigned char **pp, long length); +int q_i2d_DHparams(DH *a, unsigned char **p); +int q_DH_check(DH *dh, int *codes); BIGNUM *q_BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret); #define q_SSL_CTX_set_tmp_dh(ctx, dh) q_SSL_CTX_ctrl((ctx), SSL_CTRL_SET_TMP_DH, 0, (char *)dh) @@ -521,6 +539,9 @@ DSA *q_d2i_DSAPrivateKey(DSA **a, unsigned char **pp, long length); #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) diff --git a/src/network/ssl/ssl.pri b/src/network/ssl/ssl.pri index c70664ef9b..803d70a257 100644 --- a/src/network/ssl/ssl.pri +++ b/src/network/ssl/ssl.pri @@ -9,6 +9,8 @@ contains(QT_CONFIG, ssl) | contains(QT_CONFIG, openssl) | contains(QT_CONFIG, op ssl/qsslconfiguration_p.h \ ssl/qsslcipher.h \ ssl/qsslcipher_p.h \ + ssl/qssldiffiehellmanparameters.h \ + ssl/qssldiffiehellmanparameters_p.h \ ssl/qsslellipticcurve.h \ ssl/qsslerror.h \ ssl/qsslkey.h \ @@ -24,6 +26,7 @@ contains(QT_CONFIG, ssl) | contains(QT_CONFIG, openssl) | contains(QT_CONFIG, op ssl/qsslcertificate.cpp \ ssl/qsslconfiguration.cpp \ ssl/qsslcipher.cpp \ + ssl/qssldiffiehellmanparameters.cpp \ ssl/qsslellipticcurve.cpp \ ssl/qsslkey_p.cpp \ ssl/qsslerror.cpp \ @@ -35,6 +38,7 @@ contains(QT_CONFIG, ssl) | contains(QT_CONFIG, openssl) | contains(QT_CONFIG, op HEADERS += ssl/qsslsocket_winrt_p.h SOURCES += ssl/qsslcertificate_qt.cpp \ ssl/qsslcertificate_winrt.cpp \ + ssl/qssldiffiehellmanparameters_dummy.cpp \ ssl/qsslkey_qt.cpp \ ssl/qsslkey_winrt.cpp \ ssl/qsslsocket_winrt.cpp \ @@ -44,6 +48,7 @@ contains(QT_CONFIG, ssl) | contains(QT_CONFIG, openssl) | contains(QT_CONFIG, op contains(QT_CONFIG, securetransport) { HEADERS += ssl/qsslsocket_mac_p.h SOURCES += ssl/qsslcertificate_qt.cpp \ + ssl/qssldiffiehellmanparameters_dummy.cpp \ ssl/qsslkey_qt.cpp \ ssl/qsslkey_mac.cpp \ ssl/qsslsocket_mac_shared.cpp \ @@ -58,6 +63,7 @@ contains(QT_CONFIG, openssl) | contains(QT_CONFIG, openssl-linked) { ssl/qsslsocket_openssl_symbols_p.h SOURCES += ssl/qsslcertificate_openssl.cpp \ ssl/qsslcontext_openssl.cpp \ + ssl/qssldiffiehellmanparameters_openssl.cpp \ ssl/qsslellipticcurve_openssl.cpp \ ssl/qsslkey_openssl.cpp \ ssl/qsslsocket_openssl.cpp \ -- cgit v1.2.3